如何绕过过滤器和WAF规则实现PHP远程漏洞利用

41yf1sh 技术 2018年12月30日发布
Favorite收藏

导语:在本文中,我们将主要分析如何绕过过滤器、输入清理和WAF规则,实现PHP的远程代码执行。

概述

在本文中,我们将主要分析如何绕过过滤器、输入清理和WAF规则,实现PHP的远程代码执行。通常,当我在写这样的文章时,人们总是会问,“真的有人写出这样的代码吗?”并且通常都不是疑问句。在再次被问到这个问题之前,我要抢先回答:“是真的。”

在研究过程中,我们对两个易受攻击的PHP脚本进行了测试。其中,第一个脚本非常简单,并且近乎愚蠢,但它只是为了重现一个远程代码执行漏洞的利用场景:

1.png

显然,第六行存在问题。第三行尝试拦截注入system、exec或passthru之类的函数。在PHP中,有许多其他函数可以执行系统命令,但我们重点关注这三个函数。该脚本在CloudFlare WAF后面的Web服务器中运行。和往常一样,我使用了CloudFlare,因为它非常简单,并且广为人知,并不意味着CloudFlare WAF不安全。其他所有WAF都或多或少会有相同的问题。第二个脚本落后于ModSecurity + OWASP CRS3的安全要求。

尝试读取/etc/passwd

针对第一个脚本,我尝试使用system()函数,通过请求/cfwaf.php?code=system(“cat /etc/passwd”);来读取/etc/passwd。

2.png

如大家所见,CloudFlare会阻止我的请求,可能是由于/etc/passwd。但是,我们有方法能轻松绕过,可以使用类似于cat /etc$u/passwd这样的命令。

3.png

CloudFlare WAF已经被绕过,但其中还存在对用户输入的检查,这阻止了我的请求,因为我正在尝试使用“system”函数。那么不禁要问,是否有一种语法,能让我在不使用“system”字符串的情况下使用系统功能呢?我们来看看PHP官方文档中,有关字符串的内容:https://secure.php.net/manual/en/language.types.string.php

PHP字符串转义序列

\[0–7]{1,3}是八进制表示法的字符序列,可以溢出一个字节。例如:“\400” === “\000”

\x[0–9A-Fa-f]{1,2}是十六进制表示法的字符序列。例如:“\x41"

\u{[0–9A-Fa-f]+}是Unicode代码点(Codepoint)序列,将作为该代码点的UTF-8表示输出到字符串(在PHP 7.0.0版本中加入)。

看来,并不是所有人都知道PHP中有很多用于表示字符串的语法。因此,使用“PHP变量函数”,就成为了我们绕过过滤器和WAF规则的瑞士军刀。

PHP变量函数

PHP支持变量函数的概念。这意味着,如果变量名称附加了括号,PHP将会查找与变量等价的名称相同的函数,并尝试执行。除此之外,这可以用于实现回调、函数表等功能。

这意味着,像var(args);和“string”(args);这样的内容,都等价于function(args);。如果我能够通过使用变量和字符串来调用函数,那么我就可以使用转义序列,而不再是函数的名称。一个例子如下所示:

4.jpeg

其中,第三种语法是十六进制表示法中的字符转义序列,PHP将其转换为字符串“system”,然后使用参数“ls”将其转换为函数system。我们在易受攻击的脚本上进行尝试:

5.png

这种技术不适用于所有的PHP函数,变量函数不适用于类似echo、print、unset()、isset()、empty()、include、require这样的语言结构。利用包装器函数,可以将这些结构中的任何一个作为可变函数。

改进用户输入清理

在易受攻击的脚本中,如果我从用户输入中排除双引号和单引号等字符,那么会发生什么?即使不使用双引号,是否也可以绕过它?我们来进行一下尝试:

6.png

正如我们在第三行所看到的那样,现在脚本将阻止在$_GET[code] querystring参数中使用双引号和单引号。现在,我之前的Payload应该已经被阻止:

7.png

幸运的是,在PHP中,我们并不总是需要引号来表示字符串。PHP中,还存在其他能够声明元素的类型,例如$a = (string)foo;。在这种情况下,$a包含字符串foo。此外,在没有特定类型声明的圆括号中的内容,都将被视为字符串:

8.jpeg

基于此,我们有两种绕过新过滤器的方法:第一种方法是使用类似于(system)(ls);的形式,但我们不能再代码参数中使用“system”,所以我们可以连接字符串,类似于(sy.(st).em)(ls);。第二种方法是使用$_GET变量。如果我发送一个请求,类似于?a=system&b=ls&code=$_GET[a]($_GET[b]);,那么其结果为$_GET[a],就会替换为字符串“system”。同时,$_GET[b]可以被替换为字符串“ls”。这样一来,我就能绕过所有过滤器了!

9.jpeg

让我们试试第一个Payload:

(sy.(st).em)(whoami);

10.png

然后,试试第二个Payload:

?a=system&b=cat+/etc&c=/passwd&code=$_GET[a]($_GET[b].$_GET[c]);

11.png

在示例中,这个技巧可能没有帮助,但我们实际上甚至可以在函数名称和参数内插入注释,这将有助于绕过阻止特定PHP函数名称的WAF规则集。以下所有语法都是有效的:

12.png

get_defined_functions

该PHP函数将返回一个多维数组,其中包含所有已定义函数的列表,包括内置(内部)函数和用户定义的函数。内部函数可以通过$arr[“internal”]访问,用户定义的函数可以通过$arr[“user”]访问。例如:

13.png

这可能是在不使用其名称的前提下,实现system函数的另外一种方法。如果我对“system”进行grep,那么可以发现它的索引号,随后将其用作代码执行的字符串:

14.png

显然,这应该适用于我们的CloudFlare WAF和脚本过滤器:

15.png

字符数组

PHP中,每个字符串都可以用作字符数组(几乎与Python一样),我们可以使用语法$string[2]或$string[-3]来引用字符串中的单个字符。这可能是另一种逃避阻止PHP函数名称的规则的方法。举例来说,使用字符串$a=”elmsty/ “;,我就可以实现语法system(“ls /tmp”);

16.png

如果我们幸运,可以在脚本文件名中找到所需的所有自负。使用相同的技术,我们就可以选择所需的所有字符,类似于(__FILE__)[2]:

17.png

18.png

OWASP CRS3

不得不说,随着OWASP CRS3的发布,我们的绕过工作变得更难。首先,通过之前所描述的技术,我只能绕过第一关。但是,第一关只是我们在CRS3中能找到的规则的冰山一角,并且第一关旨在防止任何类型的误报。由于其中存在942430号规则“受限制的SQL字符异常检测(args):特殊字符数超出限制范围”,我们的绕过就变得非常困难,实际上所有事情都变得非常困难。我能做的,就是执行一个没有“ls”或“whoami”参数的命令。但是,我并不能像对CloudFlare WAF进行的那样,执行类似于system(“cat /etc/passwd”)的命令:

19.png

20.png

推荐阅读

Web应用程序防火墙逃避技术 #1

https://medium.com/secjuice/waf-evasion-techniques-718026d693d8

Web应用程序防火墙逃避技术 #2

https://medium.com/secjuice/web-application-firewall-waf-evasion-techniques-2-125995f3e7b0

Web应用程序防火墙逃避技术 #3

https://www.secjuice.com/web-application-firewall-waf-evasion/

本文翻译自:https://www.secjuice.com/php-rce-bypass-filters-sanitization-waf/如若转载,请注明原文地址: http://www.4hou.com/technology/15384.html
点赞 5
PHP
  • 分享至
取消

感谢您的支持,我会继续努力的!

扫码支持

打开微信扫一扫后点击右上角即可分享哟

发表评论