深入理解 RMI 之运行逻辑与漏洞原理
字数 2245 2025-08-12 11:34:05

深入理解RMI:运行逻辑与漏洞原理

1. RMI基础概念

1.1 RMI简介

RMI(Remote Method Invocation,远程方法调用)是Java中用于实现分布式应用的技术,允许一个JVM中的Java程序调用另一个远程JVM中的Java程序。RMI依赖JRMP(Java Remote Message Protocol)协议进行通信,该协议为Java定制,要求服务端与客户端都为Java编写。

1.2 RMI核心组件

  • Server(服务端):通过绑定远程对象,封装网络操作
  • Client(客户端):调用服务端的方法
  • Registry(注册中心):提供服务注册与服务获取

2. RMI实现机制

2.1 服务端实现

  1. 定义远程接口
public interface RemoteObj extends Remote {
    public String sayHello(String keywords) throws RemoteException;
}

要求:

  • 作用域为public
  • 继承Remote接口
  • 接口方法抛出RemoteException
  1. 实现远程接口
public class RemoteObjImpl extends UnicastRemoteObject implements RemoteObj {
    public RemoteObjImpl() throws RemoteException {
        // 如果不能继承UnicastRemoteObject就需要手工导出
        // UnicastRemoteObject.exportObject(this, 0); 
    }
    
    @Override
    public String sayHello(String keywords) throws RemoteException {
        String upKeywords = keywords.toUpperCase();
        System.out.println(upKeywords);
        return upKeywords;
    }
}

要求:

  • 实现远程接口
  • 继承UnicastRemoteObject类(用于生成Stub和Skeleton)
  • 构造函数抛出RemoteException
  • 实现类中使用的对象必须都可序列化
  1. 注册远程对象
public class RMIServer {
    public static void main(String[] args) throws RemoteException, AlreadyBoundException, MalformedURLException {
        // 实例化远程对象
        RemoteObj remoteObj = new RemoteObjImpl();
        // 创建注册中心
        Registry registry = LocateRegistry.createRegistry(1099);
        // 绑定对象示例到注册中心
        registry.bind("remoteObj", remoteObj);
    }
}

2.2 客户端实现

  1. 定义远程接口(与服务端相同)
  2. 获取远程对象并调用方法
public class RMIClient {
    public static void main(String[] args) throws Exception {
        Registry registry = LocateRegistry.getRegistry("127.0.0.1", 1099);
        RemoteObj remoteObj = (RemoteObj) registry.lookup("remoteObj");
        remoteObj.sayHello("hello");
    }
}

3. RMI通信原理

3.1 通信流程

  1. 客户端与注册中心(1099端口)建立连接
  2. 客户端查询需要调用的函数的远程引用
  3. 注册中心返回远程引用和提供该服务的服务端IP与端口
  4. 客户端新起一个端口与服务端建立TCP通讯
  5. 客户端发送远程引用给服务端
  6. 服务端返回函数唯一标识符确认可被调用
  7. 客户端序列化传输调用函数的输入参数至服务端
  8. 服务端返回序列化的执行结果至客户端

3.2 通信特点

  • 实际建立两次TCP连接:
    • 第一次:客户端连接Registry的1099端口
    • 第二次:服务端发送给客户端的连接
  • 所有数据流都使用序列化传输
  • Registry作为网关,不执行远程方法,只维护绑定关系

4. RMI运行机制深入分析

4.1 创建远程服务

  1. 发布远程对象

    • 通过exportObject()方法发布
    • 端口为0时发布到随机端口
    • 创建LiveRef对象处理网络引用
    • 创建Stub(存根)和Skeleton(骨架)
  2. 发布完成记录

    • 信息保存到静态的HashMap中
    • 通过ObjectTable.putTarget()存储封装数据

4.2 创建注册中心

  1. 通过createRegistry()方法创建
  2. 创建RegistryImpl对象
  3. 安全检查(端口是否为1099,是否开启SecurityManager)
  4. 创建LiveRefUnicastServerRef
  5. 创建StubSkeleton
    • 通过反射创建RegistryImpl_Stub
    • 通过forName()创建Skeleton

4.3 绑定过程

  • 检查是否为本地绑定
  • 检查bindings中是否已有数据
  • bindings.put(name, obj)将IP和端口放入HashTable

4.4 客户端请求处理

  1. 获取注册中心

    • 创建LiveRef
    • 通过Util.createProxy()创建代理
  2. 查找远程对象

    • 通过invoke()方法激活
    • call.executeCall()处理网络请求
    • 存在反序列化漏洞点
  3. 客户端请求服务端

    • 通过ref.invoke()创建连接
    • marshalValue()序列化参数
    • unmarshalValueSee()反序列化返回数据(存在漏洞点)

4.5 注册中心处理请求

  • 通过Target处理请求
  • disp.dispatch()方法分发请求
  • RegistryImpl_Skel#dispatch处理以下操作:
    • 0->bind
    • 1->list
    • 2->lookup
    • 3->rebind
    • 4->unbind
  • list外都存在反序列化漏洞

4.6 DGC(分布式垃圾回收)机制

  1. 自动创建过程

    • 通过DGCImpl类初始化
    • 创建DGCImpl_Stub用于内存回收
    • 端口随机分配
  2. 漏洞点

    • DGCImpl_Stub中的cleandirty方法
    • DGCImpl_Skel中的反序列化操作

5. RMI漏洞原理

5.1 漏洞点总结

  1. 客户端查找远程对象时

    • call.executeCall()中异常处理时的反序列化
    • 服务端可向客户端发送恶意对象
  2. 客户端请求服务端时

    • unmarshalValueSee方法中的反序列化
  3. 注册中心处理请求时

    • bindrebindunbindlookup操作中的反序列化
  4. DGC机制中

    • DGCImpl_StubDGCImpl_Skel中的反序列化

5.2 安全限制

  • JDK8u121之后,bindrebindunbind方法只能对localhost进行操作
  • 多数攻击在JDK8u121之后已修复

6. 实际应用与防护建议

6.1 实际应用

  • RMI多用于后续的Fastjson、Struts2等攻击组合
  • 单独攻击RMI意义有限

6.2 防护建议

  1. 升级JDK至最新版本
  2. 限制RMI服务仅监听localhost
  3. 配置SecurityManager进行权限控制
  4. 对序列化数据进行严格验证
  5. 使用SSL/TLS加密RMI通信

7. 参考资料

  1. RMI反序列化视频教程
  2. RMI反序列化技术分析
深入理解RMI:运行逻辑与漏洞原理 1. RMI基础概念 1.1 RMI简介 RMI(Remote Method Invocation,远程方法调用)是Java中用于实现分布式应用的技术,允许一个JVM中的Java程序调用另一个远程JVM中的Java程序。RMI依赖JRMP(Java Remote Message Protocol)协议进行通信,该协议为Java定制,要求服务端与客户端都为Java编写。 1.2 RMI核心组件 Server(服务端) :通过绑定远程对象,封装网络操作 Client(客户端) :调用服务端的方法 Registry(注册中心) :提供服务注册与服务获取 2. RMI实现机制 2.1 服务端实现 定义远程接口 : 要求: 作用域为public 继承Remote接口 接口方法抛出RemoteException 实现远程接口 : 要求: 实现远程接口 继承UnicastRemoteObject类(用于生成Stub和Skeleton) 构造函数抛出RemoteException 实现类中使用的对象必须都可序列化 注册远程对象 : 2.2 客户端实现 定义远程接口 (与服务端相同) 获取远程对象并调用方法 : 3. RMI通信原理 3.1 通信流程 客户端与注册中心(1099端口)建立连接 客户端查询需要调用的函数的远程引用 注册中心返回远程引用和提供该服务的服务端IP与端口 客户端新起一个端口与服务端建立TCP通讯 客户端发送远程引用给服务端 服务端返回函数唯一标识符确认可被调用 客户端序列化传输调用函数的输入参数至服务端 服务端返回序列化的执行结果至客户端 3.2 通信特点 实际建立两次TCP连接: 第一次:客户端连接Registry的1099端口 第二次:服务端发送给客户端的连接 所有数据流都使用序列化传输 Registry作为网关,不执行远程方法,只维护绑定关系 4. RMI运行机制深入分析 4.1 创建远程服务 发布远程对象 : 通过 exportObject() 方法发布 端口为0时发布到随机端口 创建 LiveRef 对象处理网络引用 创建 Stub (存根)和 Skeleton (骨架) 发布完成记录 : 信息保存到静态的HashMap中 通过 ObjectTable.putTarget() 存储封装数据 4.2 创建注册中心 通过 createRegistry() 方法创建 创建 RegistryImpl 对象 安全检查(端口是否为1099,是否开启SecurityManager) 创建 LiveRef 和 UnicastServerRef 创建 Stub 和 Skeleton : 通过反射创建 RegistryImpl_Stub 通过 forName() 创建 Skeleton 4.3 绑定过程 检查是否为本地绑定 检查 bindings 中是否已有数据 bindings.put(name, obj) 将IP和端口放入HashTable 4.4 客户端请求处理 获取注册中心 : 创建 LiveRef 通过 Util.createProxy() 创建代理 查找远程对象 : 通过 invoke() 方法激活 call.executeCall() 处理网络请求 存在反序列化漏洞点 客户端请求服务端 : 通过 ref.invoke() 创建连接 marshalValue() 序列化参数 unmarshalValueSee() 反序列化返回数据(存在漏洞点) 4.5 注册中心处理请求 通过 Target 处理请求 disp.dispatch() 方法分发请求 RegistryImpl_Skel#dispatch 处理以下操作: 0->bind 1->list 2->lookup 3->rebind 4->unbind 除 list 外都存在反序列化漏洞 4.6 DGC(分布式垃圾回收)机制 自动创建过程 : 通过 DGCImpl 类初始化 创建 DGCImpl_Stub 用于内存回收 端口随机分配 漏洞点 : DGCImpl_Stub 中的 clean 和 dirty 方法 DGCImpl_Skel 中的反序列化操作 5. RMI漏洞原理 5.1 漏洞点总结 客户端查找远程对象时 : call.executeCall() 中异常处理时的反序列化 服务端可向客户端发送恶意对象 客户端请求服务端时 : unmarshalValueSee 方法中的反序列化 注册中心处理请求时 : bind 、 rebind 、 unbind 、 lookup 操作中的反序列化 DGC机制中 : DGCImpl_Stub 和 DGCImpl_Skel 中的反序列化 5.2 安全限制 JDK8u121之后, bind 、 rebind 、 unbind 方法只能对localhost进行操作 多数攻击在JDK8u121之后已修复 6. 实际应用与防护建议 6.1 实际应用 RMI多用于后续的Fastjson、Struts2等攻击组合 单独攻击RMI意义有限 6.2 防护建议 升级JDK至最新版本 限制RMI服务仅监听localhost 配置SecurityManager进行权限控制 对序列化数据进行严格验证 使用SSL/TLS加密RMI通信 7. 参考资料 RMI反序列化视频教程 RMI反序列化技术分析