Java反序列化漏洞之殇
字数 1319 2025-08-29 08:32:00
Java反序列化漏洞全面解析与防御指南
一、序列化与反序列化基础
1.1 基本概念
- 序列化:将Java对象转换为字节序列的过程,便于保存在内存、文件或数据库中
- 反序列化:把字节序列恢复为Java对象的过程
- 关键类:
ObjectOutputStream.writeObject():实现序列化ObjectInputStream.readObject():实现反序列化
1.2 序列化数据结构
序列化后的数据具有特定格式:
STREAM_MAGIC (2 bytes) 0xACED
STREAM_VERSION (2 bytes) 0x0005
TC_OBJECT (1 byte) 0x73
TC_CLASSDESC (1 byte) 0x72
className length (2 bytes)
className text
serialVersionUID (8 bytes)
classDescFlags (1 byte)
fields count (2 bytes)
field descriptions
classAnnotation
superClassDesc
classdata[]
二、反序列化漏洞原理
2.1 漏洞成因
当输入的反序列化数据可被用户控制时,攻击者可通过构造恶意输入:
- 让反序列化产生非预期的对象
- 在此过程中执行构造的任意代码
2.2 典型漏洞代码
InputStream in = request.getInputStream();
ObjectInputStream ois = new ObjectInputStream(in);
ois.readObject(); // 危险点:未校验输入流
ois.close();
2.3 常见危险库
以下第三方库曾被用于反序列化漏洞利用:
- commons-collections 3.1/4.0
- commons-fileupload 1.3.1
- commons-io 2.4
- commons-beanutils 1.9.2
- org.slf4j:slf4j-api 1.7.21
- com.mchange:c3p0 0.9.5.2
- org.beanshell:bsh 2.0b5
- org.codehaus.groovy:groovy 2.3.9
三、漏洞检测方案
3.1 代码审计关键点
审计时应重点关注以下反序列化操作函数:
ObjectInputStream.readObject()
ObjectInputStream.readUnshared()
XMLDecoder.readObject()
Yaml.load()
XStream.fromXML()
ObjectMapper.readValue()
JSON.parseObject()
3.2 白盒检测方法
- 自动化工具检测
readObject()方法调用 - 确认参数是否来自外部输入
- 检查是否实现了安全校验
示例检测逻辑:
if (method instanceof ObjectInputStream.readObject &&
sink来自用户输入) {
report("可能存在反序列化漏洞");
}
3.3 黑盒检测技术
- 使用ysoserial生成payload测试:
java -jar ysoserial.jar CommonsCollections1 'curl http://test.com'
- 推荐工具:
- SerialBrute
- BaRMIe(RMI测试工具)
3.4 RASP检测
通过Hook resolveClass方法检测:
@Override
protected Class<?> resolveClass(ObjectStreamClass desc)
throws IOException, ClassNotFoundException {
// 检查类名是否在黑名单中
if (黑名单.contains(desc.getName())) {
throw new InvalidClassException("禁止反序列化");
}
return super.resolveClass(desc);
}
四、修复方案
4.1 最佳修复实践
方案1:Hook resolveClass校验
public class SecureObjectInputStream extends ObjectInputStream {
@Override
protected Class<?> resolveClass(ObjectStreamClass desc)
throws IOException, ClassNotFoundException {
if (!白名单.contains(desc.getName())) {
throw new InvalidClassException("未授权类");
}
return super.resolveClass(desc);
}
}
方案2:使用ValidatingObjectInputStream
ValidatingObjectInputStream ois = new ValidatingObjectInputStream(bais);
ois.accept(SerialObject.class); // 只允许特定类
Object obj = ois.readObject();
方案3:Java 9+ ObjectInputFilter
ObjectInputFilter filter = info -> {
if (info.serialClass() != null &&
!info.serialClass().getName().equals("允许的类")) {
return ObjectInputFilter.Status.REJECTED;
}
return ObjectInputFilter.Status.UNDECIDED;
};
ois.setObjectInputFilter(filter);
4.2 黑名单方案(不推荐)
仅在无法使用白名单时考虑,需持续维护:
private static final Set<String> BLACKLIST = Set.of(
"org.apache.commons.collections.functors.InvokerTransformer",
"org.apache.commons.collections4.functors.InstantiateTransformer",
"org.codehaus.groovy.runtime.ConvertedClosure",
"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl"
);
4.3 安全编码建议
- 更新所有存在漏洞的第三方库
- 尽量避免反序列化用户可控数据
- 优先使用白名单校验
- 对必须的序列化操作进行严格输入验证
五、高级防御技术
5.1 序列化数据特征检测
检测请求中是否包含序列化特征:
- 魔术数字:0xACED
- 版本号:0x0005
5.2 进阶审计技巧
- 查找所有实现
Serializable接口的类 - 检查是否有自定义的
readObject方法 - 分析是否存在危险操作(如Runtime.exec)
5.3 参考工具
- 反序列化漏洞检测:
- OWASP ZAP插件
- BurpSuite Java Serialize Scanner
- 修复参考:
- SerialKiller
- contrast-rO0
六、总结
Java反序列化漏洞危害严重且利用方式多样,防御需要:
- 理解漏洞原理
- 严格审计代码
- 实施有效修复方案
- 持续监控新出现的利用方式
通过白名单校验、输入过滤和危险类限制等多层防御,可有效降低反序列化漏洞风险。