反序列化漏洞详解

我将从原理、利用和防御三个方面详细讲解反序列化漏洞。

一、什么是序列化和反序列化

序列化(Serialization):将对象转换为字节流或字符串格式,便于存储或传输。

反序列化(Deserialization):将字节流或字符串还原为对象的过程。

常见场景

  • 网络传输数据
  • 会话(Session)存储
  • 缓存系统(Redis、Memcached)
  • 消息队列
  • 文件存储

二、漏洞原理深度解析

2.1 核心问题

反序列化漏洞的核心在于:应用程序在反序列化不可信数据时,没有对数据进行充分验证,攻击者可以构造恶意的序列化数据,在反序列化过程中执行任意代码

2.2 为什么会执行代码?

在反序列化过程中,某些”魔术方法”或特殊方法会被自动调用:

PHP中的魔术方法

1
2
3
4
__wakeup()      // 反序列化时自动调用
__destruct() // 对象销毁时调用
__toString() // 对象被当作字符串使用时调用
__call() // 调用不存在的方法时触发

Java中的特殊方法

1
2
readObject()    // 反序列化时调用
finalize() // 垃圾回收时调用

Python中的方法

1
2
__reduce__()    // 序列化时定义如何重建对象
__setstate__() // 反序列化时恢复对象状态

2.3 攻击链(Gadget Chain)

攻击者通过构造”小工具链”(Gadget Chain),将多个类的方法串联起来,最终达到执行任意代码的目的。

简化示例(PHP)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 假设存在以下类
class Logger {
private $logFile;

function __destruct() {
file_put_contents($this->logFile, "Log data");
}
}

class FileManager {
private $file;

function __toString() {
return file_get_contents($this->file);
}
}

// 攻击者构造恶意对象
$exploit = new Logger();
$exploit->logFile = new FileManager();
$exploit->logFile->file = "/etc/passwd";

// 当反序列化时:
// 1. Logger对象销毁时调用__destruct()
// 2. logFile需要转为字符串,触发FileManager的__toString()
// 3. 读取敏感文件内容

三、各语言的利用方式

3.1 PHP反序列化

漏洞代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
class User {
public $username;
public $isAdmin = false;

function __wakeup() {
if($this->isAdmin) {
echo "Welcome Admin!";
// 执行管理员操作
}
}
}

// 不安全的反序列化
$userData = unserialize($_GET['data']);
?>

攻击payload

1
2
3
4
5
6
7
// 构造恶意对象
$malicious = new User();
$malicious->username = "attacker";
$malicious->isAdmin = true;

$payload = serialize($malicious);
// O:4:"User":2:{s:8:"username";s:8:"attacker";s:7:"isAdmin";b:1;}

高级利用 - POP链攻击
利用已存在的类构造攻击链,例如使用PHP原生类或第三方库的类。

3.2 Java反序列化

经典漏洞 - Apache Commons Collections

1
2
3
// 危险的反序列化代码
ObjectInputStream ois = new ObjectInputStream(inputStream);
Object obj = ois.readObject(); // 漏洞点

利用工具

  • ysoserial:自动生成Java反序列化payload
  • 常见Gadget:Commons Collections、Spring、FastJSON

3.3 Python反序列化

Pickle模块漏洞

1
2
3
4
5
6
7
8
9
10
11
12
13
import pickle
import os

# 恶意类
class Exploit:
def __reduce__(self):
return (os.system, ('whoami',))

# 生成payload
malicious = pickle.dumps(Exploit())

# 受害者反序列化时执行命令
pickle.loads(malicious) # 执行whoami命令

3.4 .NET反序列化

常见于BinaryFormatter、JavaScriptSerializer等。

四、实际攻击场景

场景1:Cookie/Session劫持

1
2
3
4
// 应用将用户数据序列化存储在Cookie中
$_COOKIE['user'] = serialize($userObject);

// 攻击者修改Cookie,提升权限

场景2:消息队列投毒

攻击者向消息队列注入恶意序列化数据,当消费者反序列化时触发漏洞。

场景3:API接口

1
2
3
4
5
6
POST /api/process
Content-Type: application/json

{
"data": "恶意序列化的Base64编码数据"
}

五、检测方法

5.1 代码审计关键点

寻找以下危险函数:

  • PHP: unserialize(), __wakeup(), __destruct()
  • Java: readObject(), ObjectInputStream
  • Python: pickle.loads(), yaml.load()
  • .NET: BinaryFormatter.Deserialize()

5.2 黑盒测试

  1. 识别序列化格式
    • Java: ac ed 00 05 (hex)
    • PHP: O:数字:"类名":{...}
    • Python Pickle: \x80\x03 开头
  2. Fuzzing测试:修改序列化数据,观察应用响应
  3. 使用工具
    • ysoserial (Java)
    • phpggc (PHP)
    • marshalsec

六、防御措施(重点)

6.1 最佳实践

1. 避免反序列化不可信数据(最有效)

1
2
3
4
5
// 不要这样做
Object obj = deserialize(request.getParameter("data"));

// 使用安全的数据格式
JSONObject json = new JSONObject(request.getParameter("data"));

2. 使用安全的数据格式

  • 优先使用JSON、XML等纯数据格式
  • 避免使用原生序列化机制传输用户可控数据

3. 输入验证和白名单

1
2
3
4
5
// Java - 使用ObjectInputFilter限制可反序列化的类
ObjectInputFilter filter = ObjectInputFilter.Config.createFilter(
"com.myapp.SafeClass;!*" // 只允许SafeClass
);
ois.setObjectInputFilter(filter);

4. 签名验证

1
2
3
4
5
6
7
8
9
10
// 对序列化数据进行HMAC签名
$data = serialize($object);
$signature = hash_hmac('sha256', $data, SECRET_KEY);
$payload = $signature . '|' . $data;

// 反序列化前验证签名
list($sig, $data) = explode('|', $payload);
if (hash_equals($sig, hash_hmac('sha256', $data, SECRET_KEY))) {
$object = unserialize($data);
}

5. 隔离和沙箱

  • 在受限环境中运行反序列化代码
  • 使用容器或虚拟机隔离

6.2 语言特定防御

PHP防御

1
2
3
4
5
6
7
8
9
// 1. 使用JSON代替serialize
$data = json_encode($object);
$object = json_decode($data);

// 2. 禁用phar反序列化(php.ini)
phar.readonly = On

// 3. 使用allowed_classes限制
$object = unserialize($data, ['allowed_classes' => ['SafeClass']]);

Java防御

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 1. 升级有漏洞的库(Commons Collections等)

// 2. 使用白名单过滤器
public class SafeObjectInputStream extends ObjectInputStream {
@Override
protected Class<?> resolveClass(ObjectStreamClass desc) {
if (!isWhitelisted(desc.getName())) {
throw new InvalidClassException("Unauthorized class");
}
return super.resolveClass(desc);
}
}

// 3. 避免使用Java原生序列化,改用Protocol Buffers或JSON

Python防御

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 1. 不要使用pickle处理不可信数据

# 2. 使用安全的替代品
import json
data = json.dumps(obj)
obj = json.loads(data)

# 3. 如果必须使用pickle,使用受限的Unpickler
import pickle
class RestrictedUnpickler(pickle.Unpickler):
def find_class(self, module, name):
if module == "myapp.models" and name == "SafeClass":
return getattr(sys.modules[module], name)
raise pickle.UnpicklingError("Forbidden class")

6.3 安全配置

  1. 最小权限原则:应用运行在低权限账户下
  2. 网络隔离:限制外部访问
  3. 监控和日志:记录反序列化操作
  4. 定期安全审计:检查依赖库漏洞

6.4 框架层面防御

1
2
3
4
5
6
7
8
9
// Spring Boot - 禁用不安全的反序列化
@Bean
public Jackson2ObjectMapperBuilder jacksonBuilder() {
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
builder.featuresToDisable(
DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES
);
return builder;
}

七、实战检查清单

开发阶段

  • 避免使用原生序列化机制
  • 使用JSON/XML等安全格式
  • 对必须序列化的数据进行签名
  • 实现类白名单机制

部署阶段

  • 更新所有第三方库
  • 配置安全策略(如Java的SecurityManager)
  • 启用应用防火墙(WAF)规则

运维阶段

  • 监控异常反序列化行为
  • 定期漏洞扫描
  • 应急响应预案

八、总结

反序列化漏洞的危害巨大,防御的核心原则是:

  1. 永远不要反序列化不可信的数据
  2. 优先使用纯数据格式(JSON/XML)
  3. 必要时使用签名+白名单双重防护
  4. 保持依赖库更新

记住:最好的防御是从设计上避免问题,而不是依赖于检测和阻断。

如果你需要针对某个特定语言或框架的更深入分析,或想了解某个真实案例的详细剖析,随时告诉我!