zoukankan      html  css  js  c++  java
  • 【WAF技巧拓展】————3、如何绕过过滤器和WAF规则实现PHP远程漏洞利用

    概述

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

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

    显然,第六行存在问题。第三行尝试拦截注入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。

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

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

    PHP字符串转义序列

    • [0–7]{1,3}是八进制表示法的字符序列,可以溢出一个字节。例如:“400” === “00”
    • 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);。如果我能够通过使用变量和字符串来调用函数,那么我就可以使用转义序列,而不再是函数的名称。一个例子如下所示:

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

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

    改进用户输入清理

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

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

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

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

    让我们试试第一个Payload:

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

    然后,试试第二个Payload:

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

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

    get_defined_functions

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

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

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

    字符数组

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

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

    OWASP CRS3

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

    推荐阅读

    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/

    转自:http://www.4hou.com/technology/15384.html

    总会有不期而遇的温暖. 和生生不息的希望。
  • 相关阅读:
    Java单例模式(Singleton)以及实现
    golang 垃圾回收机制
    MySQL索引背后的数据结构及算法原理
    简述拥塞控制的四种基本算法
    分库分表
    lvalue & rvalue
    理解linux cpu load
    android使用百度地图SDK获取定位信息
    iOSUIWebView---快停下啦,你的愚蠢的行为
    【翻译自mos文章】当/var/tmp文件夹被remove掉之后,GI crash,并启动失败,原因是ohasd can not create named pipe
  • 原文地址:https://www.cnblogs.com/devi1/p/13486368.html
Copyright © 2011-2022 走看看