thinkphp反序列化利用链本质是串联可控对象与魔术方法调用路径,最终导向危险操作;入口多为__destruct或__wakeup,中间依赖__tostring传递,终点指向system、file_put_contents等敏感函数,需适配版本差异与绕过限制。

ThinkPHP反序列化利用链的构造,本质是串联可控对象与魔术方法调用路径,最终导向危险操作(如任意代码执行、文件删除、写入等)。关键不在于堆砌类名,而在于理解每一步的触发条件和数据流向。
入口点:从__destruct或__wakeup开始
绝大多数ThinkPHP版本的POP链以__destruct为起点,因其在对象销毁时必然触发,无需额外上下文。例如:
- ThinkPHP 5.0–5.1 中
think\process\pipes\Windows类的__destruct()调用removeFiles(); - ThinkPHP 6.0 中某些容器或驱动类的
__destruct可能触发资源清理逻辑,进而调用属性对象的字符串转换; -
__wakeup在反序列化完成瞬间执行,适合控制初始化行为,但需注意 PHP 7.4+ 对其调用限制增强,部分链已失效。
中间跳板:__toString 是最常用的桥梁
当removeFiles()中调用file_exists($filename),而$filename是一个对象时,PHP会自动调用该对象的__toString()方法——这是链式传递的核心枢纽。
- 常见跳板类包括
think\Model、think\model\Pivot(继承Model)、think\Image\Driver\Imagick等; - 这些类的
__toString()往往调用toJson()→toArray()→getAttr(),最终可能触发__call()或反射调用; - 只要
__toString内部存在对用户可控属性的间接调用(如$this->data[$key]或$value->method()),就可继续向下传导。
终点控制:导向敏感函数调用
链的末端目标明确:执行任意PHP函数。主流落点有三类:
-
命令执行:通过
__call()触发system、exec、call_user_func等,典型如think\Request类中$filter和$config配合isAjax()实现system('ls'); -
文件操作:利用
file_put_contents写入Webshell,常见于think\Cache\Driver\File的set()方法,需控制$name(文件路径)和$value(内容); -
文件删除:如
Windows::removeFiles()+file_exists($obj)→__toString()→unlink($path),只需让$files数组包含可控路径字符串或可转字符串的对象。
实战要点:环境与绕过不可忽视
构造出理论链只是第一步,真实利用受版本、配置、PHP特性影响极大:
- PHP 版本差异:7.2+ 对
unserialize的异常处理更严格;8.0+ 移除了部分魔术方法的隐式调用场景; - 死亡绕过(Dead Code Bypass):若
__wakeup中存在return;或提前 exit,需改用__destruct入口; - Phar 扩展攻击面:当存在文件系统函数(
file_exists、copy、imagecreatefromjpeg等)且参数可控时,可用phar://协议直接触发反序列化,绕过显式unserialize()调用; - 类名空间与自动加载:确保所用类在目标环境中真实存在且可被自动加载,避免因类未定义导致链中断。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











