Java反序列化RMI专题
1.创建远程服务
用exportObject()指定到发布的 IP 与端口,端口的话是一个随机值。至始至终复杂的地方其实都是在赋值,创建类,进行各种各样的封装,实际上并不复杂。是Stub
创建一个临时的对象,把临时封装的对象put到一个hash表中
2.创建注册中心+绑定
总结一下比较简单,注册中心这里其实和发布远程对象很类似,不过多了一个持久的对象,这个持久的对象就成为了注册中心。
创建一个永久对象,把永久封装的对象put到一个表中。是skel
绑定的话就更简单了,一句话形容一下就是 hashTable.put(IP, port)
3.客户端请求,客户端调用注册中心
查找远程对象,首先获取注册中心。然后在lookup里面把对象给反序列化掉了,然后invoke()—>
call.executeCall()—>
out.getDGCAckHandler()
处理真实的网络请求。out.getDGCAckHandler()在这里面有个try,这里它有一个异常存在潜在攻击的可能性

不难理解,in 就是数据流里面的东西。这里获取异常的本意应该是在报错的时候把一整个信息都拿出来,这样会更清晰一点,但是这里就出问题了 ———— 如果一个注册中心返回一个恶意的对象,客户端进行反序列化,这就会导致漏洞。这里的漏洞相比于其他漏洞更为隐蔽。
4. 客户端请求,客户端请求服务端
先说说存在攻击的点吧,在注册中心 –> 服务端这里,查找远程对象的时候是存在攻击的。具体表现形式是服务端打客户端,入口类在call.executeCall(),里面抛出异常的时候会进行反序列化。
这里可以利用 URLClassLoader 来打,具体的攻击在后续文章会写。

在服务端 —> 客户端这里,也是存在攻击的,一共是两个点:一个是 call.executeCall()
,另一个点是 unmarshalValueSee
这里。
5. 客户端发起请求,注册中心如何处理.
我们与注册中心进行交互可以使用如下几种方式:
- list
- bind
- rebind
- unbind
- lookup
这几种方法位于 RegistryImpl_Skel#dispatch
中,也就是我们现在 dispatch 这个方法的地方。
如果存在对传入的对象调用 readObject
方法,则可以利用,dispatch
里面对应关系如下:
- 0->bind
- 1->list
- 2->lookup
- 3->rebind
- 4->unbind
只要中间是有反序列化就是可以攻击的,而且我们是从客户端打到注册中心,这其实是黑客们最喜欢的攻击方式。
除了list都可以攻击
简单,注册中心就是处理 Target,进行 Skel 的生成与处理。
漏洞点是在 dispatch 这里,存在反序列化的入口类。这里可以结合 CC 链子打的。
到 DGCImpl_Stub
这个类下,它有两个方法,一个是 clean,另外一个是 dirty。clean 就是”强”清除内存,dirty 就是”弱”清除内存。

同样在 DGCImpl_Skel
这个类下也存在反序列化的漏洞,如图。
- 是自动创建的一个过程,用于清理内存。
漏洞点在客户端与服务端都存在,存在于 Skel
与 Stub
当中。这也就是所谓的 JRMP 绕过
总结
- 如果是漏洞利用的话,单纯攻击 RMI 意义是不大的,不论是 codespace 的那种利用,难度很高,还是说三者互相打这种,意义都不是很大,因为在 jdk8u121 之后都基本修复完毕了。
RMI 多数的利用还是在后续的 fastjson,strust2 这种类型的攻击组合拳比较多,希望这篇文章能对正在学习 RMI 的师傅们提供一点帮助。

RMI 底层通讯采用了Stub (运行在客户端) 和 Skeleton (运行在服务端) 机制,RMI 调用远程方法的大致如下:
- RMI 客户端在调用远程方法时会先创建 Stub (
sun.rmi.registry.RegistryImpl_Stub
)。 - Stub 会将 Remote 对象传递给远程引用层 (
java.rmi.server.RemoteRef
) 并创建java.rmi.server.RemoteCall
( 远程调用 )对象。 - RemoteCall 序列化 RMI 服务名称、Remote 对象。
- RMI 客户端的远程引用层传输 RemoteCall 序列化后的请求信息通过 Socket 连接的方式传输到 RMI 服务端的远程引用层。
- RMI服务端的远程引用层(
sun.rmi.server.UnicastServerRef
)收到请求会请求传递给 Skeleton (sun.rmi.registry.RegistryImpl_Skel#dispatch
)。 - Skeleton 调用 RemoteCall 反序列化 RMI 客户端传过来的序列化。
- Skeleton 处理客户端请求:bind、list、lookup、rebind、unbind,如果是 lookup 则查找 RMI 服务名绑定的接口对象,序列化该对象并通过 RemoteCall 传输到客户端。
- RMI 客户端反序列化服务端结果,获取远程对象的引用。
- RMI 客户端调用远程方法,RMI服务端反射调用RMI服务实现类的对应方法并序列化执行结果返回给客户端。
- RMI 客户端反序列化 RMI 远程方法调用结果。
参考:
https://drun1baby.top/2022/07/19/Java%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E4%B9%8BRMI%E4%B8%93%E9%A2%9801-RMI%E5%9F%BA%E7%A1%80/#3-%E5%AE%A2%E6%88%B7%E7%AB%AF%E8%AF%B7%E6%B1%82%EF%BC%8C%E5%AE%A2%E6%88%B7%E7%AB%AF%E8%B0%83%E7%94%A8%E6%B3%A8%E5%86%8C%E4%B8%AD%E5%BF%83