基础知识
概念
BYPASS WAF实际上是去寻找位于WAF设备之后处理应用层数据包的硬件/软件的特性。
利用特性构造WAF不能命中,但是在应用程序能够执行成功的载荷,绕过防护。
那些特性就像是一个个特定的场景一样,一些是已经被研究人员发现的,一些是还没被发现,等待被研究人员发现的。当我们的程序满足了这一个个的场景,倘若WAF没有考虑到这些场景,我们就可以利用这些特性bypass掉WAF了。
例如我们现在需要bypass一个云 WAF/IPS/硬件 WAF,此处我们可以利用的点就是:
- Web架构层bypass
- Web服务器层bypass
- Web应用程序层bypass
- 数据库层bypass
- WAF层bypass
分类
云WAF
在配置云WAF时(通常是CDN包含的WAF),DNS需要解析到CDN的IP上去,在请求uri时,数据包就会先经过云WAF进行检测,
如果通过再将数据包流给主机。
主机防护软件
在主机上预先安装了这种防护软件,可用于扫描和保护主机(废话),和监听web端口的流量是否有恶意的,所以这种从功能上进较为全面。
modsecurity、ngx-lua-WAF这类开源WAF虽然看起来不错,但是有个弱点就是升级的成本会高一些。
硬件ips/ids防护、硬件WAF
使用专门硬件防护设备的方式,当向主机请求时,会先将流量经过此设备进行流量清洗和拦截,如果通过再将数据包流给主机。
软WAF
软件WAF则是安装在需要防护的服务器上,实现方式通常是WAF监听端口或以Web容器扩展方式进行请求检测和阻断。
工作过程
假设客户端访问url:htp/www.miku.com/1.php?id=1'and'1'='1
,请求的数据是服务器上数据库中id为1的记录。
假设这台服务器使用了相关云WAF
域名解析
一个完整的过程,首先会请求DNS,由于配置云WAF的时候,会修改DNS的解析。
我们发送DNS请求之后,域名会被解析到云WAF的IP上去。
DNS解析完成之后,获取到域名信息,然后进入下一个步骤。
三次握手
HTTP协议是应用层协议,且是tcp协议,因此会首先去做TCP的三次握手
发送HTTP请求
发送HTTP请求过去,请求会依次经过云WAF、硬件IPS/IDS设备、硬件WAF设备、服务器、web服务器、主机防护软件/软WAF、WEB程序、数据库。
云WAF、硬件IPS/IDS、硬件WAF均有自己处理数据的方式。
TCP重组
在获取HTTP数据之前会做TCP重组,重组主要目的是针对互联网数据包在网络上传输的时候会出现乱序的情况,数据包被重组之后就会做协议解析,取出相关的值。
如http.method=GET,http payload=x等等。
这些值就对应了IPS规则中相关规则的值。从而来判断规则匹配与不匹配。
Web架构层绕过
寻找真实IP
云WAF通过修改DNS解析隐藏了真实IP地址
查找域名解析记录
利用邮件发送功能来抓包,获取真实IP
畸形数据包BYPASS
GET型请求转POST型
Content-Length头长度大于4008
正常参数放在垃圾数据后面
Web Server层bypass
Apache
畸形method
某些apache版本(version2.*)在做GET请求的时候,无论method为何值均会取出GET的内容,如请求为的method为DOTA2,依然返回了aid为2的结果。
如果某些WAF在处理数据的时候严格按照GET,POST等方式来获取数据,就会因为apache的宽松的请求方式导致bypass。
实例
我们使用实体机安装DVWA+phpstudy搭建测试环境,测试的内容是某些apache版本与waf解析request时的不同
改成任意一个非标准HTTP method都可以取出GET的内容
php+apache 畸形的boundary
php在解析multipart data的时候有自己的特性,对于boundary的识别,只取了逗号前面的内容,例如设置的boundary为--aaaa,123456
,php解析的时候只识别了--aaaa
,后面的内容均没有识别。然而其他的如WAF在做解析的时候,有可能获取的是整个字符串,此时可能就会出现BYPASS。
multipart data
Multipart/form-data的请求方式来完成上传图片等服务器交互的操作,这需要我们去严格按照规范的格式来组装请求体,每一个换行每一个空格都是不可忽略的。
Multipart/form-data的基础方法是POST,也就是说是由POST方法来组合实现的
Multipart/form-data与POST方法的不同之处在于请求头和请求体
- Multipart/form-data的请求头必须包含一个特殊的头信息:Content-Type
-
- Content-Type的值必须规定为multipart/form-data
- 同时还需要规定一个内容分割符用于分割请求体中的多个POST的内容,如文件内容和文本内容自然需要分割开来,不然接收方就无法正常解析和还原这个文件了
- Multipart/form-data的请求体也是一个字符串,不过和post的请求体不同的是它的构造方式
-
- post是简单的
name=value值
连接 - Multipart/form-data则是添加了分隔符等内容的构造体
- post是简单的
请求的头部信息如下
Content-Type: multipart/form-data; boundary=你的自定义boundary
boundary
这个参数是分界线的意思,也就是说你在请求头中指定分界线为:你的自定义boundary
,那么请求体中凡是你的自定义boundary这样的字段都会被视为分界线,这个分界线参数具体是什么你可以随意自定义
实例
只识别了两个分隔符,中间的内容都成为了内容,php在解析分隔符时会自动忽略,之后的内容正常解析请求,然而某些waf会把这数个content-disposition
识别为一整串内容,从而达到bypass的效果。
iis
%特性
在asp+iis的环境中存在一个特性,就是特殊符号%
,在该环境下当们我输入s%elect
的时候,在WAF层可能解析出来的结果就是s%elect
,但是在iis+asp的环境的时候,解析出来的结果为select
本地搭建asp+iis环境测试
1. 在实体机或是虚拟机中安装iis+asp环境
2. 随后我们在存放本地页面的文件夹中创建一个名为index.asp
的文件
用记事本打开后输入以下内容并保存:
<form method="get"action="simpleform.asp">
<p>First Name:<input type="text" name="fname"/></p>
<p>Last Name:<input type="text" name="Iname"/></p>
<input type="submit"value="Submit"/>
</form>
3. 创建第二个新的名为simpleform.asp
的文件:
<body>
Welcome
<%
response.write(request.querystring("fname"))
response.write(" " & request.querystring("Iname"))
%>
</body>
4. 打开浏览器在地址栏输入localhost:8099/index.asp
注意:此处的端口号按照你分配给的端口号为准。
我们可以看到一个简单的submit框
分别输入:
wa%sj
inc
随后点击submit
页面显示
此时URL中%
转码为了%25
如果URL中改成%
,则会显示
这就是iis环境中的%
特性,我们可以依次这个特性构造诸如sel@ect
的语句利用waf与iis服务器解析的差异达到bypass效果
u%特性
iis服务器支持对于unicode的解析,例如我们对于select中的字符进行unicode编码可以得到s%u006c%u0006ect
,这种字符在IIS接收到之后会被转换为select,但是对于WAF层,可能接收到的内容还是s%u006cu0006ect
,这样就会形成bypass的可能。
本地搭建asp+iis环境测试
在index.asp页面我们分别输入以下内容:
s%u0065%u006cect
select
提交后页面显示
此时URL中%
也转码为了%25
如果URL中改成%
,则会显示
另类%u特性
该漏洞主要利用的是unicode在iis解析之后会被转换成multibyte,但是转换的过程中可能出现:多个widechar会有可能转换为同一个字符。
打个比方就是譬如select中的e对应的unicode为%u0065
,但是%u00f0
同样会被转换成为e。
s%u0065lect->select
s%u00f0lect->select
WAF层可能能识别s%u0065lect
的形式,但是很有可能识别不了s%u00f0lect
的形式。
这样就可以利用起来做WAF的绕过
Web应用层绕过
双重url编码
双重url编码,即对于浏览器发送的数据进行了两次urlencode操作,如s做一次url编码是%73
,再进行一次编码是%25%37%33
一般情况下数据经过WAF设备的时候只会做一次url解码,这样解码之后的数据一般不会匹配到规则,达到了bypass的效果
nginx url解码问题带来的waf绕过漏洞
nginx的ngx_unescape_uri
函数在处理url decode时没有遵照标准的url decode,从而引起一系列使用该函数解码的waf都存在绕过漏洞。
出现该问题的函数位于src\core\ngx_string.c
代码中ngx unescape_uri(u_char **dst, u_char*src, size_t size, ngx_uint_t type)
该函数在处理%
号编码时,如果%
后面第一个字符非16进制范围则会丢弃%,否则第二个字符非16进制范围则会丢弃%和第一个字符
具体表现为SQL注入关键字select
如果写成s%elect
经过ngx编码处理后则会变成slect
从而绕过waf过滤规则,例如lIS asp对s%elect
的编码处理结果为select
,还有and经过ngx解码函数后会变为nd等等。
请求获取方式
变更请求方式
GET,POST,COOKIE
在web环境下有时候会出现统一参数获取的情况,主要目的就是对于获取的参数进行统一过滤。
例如我获取的参数t=select 1 from 2
这个参数可以从get参数中获取,可以从post参数获取,也可以从cookie参数中获取。
典型的案例是dedecms,在之前测试的时候就发现了有些waf厂商进行过滤的时候过滤了get和post,但是cookie没有过滤,直接更改cookie参数提交payload,即绕过。
使用bp可以进行请求方式的切换
urlencode
和form-data
POST
在提交数据的时候有两种方式
urlencode
form-data
当我们在测试站点的时候,如果发现POST提交的数据被过滤掉了,此时可以考虑使用form-data
的方式去提交
使用bp可以进行两种格式的切换
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
如此可以分析:当WAF获取参数的形式与WEB程序获取参数的形式不一致的时候就可能出现WAF bypass的可能。
此处关键还是要分析WAF对于获取参数的方式是如何处理的
hpp可以灵活运用,譬如有些cms基于url的白名单,因此可以利用hpp的方式在参数1的位置添加白名单目录,参数2的位置添加恶意的payload。比如:
index.php?a=[whitelist]&a=select 1 union select 2
数据库层bypass
数据库层bypass常常是在bypass waf的sql注入防护规则。我们需耍针对数据库,使用该数据库的特性即可。如mysql,sqlserver等等。
目前数据库被暴露出来的特性很多很多,基本上很多特性综合利用就已经够用了,因此特性知不知道是一方面,能不能灵活运用就 得看测试者自己
mysql数据库
就目前来看是使用最多的,也是研究人员研究最深的数据库。测试的角度上一般会测试下面的过滤点,因为一般绕过了select from
就基本可以sql注入获取数据了。
常见过滤位置
参数和union之间的位置
\Nunion
- 浮点数,如
8.0
8e0
/*!50000*/
union和select之间的位置
- 空白字符 mysql中可利用空白字符有
%09
%0a
%0b
%0c
%0d
%a0
- 空白注释 mysql中可利用空白注释有
/**/
/*letmetest*/
- 使用括号
常见过滤函数
字符串截取函数
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);
字符转换
Ascii(1)
此函数之前测试某云waf的时候被过滤了,然后使用ascii(1)
即可
Char(49)
Hex('a')
Unhex(61)
过滤了逗号
- limit处的逗号,通过
limit 1 offset 1
绕过 - 字符串截取处的逗号,比如mid处的逗号,通过
mid(version() from 1 for 10)
绕过 - union处的逗号,通过
join
拼接绕过
sqlserver数据库
常见过滤位置
select from后的位置
- 可以通过空白字符绕过
-
01
~0F
、10
~1F
、20
都是空白字符- 需要做urlencode salserver中的表示空白字符比较多,靠黑名单去阻断一般不合适
- 注释符号Mssql也可以使用注释符号
/**/
- 其他符号:
.
符号
select from后之间的位置
- 可以通过空白字符绕过
-
01
~0F
、10
~1F
、20
都是空白字符- 需要做urlencode salserver中的表示空白字符比较多,靠黑名单去阻断一般不合适
- 注释符号Mssql也可以使用注释符号
/**/
- 2017版本之前还可以通过
:
绕过
and之后的位置
- 可以通过空白字符绕过
-
01
~0F
、10
~1F
、20
都是空白字符- 需要做urlencode salserver中的表示空白字符比较多,靠黑名单去阻断一般不合适
- 注释符号Mssql也可以使用注释符号
/**/
- 2017版本之前还可以通过
:
绕过
常见过滤函数
字符串截取函数
Substring(@@version,1,1)
Left(@@version,1)
Right(@@version,1)
字符串转换数
Ascii('a')
这里的函数可以在括号之间添加空格的,一些waf过滤不严会导致bypass
Char('97')
其他方式
Mssql支持多语句查询,因此可以使用;
结束上面的查询语句,然后执行自己构造的动态执行。
- 使用
exec
的方式构造语句使返回查询结果的时间向后延迟五秒钟 - 使用
sp_executesql
的方式构造语句使返回查询结果的时间向后延迟五秒钟
WAF层绕过
性能bypass
WAF在设计的时候都会考虑到性能问题,例如
- 如果是基于数据包的话会考虑检测数据包的包长
- 如果是基于数据流的话就会考虑检测一条数据流的多少个字节
一般这类算检测的性能,同时为了保证WAF的正常运行,往往还会做一个bypass设计,在性能如cpy高于80%或则内存使用率高于如80%是时候,会做检测bypass,以保证设备的正常运行。
WAF等设备都是工作在应用层之上的,如HTTP、FTP、SMTP等都是应用层的协议,这些数据要被处理都会被进行数据解析,协议分析。最终获取应用层的数据。如HTTP的方法是什么,HTTP的querystring是什么,以及HTTP的requestboody是什么。然后将这些实时获取的值与WAF设计的规则进行匹配,匹配上着命中规则做相应的处理。
性能检测bypass
现在问题就是检测多长呢?
例如我用HTTP POST上传一个2G的文件,明显不可能2G全做检测,不但耗CPU同时也会耗内存。
因此在设计WAF的时候可能就会设计一个默认值,有可能是默认多少个字节的流大小,可能是多少个数据包。
测试实例
测试安全狗的,大致原理应该是通过一个脚本,不断的向HTTP POST添加填充数据,当将填充数据添加到一定数目之后,发现POST中的注入恶意代码没有被检测了。
最终达到了bypass的目的。
在测试某家云WAF的时候使用此类方法也可以达到bypass的目的。
性能负载bypass
一些传统硬件防护设备为了避免在高负载的时侯影响用户体验,如延时等等问题,会考虑在高负载的时候bypass掉自己的防护功能,等到设备的负载低于门限值的时候又恢复正常工作。
一些高性能的WAF可能使用这种方法可能不能bypass,但是一些软WAF使用这种方式还是可以bypass的。
测试实例
一个bypass的例子,将请求并发同时发送多次,多次访问的时候就有几次漏掉了,没有触发waf的拦截。
例如制造了一个payload,同时添加了大量的无效数据,使用脚本发送该请求,发现请求的时候有些通过了WAF,有些被WAF所拦截了。应该就是性能问题导致了bypass。
fuzz bypass
使用脚本去探测WAF设备对于字符处理是否有异常,上面已经说过WAF在接收到网络数据之后会做相应的数据包解析,一些WAF可能由于自身的解析问题,对于某些字符解析出错,造成全局的bypass。
我测试的时候常常测试的位置:
- get请求处
- header请求处
- post urlencode内容处
- post form-data内容处
然后模糊测试的基础内容有:
- 编码过的0-255字符
- 进行编码的0-255字符
- utf gbk字符
测试实例
在一次测试安全狗的过程中,使用POST的方式提交数据,提交数据包括两个参数
- 正常的fuzz点
- 一个参数包含一个Sql注入语句
当在测试前面的fuzz点的时候,处理到\x00
的字符时,没有提示安全狗阻拦。应该是解析这个字符的时候不当,导致了bypass。
在一次测试云WAF中,使用get方式提交数据,提交内容包括一个参数,参数为字符+sql
注入的语句。当在fuzz字符的时候,发现云waf在处理到&
字符的时候,没有提示云waf阻拦。
由于&
字符的特殊性,猜测是由于和请求中的&
没有处理好导致的
由于mysal中&&
同样可以表示and
,因此拼凑一下sql语句就达到了bypass的目的。
白名单bypass
WAF在设计之初一般都会考虑白名单功能。
如来自管理IP的访问,来自cd如服务器的访问等等。这些请求是可信任的,不必走WAF检测流程。
- 获取白名单的IP地址如果是从网络层获取的IP,这种一般bypass不了
- 如果采用应用层的数据作为白名单,这样就可能造成bypass
之前有一篇文章内容是通过修改http的header来bypass waf,这里我们截取文章中部分内容:
这些header常常用来获取IP,可能还有其他的,例如nginx-lua-waf
获取clientip使用了X-Real-ip
的header
此种方法还可以用来绕过如登陆锁IP,登陆多次验证码,后台验证等等的场景