【vulhub漏洞复现】Java < JDK8u232_b09 RMI Registry 反序列化远程代码执行绕过
本文最后更新于 2026年3月16日 晚上
一、漏洞原理分析
1.RMI原理
RMI(Remote Method Invocation)为远程方法调用,是允许运行在一个Java虚拟机的对象调用运行在另一个Java虚拟机上的对象的方法。 这两个虚拟机可以是运行在相同计算机上的不同进程中,也可以是运行在网络上的不同计算机中。
通过这样的方式可以实现分布式
在 RMI(Java 远程方法调用)的标准架构里,有三个独立角色:
| 角色 | 作用 | 比喻 |
|---|---|---|
| 服务端(Server) | 实现远程业务接口,提供真正的服务逻辑 | 提供服务的 “商家” |
| 注册中心(Registry) | 一个独立的目录服务,用来登记、查找远程服务对象 | 提供服务索引的 “黄页 / 中介” |
| 客户端(Client) | 调用远程服务的一方 | 找服务的 “用户” |
2.RMI实际价值
简单示例直接理解RMI的使用场景:
假设一个实际场景:服务端连接着数据库,接口是getUserById(int id),客户端需要获取用户信息:
- 客户端有接口:知道 “可以通过 id 查用户”,但没有数据库权限、没有数据库连接、没有查询逻辑;
- 服务端有接口 + 实现:拿着接口的定义,写了查询数据库的逻辑(select * from user where id=?);
- RMI 的作用:客户端调用getUserById(1)时,实际是服务端执行数据库查询,把查询结果返回给客户端 —— 客户端全程没碰数据库,只是拿到了结果。
如果没有 RMI,你要实现这个功能,得自己写 Socket 通信、序列化 / 反序列化、网络异常处理;而 RMI 帮你封装了这些底层细节,让你 “像调用本地方法一样调用远程方法”。
注意:客户端调用getUserById(1)时,是服务端执行getUserById()方法并传入1这个参数,并且查询的是服务端的数据库。这个过程中客户端没有任何实际业务代码,全在服务端。
1 | |
3.常见的RMI漏洞
3.1伪装成注册中心攻击客户端或服务端 (发生在客户端与服务端建立连接前的工作中)
- 注册中心攻击客户端原理
1 | |
当客户端环境有可利用的攻击链时候,会触发反序列化攻击rce.简单来说就是注册中心发送给客户端的数据会被客户端反序列化,进而触发漏洞.
1 | |
- 注册中心攻击服务端原理
业务服务端启动后,执行 registry.bind("服务名", 远程对象),向恶意注册中心发送 “绑定对象” 的请求,也就是将自己要提供给客户端的实际业务对象绑定到注册中心
恶意注册中心不处理绑定请求,直接返回构造好的响应包:首字节(1/2)+ 恶意序列化 payload
业务服务端底层调用 StreamRemoteCall.executeCall(),读取响应包首字节后,对余下的 InputStream
反序列化触发 gadget(如 Commons Collections),业务服务端 JVM 执行攻击者指定的恶意命令
1 | |
3.2客户端攻击注册中心
原理是RMI框架采用DGC(Distributed Garbage Collection)分布式垃圾收集机制来管理远程对象的生命周期,可以通过与DGC通信的方式发送恶意payload让注册中心反序列化。
DGC 是 RMI 协议的原生内置功能,所有 RMI 节点(注册中心、服务端、客户端)都运行 DGCImpl 组件,且暴露在 JRMP 端口(默认 1099)
条件jdk<=jdk8u111
1 | |
3.3服务端攻击注册中心
服务端需要将自己想注册的类绑定到注册中心上,可以直接发送反序列化数据给注册中心,进而可以造成反序列化漏洞。服务端调用bind(name,obj)注册远程对象,其中name,obj会以序列化方式发送给registry,registry反序列化它们,触发rce。
#使用bind的方式绑定恶意payload进行攻击,反序列化的直接执行链命令。
条件jdk<=jdk8u111
1 | |