一、 请求方法
也就是GET/POST,waf可能以此为依据对流量检测,列举一些少数不用GET/POST的请求情况。
比如IIS和tomcat存在老掉牙的PUT漏洞,WebDAV PUT/CVE-2017-12615。
比如存在直接PUT的oos。
比如springboot部分接口会写成PUT或者DELETE。
比如shiro-550在错误的请求方法中依旧能反序列化成功。
二、 传参
GET/POST/Cookie/Header,通常来说,waf对这四种传参方式的检测力度会不断削弱。
此外,在传输多个同一参数时,不同的中间件有不同的处理方案,apache/php
var_dump("id: ".$_REQUEST['id']);
var_dump("token: ".$_COOKIE['token']);
var_dump("test: ".$_SERVER['HTTP_TEST']);
tomcat/java
<%
out.println("id: "+request.getParameter("id"));
Cookie[]cookie = request.getCookies();
for(Cookie c:cookie){
out.println("token:"+c.getValue());
}
out.println("test: "+request.getHeader("test"));
%>
IIS/asp
<%
Response.Write("id:" & Request("id"))
Response.Write("<br>id:" &Request.Form("id"))
Response.Write("<br>token:" &Request.Cookies("token"))
Response.Write("<br>test:" &Request.ServerVariables("HTTP_TEST"))
%>
可以看到,IIS和apache均出现了以逗号合并多个参数的情况。因此在传输恶意payload时,可以用双参数进行,如下。
/test?id=1+and/*&id=*/1=1
IIS还允许传参unicode编码,或者在英文字母间插入%字符。
/1.asp?id=%u0031&id=a%b%c
GET传一个id,POST再传一个效果更好,虽然测试的几个中间件只有IIS可以,但实际情况中还是比较常见的。
三、 Content-Type
常见Content-Type如下
Content-Type: application/x-www-form-urlencoded
Content-Type: application/json
Content-Type: text/xml
Content-Type: multipart/form-data
有时候会加个编码
Content-Type: text/html; charset=utf-8
这个header允许大小写,还允许分号分割后增加脏数据干扰waf识别。一般来说Content-Type声明和POST传输的数据格式需要对应,不过也有的时候不对应或者没有Content-Type也可以被后端识别。
apache/php
tomcat/java
IIS/asp
四、 multipart/form-data
Content-Type: multipart/form-data通常用于文件上传,也可以用于传参,除了上面的干扰方法之外,在boundary段,Content-Disposition段也存在很多方法。
apache/php,最宽松。
tomcat/java,较为宽松。需要Context.xml
<Context allowCasualMultipartParsing="true">
</Context>
IIS/aspx,较为严格。
<%@ Page Language="C#" Debug="true"%>
<script runat="server">
protected void Page_load(object sender,EventArgs e)
{
string id = Request.Params["id"];
string cid= Request.Params["cid"];
Response.Write("id: " + id);
Response.Write("rncid: " + cid);
}
</script>
flask/python,较为宽松
from flask import Flask, request
app=Flask(__name__)
@app.route('/test', methods=["GET", "POST"])
def test():
id = request.form.get("id")
return "id: "+str(id)
if __name__ == '__main__':
app.run()
以如下格式为标准
Content-Type: multipart/form-data; boundary=---69
-----69
Content-Disposition: form-data; name="id"
3
-----69--
复数显示
Content-Type: multipart/form-data; boundary=---69; boundary=---69
Content-Type: multipart/form-data; boundary=---69
-----69
Content-Disposition: form-data; name="id";name="id";
3
-----69
Content-Disposition: form-data; name="id"
4
-----69--
分割成段并编号
这些位置基本都可以增加一些变化以逃脱waf识别,有兴趣的可以自己根据我标识的位置进行测试。
对于文件上传来说,多了filename和Content-Type,而filename和name的解析规律基本一致。
Content-Type在大概部分情况下可去除或者为其他任意值,因此waf不会检测,不过也有少数情况能影响到waf。
Content-Disposition: form-data; name="file"; filename="1.php"
Content-Type: image/png
在Content-Type后面还可以设置charset,参考https://www.anquanke.com/post/id/242583的几种编码方法。
Content-Disposition: form-data; name="id"
Content-Type: application/octet-stream; charset=CP037
cp037code
实际测试只有python/flask支持UTF-16be/UTF-16le/CP037/UTF-7等编码。
文章测试了java也有,同时werkzeug还支持Content-Transfer-Encoding: base64
可以看到,multipart/form-data可用变形非常多,而且不同中间件/语言规则都不同。很容易造成waf和后端解析的差异,导致waf无法识别被绕过。因此越来越多的waf选择自己实现严格的multipart/form-data,随便多个空格就拦截。比如宝塔。
五、 Transfer-Encoding: chunked
也就是分块传输,很多免费waf直接不识别分块传输。
Content-Type: application/x-www-form-urlencoded
Transfer-Encoding: chunked
7;qwe
id=test
002;qwe
st
0;
Transfer-Encoding: chunked部分同样可以改变大小写和加空格。
标识长度的16进制数7,如上可以分号后增加各种脏数据填充,此处算A1填充处。
7本身可以在前面增加若干0来填充,此处算A0填充处。
这两处填充规则,不同的中间件也有着区别。
apache/php
A0+A1包括分号,也就是一行总长度最大为8188,如果有一行超出了长度限制,比如2;rty处填充9000脏数据,apache会报413,但前面正确的chunk也就是id=test依旧正确识别。
但如果整个body长度超过33180,id将无法获取包括GET上面的也一样,且这个数字是浮动的。
同理,如果有一个chunk和实际长度不符合,会报400错误但依旧能取得到对应的值。
这种情况曾用于ModSecurity的绕过。
Transfer-Encoding: chunked还可以和Content-Length: 8217共存,这种情况常见于找请求走私漏洞,但也有可能干扰waf识别。
tomcat/java
A0处仅允许8字节。A1处最大长度8191,并且所有chunk共享这一长度。超出长度后所有chunk将不再解析。
IIS/aspx
A0+A1,长度限制为16264,也是浮动的。
不使用Content-Length的情况下,在A0+A1处填充还很容易发包被拒绝。
flask/python
A0处无长度限制,但不支持A1和分号
nginx/php
A0和A1处均无长度限制。
分块传输还可以和Content-Type: multipart/form-data相结合,使其更加复杂。
分块传输也可以延迟发送,这也是分块传输被设计出来的意义,用如下burp插件可以很好的完成。
https://github.com/c0ny1/chunked-coding-converter
除了chunked之外,Transfer-Encoding还支持声明compress/deflate/gzip等压缩算法,可以让请求包的body压缩以免于被waf识别,但很早就被淘汰了,现在没有浏览器和服务端支持这个用法。
请求包能被压缩,与之对应的返回包压缩header如下
Accept-Encoding: gzip, deflate
支持compress/deflate/gzip/br,这个是客户端跟服务端声明自己的浏览器支持解压哪些返回包,服务端再响应Content-Encoding并返回压缩包,请求包并不会因为这个header被压缩。
六、 Expect: 100-continue
有的waf会检测返回包,这个header可以让返回包加个100状态码,所有中间件都支持。
七、 脏数据
通过上面介绍的可知,脏数据可以插在很多不影响传参的地方。根据payload的不同,也可以插在传参的注释处。比如sql注入的/*aaaaa*/。
脏数据用x00填充有奇效。
除了脏数据之外,针对免费云waf的打空流量,找真实ip,高并发等也是暴力手段之一。
原文始发于微信公众号(珂技知识分享):http协议的waf绕过
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论