怎样挖掘出属于自己的php反序列化链
字数 1583 2025-08-22 12:23:47
PHP反序列化链挖掘指南
前言
本文旨在系统性地介绍如何挖掘PHP框架中的反序列化利用链(POP Chain)。通过分析多个实际案例,总结出一套有效的挖掘方法论,帮助安全研究人员发现新的反序列化漏洞。
基本概念
PHP反序列化漏洞需要满足两个基本条件:
- 存在可被用户控制的unserialize()调用点
- 存在合理的POP Chain(属性-方法调用链)
核心原则
1. 变量可控原则
- 尽可能寻找参数完全可控的方法
- 关注危险函数和结构中的可控变量
2. 扩大影响原则
- 寻找可以扩大攻击面的结构和方法
- 通过多种方式连接不同的调用链
挖掘方法论
1. 寻找起点
魔术方法选择:
__destruct()- 对象销毁时自动调用,最常用__wakeup()- 反序列化时调用,使用较少
优秀起点的特征:
- 自动调用(魔术方法)
- 参数完全可控
- 攻击面广(调用其他方法或函数)
案例分析:
Laravel框架中PendingBroadcast.php的__destruct()方法:
public function __destruct() {
$this->events->dispatch($this->event);
}
优势:
- 两个参数(
$this->events和$this->event)都可控 - 可导向
__call()或dispatch()方法
2. 跳板选择
常见跳板类型:
- 字符串操作函数:如
trim(),可触发__toString() - 动态调用结构:
call_user_func($this->test)$test()new $test1($test2, $test3)
- 数组调用:
[(new test), "aaa"]()可调用test类的aaa方法
跳板选择策略:
- 寻找参数可控的函数调用
- 关注可能触发其他魔术方法的结构
- 优先选择能扩大攻击面的跳板
3. 终点定位
两类终点:
-
危险动态调用:
($this->a)($this->b)$this->a[0]($this->b)
-
危险函数:
- RCE:
call_user_func,array_walk等 - 文件操作:
file_put_contents等
- RCE:
实战案例
Yii2/RCE1
调用链:
-
起点:
yii\db\BatchQueryResult::__destruct()public function __destruct() { $this->reset(); } -
跳板:
yii\web\DbSession::close()public function close() { if ($this->getIsActive()) { $this->fields = $this->composeFields(); session_write_close(); } } -
跳板:
yii\web\MultiFieldSession::composeFields()protected function composeFields() { $fields = $this->writeCallback ? call_user_func($this->writeCallback, $this) : []; // ... } -
终点:
yii\rest\IndexAction::run()public function run() { if ($this->checkAccess) { call_user_func($this->checkAccess, $this->id); } // ... }
利用方式:
- 设置
$writeCallback为[(new IndexAction), 'run'] - 设置
$checkAccess和$id为恶意代码
Yii2/RCE2
调用链:
-
起点:
Symfony\Component\String\UnicodeString::__wakeup()public function __wakeup() { normalizer_is_normalized($this->string) ?: $this->string = normalizer_normalize($this->string); } -
跳板:
Symfony\Component\String\LazyString::__toString()public function __toString() { if (\is_string($this->value)) { return $this->value; } return $this->value = ($this->value)(); }
利用方式:
- 通过
__toString()触发动态函数调用 - 直接执行任意代码
防御策略
1. 动态调用与危险函数
- 对变量和方法进行完整回溯
- 优先使用静态属性进行动态调用
- 使用
instanceof检查对象类型
2. 方法设计
- 避免在魔术方法中调用过多其他方法
- 公共方法中避免直接使用危险函数
- 不需要序列化的类可使用
__wakeup()阻止反序列化
3. 最重要原则
- 永远不要让
unserialize()和文件操作函数用户可控
总结
挖掘PHP反序列化链的关键在于:
- 从魔术方法开始,寻找参数可控的起点
- 通过多种跳板连接不同的方法和类
- 最终导向危险函数或动态调用
- 保持耐心,系统性地分析框架结构
通过这种方法论,可以有效提高发现新反序列化漏洞的效率。