PHP反序列化原理及绕过技巧解析

慢慢来
慢慢来
发布时间:2026-10-13 | 3人浏览过
原创

PHP序列化常用于数据缓存传输。掌握序列化结构与魔术方法(如__wakeup)是理解漏洞的基础。本文将解析PHP反序列化核心原理,并详细说明如何绕过正则过滤、CVE-2016-7124缺陷及属性校验等常见安全措施。

反序列化简介与魔术方法

在PHP开发中,序列化(Serialization)是将对象转换为可存储或传输的字符串过程,反序列化则是还原该对象的过程。这一机制广泛应用于Session缓存、Cookie传递或跨服务器数据传输。

反序列化的核心在于类中定义的“魔术方法”(Magic Methods),它们在特定生命周期被自动调用:

  1. __wakeup():执行 unserialize() 时立即触发。
  2. __sleep():执行 serialize() 前触发,用于定义需要序列化的属性。
  3. __destruct():对象在所有引用被删除后销毁时触发。
  4. __get() / __set():读写不可访问或未定义属性的触发器。
  5. __call() / __callStatic():调用不可访问的方法时的静态与非静态触发器。
  6. __toString():将对象当作字符串输出(如 echo $obj;)时触发。
  7. __invoke():尝试将对象作为函数调用时触发。

反序列化绕过技巧与原理

在实际安全测试中,常遇到WAF或过滤代码对序列化字符串进行限制。以下列举几种经典的绕过策略。

第1步:利用属性不敏感绕过(PHP 7.1+)

在早期的PHP版本中,不同可见性属性的序列化字符串有明显区分:
- Public属性:直接存储名字。
- Protected属性:以 %00*%00 包裹。
- Private属性:以 %00类名%00 包裹。

但在 PHP 7.1 及以上版本,反序列化引擎变得不敏感,不再严格区分属性可见性的特殊字符。这意味着即使序列化字符串中缺少这些字节,也能正确识别属性并赋值。

<?php
class test{
    protected $a;
    public function __construct(){
        $this->a = 'abc';
    }
    public function  __destruct(){
        echo $this->a;
    }
}
// 即使使用了public的格式 s:1:"a",依然能生效
unserialize('O:4:"test":1:{s:1:"a";s:3:"abc";}');

第2步:跳过 __wakeup 执行(CVE-2016-7124)

此漏洞影响版本:
- PHP 5 < 5.6.25
- PHP 7 < 7.0.10

利用条件:当序列化字符串中表示的对象属性个数(count)大于实际属性个数时,__wakeup() 将被跳过,从而无法执行清理操作或关闭某些功能。

修改序列化字符串中的数值部分即可实现绕过:

// 原始对象有两个属性,但构造只赋值了一个,此处演示伪造
// O:4:"test":1:{...} 改为 O:4:"test":2:{...}

注意:在PHP 7.0.10之后该Trick已失效,新版本引入了严格的长度检查。

第3步:利用数组闭合绕过正则检测

许多过滤器使用 preg_match('/^O:\d+/') 拦截对象序列化开头。如果我们将目标对象放入数组中,外层将是数组标识符 a:,而对象位于内部,可完美绕过开头检测。

<?php
function match($data){
    if (preg_match('/^O:\d+/',$data)){
        die('Blocked!');
    }else{
        return $data;
    }
}
// 正常对象开头是 O:4:"test"
$payload = 'a:1:{i:0;O:4:"test":1:{s:1:"a";s:3:"abc";}}';
unserialize(match($payload)); // 成功通过,内部test对象析构

第4步:利用加号伪装整数

PHP允许在整数前添加正负号。通过将 O:4 修改为 O:+4,可以改变字符串外观以混淆正则表达式(如匹配非数字开头的场景):

$a = 'O:4:"test":1:{s:1:"a";s:3:"abc";}';
$b = str_replace('O:4', 'O:+4', $a);
unserialize($b); // 仍然能被解析

第5步:属性引用特性与十六进制编码

引用:若在 __construct 中设置变量引用(如 $this->b = &$this->a;),反序列化后可通过比较两者值达到特定逻辑判断。

十六进制:序列化字符串中的键名(string)支持十六进制转义。例如小写字母 a 可写为 \61,这可用于绕过简单的黑名单:

标准:s:1:"a" → 十六进制改写:S:1:"\61" (注意 S 大写在某些情况下也会引发解析变化,主要利用其表示字符类型的灵活性)。

总结

了解序列化字符串的结构是防御和检测反序列化漏洞的关键。从简单的魔术方法调用到复杂的绕过技巧(如CVE修复前的版本差异、引用传递等),每一个点都可能成为代码审计的重点。

以上就是PHP浅析反序列化结构及常见绕过技术的详细内容,更多关于PHP安全与反序列化漏洞防护的资料请关注本站其它相关文章!

相关标签:

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表