TP6.0.13反序列化分析
字数 781 2025-08-06 18:07:44
ThinkPHP 6.0.13 反序列化漏洞分析
0x00 环境搭建
- 使用Composer安装ThinkPHP 6.0.13:
composer create-project topthink/think tp - 要求PHP版本>7.2
- 使用PHPStudy搭建运行环境
0x01 漏洞分析
漏洞触发流程
-
入口点:
__destruct()魔术方法- 位于
AbstractCache类(Psr6Cache的父类)
public function __destruct() { if ($this->lazySave) { $this->save(); } } - 位于
-
调用链:
__destruct()→save()→getItem()→__call()→log()→record()→save()→Socket::save()
关键调用分析
-
Psr6Cache类:
public function save() { $item = $this->pool->getItem($this->key); $item->set($this->getForStorage()); $item->expiresAfter($this->expire); $this->pool->save($item); } -
Channel类的__call方法:
public function __call($method, $parameters) { $this->log($method, ...$parameters); } -
record方法:
public function record($msg, string $type = 'info', array $context = [], bool $lazy = true) { // ...省略部分代码... if (!$this->lazy || !$lazy) { $this->save(); } } -
Socket驱动类的save方法(关键点):
public function save(array $log = []): bool { // ...省略部分代码... if (!empty($this->config['format_head'])) { try { $currentUri = $this->app->invoke($this->config['format_head'], [$currentUri]); } catch (NotFoundExceptionInterface $notFoundException) { // Ignore exception } } // ...省略部分代码... }
漏洞利用关键
-
反射调用:
$this->app->invoke()方法支持动态反射调用- 可以调用任意类的任意方法
-
Php视图驱动类的display方法:
public function display(string $content, array $data = []): void { $this->content = $content; extract($data, EXTR_OVERWRITE); eval('?>' . $this->content); }- 通过eval执行任意PHP代码
完整利用链
- 构造
Psr6Cache对象,设置lazySave为true - 设置
pool为Channel对象 - 在
Channel对象中设置logger为Socket驱动 - 在
Socket驱动中设置config['format_head']为[new \think\view\driver\Php, 'display'] - 通过反射调用最终执行
eval代码
0x02 漏洞复现
-
构造Payload:
$b = new think\log\Channel(); $a = new League\Flysystem\Cached\Storage\Psr6Cache($b); // 设置相关属性构造完整利用链 -
触发方式:
- 序列化恶意对象
- 在应用反序列化该对象时触发漏洞
0x03 防御措施
- 升级到最新版本
- 避免反序列化用户可控数据
- 对
__destruct和__wakeup等魔术方法进行安全审查 - 限制反射调用的使用范围
流程图
__destruct() → save() → getItem() → __call() → log() → record() → save() → Socket::save() → invoke() → display() → eval()