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序列化引擎:
-
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内置类中拥有魔术方法的类可用于攻击:
$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文件结构:
- Stub - 必须包含
<?php __HALT_COMPILER(); ?> - Manifest - 存放PHAR归档信息
- Contents - 文件内容
- 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://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反序列化漏洞的关键。