thinkphp反序列化pop链的本质是通过可控反序列化入口,串联多个类的魔术方法(如__destruct、__tostring、__call)逐步跳转,最终触发任意代码执行或文件操作;其关键在于属性可控性与魔术方法的自动接力调用。

ThinkPHP反序列化漏洞利用链(POP链)的本质,是通过可控的反序列化入口,串联多个类的魔术方法调用,最终触发危险操作。它不依赖单一高危函数,而是靠对象间属性引用和自动执行的魔术方法(如__destruct、__toString、__call)逐步推进,直到达成任意文件写入、命令执行或文件删除等效果。
常见入口点与起始类
POP链通常从__destruct或__wakeup开始,因为它们在unserialize()后立即触发。ThinkPHP 6.x 中高频出现的起点包括:
-
Psr6Cache类(继承自AbstractCache),其__destruct中会调用save(),且pool和key属性可控 -
ResourceRegister类,__destruct中调用register(),进而触发parseGroupRule(),形成可控跳转 -
think\process\pipes\Windows类(TP5/6共用路径),__destruct→removeFiles()→file_exists($filename),强制类型转换可触发__toString()
关键跳转节点与触发逻辑
链的延续依赖中间类对魔术方法的“接力”调用。几个典型跳转模式如下:
- 当
file_exists()、echo $obj、strval($obj)等函数接收对象时,会自动调用__toString();而__toString()内部若调用$this->xxx()且该方法不存在,则触发__call() -
__call()方法若将参数直接传给call_user_func_array()或invoke(),且第一个参数为可控制的回调(如system、file_put_contents),第二个参数为可控数组,即可执行任意代码 - 部分链通过
Model类的save()或updateData()方法,结合$this->data、$this->exists等属性控制流程,最终进入db()->query()或log()->record()等可扩展点
可控性与构造要点
一条可用的POP链,必须保证每一步的关键属性均可控。实际构造时需重点关注:
- 所有参与链路的属性(如
$this->pool、$this->key、$this->files、$this->config)都应能通过反序列化字符串赋值,注意私有属性名称中的不可见字符(如"\0think\process\pipes\Windows\0files") - 避免因类型校验中断链路,例如
getRelationData()要求参数为Relation实例,就需构造一个合法的Relation子类对象并控制其$parent和$query - 慎用
__invoke()或__get()等间接触发点——它们依赖特定上下文(如对象被当作函数调用),不如__destruct稳定可靠
实战防御与验证建议
发现疑似POP链后,不要仅停留在静态分析。建议按以下步骤验证有效性:
- 确认目标环境中存在反序列化入口,例如
unserialize($_GET['data'])或日志、缓存、Session反序列化场景 - 使用
phpggc或手动构造最小POC,逐层打印调用栈,观察是否真能走到预期的危险函数(如unlink、file_put_contents) - 检查PHP版本与ThinkPHP补丁状态:6.0.13之后部分链已被修复,但新链仍在持续披露(如CVE-2024-44902)
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











