PHP反序列化漏洞详解
字数 2150 2025-08-15 21:33:52

PHP反序列化漏洞详解

一、基础概念

1. 序列化与反序列化

  • 序列化:将对象转化为字符串的操作,便于存储和传输
  • 反序列化:将序列化后的字符串恢复成对象的过程

PHP中的序列化与反序列化函数:

serialize()   // 序列化
unserialize() // 反序列化

2. 序列化格式示例

class azhe {
    public $iq = '200';
    public $eq = 300;
    private $pr = "4ut15m";
}

序列化结果:

O:4:"azhe":3:{s:2:"iq";s:3:"200";s:2:"eq";i:300;s:8:"azhepr";s:6:"4ut15m";}

3. 序列化字符串类型

  • O:d - 对象,d代表对象类型长度
  • a:d - 数组,d代表元素数量
  • s:d - 字符串,d代表长度
  • i:d - 整型,d代表值
  • b - 布尔型
  • d - 双精度浮点型
  • N - NULL

二、PHP Session序列化引擎

PHP有三种session序列化引擎:

  1. php (默认)

    • 格式:key|serialize后的结果
  2. php_serialize

    • serialize()函数结果一致
  3. php_binary

    • 格式:键名长度(ASCII字符) + 键名 + serialize()序列化值

三、魔术方法

魔术方法在特定条件下自动调用:

魔术方法 触发条件
__wakeup() 使用unserialize时触发
__sleep() 使用serialize时触发
__destruct() 对象被销毁时触发
__call() 调用不可访问方法时触发
__callStatic() 静态调用不可访问方法时触发
__get() 读取不可访问属性时触发
__set() 写入不可访问属性时触发
__isset() 对不可访问属性调用isset()empty()时触发
__unset() 对不可访问属性使用unset()时触发
__toString() 把类当作字符串使用时触发
__invoke() 将对象作为函数调用时触发

四、反序列化漏洞利用

1. 原生类利用

PHP内置类中拥有魔术方法的类可用于攻击:

$classes = get_declared_classes();
foreach($classes as $class) {
    $methods = get_class_methods($class);
    // 检查魔术方法...
}

常用原生类:

  • Error/Exception:触发__toString可实现XSS
  • SoapClient:利用__call方法发起SSRF

2. 反序列化字符逃逸

字符增加情况

当敏感词被替换为更长的字符串时,可能导致序列化结构被破坏,从而逃逸出可控部分。

示例:

function filter($string) {
    return preg_replace('/QAQ/','wuwu',$string); 
}

payload构造思路:计算需要逃逸的字符串长度,用足够数量的敏感词填充。

字符减少情况

当敏感词被替换为更短的字符串时,需要至少两处可控点来构造逃逸。

示例:

function filter($string) {
    return preg_replace('/wuwu/','QAQ',$string); 
}

3. PHAR协议利用

PHAR文件是PHP的打包文件,当phar.readonly=Off时可生成PHAR文件。

PHAR文件结构:

  1. Stub - 必须包含<?php __HALT_COMPILER(); ?>
  2. Manifest - 存放PHAR归档信息
  3. Contents - 文件内容
  4. Signature - 签名

触发PHAR反序列化的文件操作函数:

fileatime, filectime, file_exists, file_get_contents, 
file_put_contents, file, filegroup, fopen, fileinode, 
filemtime, fileowner, fileperms, is_dir, is_executable, 
is_file, is_link, is_readable, is_writable, is_writeable, 
parse_ini_file, copy, unlink, stat, readfile

4. PHP引用

使用&可使不同变量指向同一值,序列化结果中表示为R:2;

五、实战案例

1. BUGKU 安慰奖

  • 反序列化入门题
  • 利用__destruct__wakeup魔术方法
  • 绕过过滤执行命令

2. BUUCTF ZJCTF 2019 NiZhuanSiWei

  • 利用php://inputphp://filter伪协议
  • 反序列化触发文件读取

3. MRCTF2020 Ezpop

  • POP链构造:__destruct -> __toString -> __get -> __invoke
  • 最终触发文件包含

4. CISCN2019 Day1 Web1 Dropbox

  • 任意文件下载获取源码
  • 利用PHAR协议触发反序列化
  • 构造POP链读取flag

5. 网鼎杯 2020 青龙组 AreUSerialz

  • 利用PHP弱类型比较漏洞(=====区别)
  • 绕过字符过滤读取文件

6. 0CTF 2016 piapiapia

  • 序列化字符串字符增加导致的逃逸
  • 利用数组绕过长度检查

7. 安洵杯 2019 easy_serialize_php

  • 序列化字符串字符减少导致的逃逸
  • 利用extract变量覆盖$_SESSION

8. bestphp's revenge

  • 利用SoapClient原生类发起SSRF
  • 修改session序列化引擎
  • 通过call_user_func触发__call方法

六、防御措施

  1. 不要反序列化不可信数据
  2. 使用json_encode/json_decode替代序列化
  3. 实施严格的输入验证
  4. 使用PHP 7的allowed_classes选项限制反序列化类
  5. 及时更新PHP版本修复已知漏洞

七、总结

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

  1. 找到程序中的反序列化入口点
  2. 分析可用类和魔术方法构造POP链
  3. 利用字符逃逸、PHAR协议或原生类实现攻击
  4. 最终达成文件操作、代码执行或SSRF等目的

理解序列化格式、魔术方法触发条件和各种利用技巧是掌握PHP反序列化漏洞的关键。

PHP反序列化漏洞详解 一、基础概念 1. 序列化与反序列化 序列化 :将对象转化为字符串的操作,便于存储和传输 反序列化 :将序列化后的字符串恢复成对象的过程 PHP中的序列化与反序列化函数: 2. 序列化格式示例 序列化结果: 3. 序列化字符串类型 O:d - 对象,d代表对象类型长度 a:d - 数组,d代表元素数量 s:d - 字符串,d代表长度 i:d - 整型,d代表值 b - 布尔型 d - 双精度浮点型 N - NULL 二、PHP Session序列化引擎 PHP有三种session序列化引擎: php (默认) 格式: key|serialize后的结果 php_ serialize 与 serialize() 函数结果一致 php_ binary 格式:键名长度(ASCII字符) + 键名 + serialize()序列化值 三、魔术方法 魔术方法在特定条件下自动调用: | 魔术方法 | 触发条件 | |---------|---------| | __wakeup() | 使用 unserialize 时触发 | | __sleep() | 使用 serialize 时触发 | | __destruct() | 对象被销毁时触发 | | __call() | 调用不可访问方法时触发 | | __callStatic() | 静态调用不可访问方法时触发 | | __get() | 读取不可访问属性时触发 | | __set() | 写入不可访问属性时触发 | | __isset() | 对不可访问属性调用 isset() 或 empty() 时触发 | | __unset() | 对不可访问属性使用 unset() 时触发 | | __toString() | 把类当作字符串使用时触发 | | __invoke() | 将对象作为函数调用时触发 | 四、反序列化漏洞利用 1. 原生类利用 PHP内置类中拥有魔术方法的类可用于攻击: 常用原生类: Error/Exception :触发 __toString 可实现XSS SoapClient :利用 __call 方法发起SSRF 2. 反序列化字符逃逸 字符增加情况 当敏感词被替换为更长的字符串时,可能导致序列化结构被破坏,从而逃逸出可控部分。 示例: payload构造思路:计算需要逃逸的字符串长度,用足够数量的敏感词填充。 字符减少情况 当敏感词被替换为更短的字符串时,需要至少两处可控点来构造逃逸。 示例: 3. PHAR协议利用 PHAR文件是PHP的打包文件,当 phar.readonly=Off 时可生成PHAR文件。 PHAR文件结构: Stub - 必须包含 <?php __HALT_COMPILER(); ?> Manifest - 存放PHAR归档信息 Contents - 文件内容 Signature - 签名 触发PHAR反序列化的文件操作函数: 4. PHP引用 使用 & 可使不同变量指向同一值,序列化结果中表示为 R:2; 。 五、实战案例 1. BUGKU 安慰奖 反序列化入门题 利用 __destruct 和 __wakeup 魔术方法 绕过过滤执行命令 2. BUUCTF ZJCTF 2019 NiZhuanSiWei 利用 php://input 和 php://filter 伪协议 反序列化触发文件读取 3. MRCTF2020 Ezpop POP链构造: __destruct -> __toString -> __get -> __invoke 最终触发文件包含 4. CISCN2019 Day1 Web1 Dropbox 任意文件下载获取源码 利用PHAR协议触发反序列化 构造POP链读取flag 5. 网鼎杯 2020 青龙组 AreUSerialz 利用PHP弱类型比较漏洞( == 与 === 区别) 绕过字符过滤读取文件 6. 0CTF 2016 piapiapia 序列化字符串字符增加导致的逃逸 利用数组绕过长度检查 7. 安洵杯 2019 easy_ serialize_ php 序列化字符串字符减少导致的逃逸 利用 extract 变量覆盖 $_SESSION 8. bestphp's revenge 利用SoapClient原生类发起SSRF 修改session序列化引擎 通过 call_user_func 触发 __call 方法 六、防御措施 不要反序列化不可信数据 使用 json_encode/json_decode 替代序列化 实施严格的输入验证 使用PHP 7的 allowed_classes 选项限制反序列化类 及时更新PHP版本修复已知漏洞 七、总结 PHP反序列化漏洞的核心在于: 找到程序中的反序列化入口点 分析可用类和魔术方法构造POP链 利用字符逃逸、PHAR协议或原生类实现攻击 最终达成文件操作、代码执行或SSRF等目的 理解序列化格式、魔术方法触发条件和各种利用技巧是掌握PHP反序列化漏洞的关键。