PHP 反序列化知识点零散,这篇收成一张图:链子怎么找、怎么串、怎么绕(完整复现见 ThinkPHP 5.1.37 篇)。
反序列化为什么危险
unserialize($data) 还原对象时自动触发魔术方法;$data 可控就能自定义类和属性——魔术方法被执行,属性值由攻击者定。危险的不是 unserialize,是它点燃的那串自动调用。
魔术方法速查
| 方法 | 触发时机 | POP 链角色 |
|---|---|---|
__destruct | 对象销毁 | 最常见引爆点 |
__wakeup | unserialize 时 | 引爆点(可被绕过) |
__construct | new 时 | 反序列化不触发,别混淆 |
__toString | 对象被当字符串用 | 常用跳板 |
__call | 调不存在的方法 | 跳板 |
__get / __set | 访问不存在的属性 | 跳板 |
__invoke | 对象被当函数调 | 常见末端执行点 |
串链子的思路
从尾往前找不容易迷路:
- 找终点:锁定危险”动作函数”——
file_put_contents、call_user_func、eval、system这类。 - 找倒数第二环:哪个类的魔术方法触达终点,参数来自可控属性。
- 向前接力:每环只回答”哪个魔术方法把可控属性传给下一环”。
- 接通引爆点:直到某环能被
__destruct/__wakeup直接触达。
心法:属性全可控——每环 $this->xxx 都你说了算。
phar 反序列化
phar:// 在文件操作函数(file_exists、file_get_contents 等)里被调用时会反序列化 phar 包的 metadata——不用 unserialize 入口,有文件操作函数 + 能上传 phar 文件(可伪装图片)就能打。
file_exists('phar://evil.jpg/test') → 触发 metadata 反序列化
两个实用绕过
__wakeup绕过(CVE-2016-7124):属性个数大于真实个数时__wakeup不执行(O:4:“Demo”:2:{…} 把 2 改成 3)。- 字符逃逸:替换改变长度时,设计前后值可让属性”逃出”边界,吞掉后续序列化内容——变长→后面补位逃逸,变短→前面吃字符逃逸。
审计时的检查清单
- 全局搜
unserialize(,看参数是否可控(COOKIE/POST/文件内容)。 - 搜
phar://可用的文件操作函数。 - 框架项目直接分析已知 POP 链类(ThinkPHP 的 Windows/Request/Conversion)。
- 没现成链就按”从尾往前”自己串。
小结
反序列化的上限取决于项目里有什么类可串,和代码审计绑死;Xdebug 单步跟一遍链子胜过读十遍文章。