WAF 绕过笔记写过雷池侧的对抗,这篇深入到主机侧查杀引擎(安全狗/D 盒/HIDS 一类)的免杀。课上边写边绕,把”检测为什么抓得住”讲在”怎么绕”之前——这个顺序本身就是免杀的正确姿势。
0x01 先懂检测:两类引擎的分工
静态引擎:不运行代码,直接匹配特征——完整高危函数名(eval/system/assert)、已知样本哈希、正则规则。对抗思路:让特征不完整。
动态引擎:沙箱里跑一遍,做污点追踪——标记”用户可控数据”(如 $_GET['x'])如何流向危险函数(eval/system)。数据从污点源流到敏感函数就报警。对抗思路:切断污点链或让追踪失效。
两种引擎绕过难度完全不同:静态拆分关键词就能过;动态分析的是数据流,“不是简单匹配代码片段”,需要真正破坏追踪逻辑。
0x02 中断污点追踪的两种方式
课堂给的两个比喻很形象(罪犯与警察):
- 语法报错(最彻底):在污点传播路径上插入一个致命语法错误(如把
eval拼成evm一类的”死亡写法”),引擎执行到此处直接终止——警察追到嫌疑人”死亡”,停止追查。但代码要能通过引擎侧的语法检查而只在追踪侧报错,需要利用引擎与解析器的差异; - 过滤函数”洗白”:在污点源与危险函数之间插入一层处理(自定义过滤/编码转换),让追踪器认为数据已被净化——罪犯换了身份证,警察暂时跟丢。缺点是污点可能仍被标记,不如语法报错干净。
配套的一个实战技巧是全局变量多层递归:让污点数据在 global 引用链上循环多次,动态引擎的追踪力度随循环深度衰减,几轮后跟丢。
0x03 沙箱与环境差异:检测端的天然裂缝
HIDS/沙箱为了效率会精简运行环境,真实服务器环境是完整的——这个差异就是绕过点:
- 恶意分支依赖沙箱里不存在的扩展/配置(如某个
error_reporting配置项、特定扩展函数):沙箱里这段代码报错终止 → 引擎判定”无害”;真实服务器上环境完整 → 恶意分支正常执行; - 时间与随机数触发:用时间戳/随机数做开关(如仅凌晨 3 点执行、随机数命中特定值才执行),沙箱短时快跑根本不会走到恶意分支。
同类思路还有一个”高级接口盲区”:WAF/引擎根本不检查的功能(如自定义 Session Handler 的回调、生僻的 SPL 接口)——“找它不看的地方下手,比研究怎么绕它的规则更有效”,这是整个免杀专题我最认同的一句话。
0x04 具体手法清单
课堂演示的手法,按从简单到隐蔽排列:
静态层绕过
- 关键词拆分/拼接:
$f='ev'.'al'; $f($_POST['x']); - 函数别名:PHP 支持命名空间别名(5.3+),
use function system as test;后test()直接调用——WAF 对别名识别普遍薄弱。反斜杠\system()(全局命名空间,5.6+)同理; - 数组拼接:把
sys、tem分进数组再 join,用分隔符(逗号/大括号)干扰字符串匹配; - 哈希碰撞对抗双重检测:第一次上传无害
1.php让引擎记录”文件名+哈希”,第二次上传碰撞改造后的恶意文件(改一个字符让内容变了但引擎按文件名+缓存哈希放行)——利用的是缓存机制的信任逻辑。
动态层绕过
- Session Handler 动态写入:自定义
session handler把 payload 写进 session 存储路径(session_start()创建文件时若目录不存在会报错泄露路径),再由包含触发——写入的文件是”正常 session 文件”,检测面完全避开; - 上传落地兜底:Windows 下直接写文件会被 HTML 实体转义/特殊字符截断时,先下载 txt 再
COPY改后缀为 php; - 命令执行函数选型:
assert在 PHP 7.3 已不可用、5.6 直接崩溃——写马前先探 PHP 版本,7.x+ 走system/反引号/array_map回调链。
落点选择
- C2/回调监听用 80 端口而非 9999 之类生僻端口——安全系统对非常见端口的检测严格得多;
- 数据回传放云服务器(阿里云等大厂域名)比自建 IP 可信度高。
0x05 一条原则收尾
免杀的对抗是对检测原理的理解竞赛,不是 payload 背诵。每研究一个绕过,先回答三个问题:它检测什么数据(静态特征/数据流)?在哪里检测(传输/落盘/执行)?引擎和真实环境的差异在哪?答得出三个问题,payload 自己就能推导出来。
老规矩:所有手法仅在授权测试与本地靶场使用,检测与绕过的知识价值在攻防两侧同样成立——防守方知道这些,才知道该审计什么。
相关旧文
- WAF 绕过与免杀笔记:从规则原理到雷池实战
- XSS、文件上传、CSRF 与越权(一句话木马的上游)
- PHP 反序列化进阶(Session 机制的深入篇)