利用分块传输协议绕waf
文章最后更新时间为:2019年10月27日 13:11:01
0. 实验环境
本次实验采用的是win10下面的phpstudy+安全狗。安全狗开启http安全监测,可以看到会过滤and or等关键词:
在根目录,写一个最简单的php页面如下:
<?php
header("Content-Type: text/html;charset=utf-8");
$id = $_REQUEST["id"];
if ($id){
echo "接收到的id为" . $id;
}
else{
echo "No id";
}
?>
采用$_REQUEST
既可以接收post参数也可以接收get参数,我们来试试安全狗有没有用,尝试加关键字and:
可以看到,请求中含有关键字的都会被拦截。
1. 关于分块编码传输
在通过http传输文件的时候,通常会有一个Content-Length
用来指定文件的长度,比如传输图片,静态页面,客户端也以Content-Length
作为接收内容结束的标志,接收完毕后就可以断开连接了。但是有时候发送方并不能确定内容的长度,造成的影响就是:接收方无法通过Content-Length
得到报文体的长度,也就无法得知什么时候应该中断连接。
为此我们需要一个新的机制:不依赖头部的长度信息,也能知道实体的边界。
HTTP 1.1引入了分块传输编码的方式。只要在header头部加入 Transfer-Encoding: chunked
,就代表这个报文采用了分块编码。此时不用指定Content-Length
接收方也可以知道什么时候传输结束了,只需要约定一个信号即可,比如,接收方只要接收到一个长度为0内容为0的分块,则代表传输完毕。具体的优点可参阅:维基百科
那么怎么样将一个普通的报文改编成分块传输形式的呢?
报文中的实体需要改为用一系列分块来传输。每个分块包含十六进制的长度值和数据,长度值独占一行,长度不包括它结尾的 CRLF(\r\n),也不包括分块数据结尾的 CRLF。最后一个分块长度值必须为 0,对应的分块数据没有内容(两个空行),表示实体结束。
下面用一个最简单的例子,向本地post一个数据包,为id=123455
:
可以实验一下,如果关闭burp在repeater选项里面自带的content-length补全功能,然后去掉Content-Length
,就无法接收到id。
接下来我们将其改为分块传输的方式:
通过上图可以看到,即使没有Content-Length
,我们也可以采用分块传输的方式,分多少块,每块多大都不是唯一的,但是最后的结尾需要一个长度为0内容为空的块(内容为两个空行)。
2. 利用分块传输绕过waf
当我们将传输的内容分块时,处理后的HTTP请求由于和已知的payload相差较大,所以可以起到一定的绕过WAF的效果。比如我们来试试安全狗:
可以看到当对id=1 and 1=1
进行分块后传输,可以绕过安全狗的检查。
但是有一些如Imperva的,360等比较好的WAF已经对传输编码的分块传输做了处理,可以把分块组合成完整的HTTP数据包,这时直接使用常规的分块传输方法尝试绕过的话,会被WAF直接识别并阻断。
这个时候我们可以在每个分块长度标识处加上分号“;”作为注释,如下所示:
几乎所有可以识别传输编码数据包的WAF,都没有处理分块数据包中长度标识处的注释,导致在分块数据包中加入注释的话,WAF就识别不出这个数据包了。
此外构造畸形的分块数据包还可以绕过ModSecurity,具体可以看文末参考文章,这里本地没有复现。关于该绕过方法的局限性,chunk只能在POST方法中使用,如果是GET方法的注入点就无法绕过
对于burpsuite,已经有人做出了将普通的post数据包转变为分块数据包的插件,地址为:https://github.com/c0ny1/chunked-coding-converter
很实用,学习中