针对RMI服务的九重攻击 - 下
字数 1654 2025-08-25 22:59:02

针对RMI服务的九重攻击 - 下篇:绕过JEP290的JRMP利用方式

前言

本文重点讲述如何绕过JEP290限制,通过引入JRMP协议对RMI服务进行攻击。JEP290是Java的反序列化防御机制,默认只为RMI注册表和RMI分布式垃圾收集器(DGC)提供了内置过滤器,但最底层的JRMP协议没有过滤器,这成为绕过防御的关键。

利用JRMP反序列化绕过JEP290

JRMP服务端攻击JRMP客户端

攻击流程:

  1. 攻击者自实现恶意JRMP服务端
  2. 受害者作为JRMP客户端连接攻击者的服务端
  3. 恶意服务端返回包含payload的响应包
  4. 客户端反序列化响应时触发漏洞

关键点:

  • JRMP客户端的反序列化点在sun.rmi.transport.StreamRemoteCall#executeCall
  • 服务端在sun.rmi.transport.StreamRemoteCall#getResultStream写入payload
  • 绕过原理:JRMP层没有JEP290过滤器

与RMI服务端反序列化攻击RMI注册端结合

攻击流程:

  1. 攻击者发送payload让RMI注册端作为JRMP客户端连接恶意JRMP服务端
  2. 恶意JRMP服务端返回利用链
  3. 由于JRMP客户端反序列化无过滤器,payload被执行

关键点:

  • 需要找到白名单内的Gadget让注册端发起JRMP连接
  • 攻击链分为两部分:诱导连接和实际攻击

UnicastRef对象利用

调用栈分析

UnicastRef.newCall(RemoteObject, Operation[], int, long)
DGCImpl_Stub.dirty(ObjID[], long, Lease)
DGCClient$EndpointEntry.makeDirtyCall(Set<RefEntry>, long)
DGCClient$EndpointEntry.registerRefs(List<LiveRef>)
DGCClient.registerRefs(Endpoint, List<LiveRef>)
LiveRef.read(ObjectInput, boolean)
UnicastRef.readExternal(ObjectInput)

触发过程

  1. readObject()组装填入ref
  2. releaseInputStream()统一处理ref

攻击方式实现

1. 绕过客户端-自实现协议

底层实现bind/lookup协议,直接发送UnicastRef对象,不依赖Remote接口。

2. 动态代理方式

自定义拦截器

public static class PocHandler implements InvocationHandler, Serializable {
    private RemoteRef ref;
    
    protected PocHandler(RemoteRef newref) {
        ref = newref;
    }
    
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) {
        return this.ref;
    }
}

RemoteObjectInvocationHandler

Ysoserial的实现方式,利用JDK内置的RemoteObjectInvocationHandler。

3. 使用内置Remote接口类

RMIConnectionImpl_Stub

RMIConnectionImpl_Stub stub = new RMIConnectionImpl_Stub(ref);

其他可用类

需满足:

  1. 可填入UnicastRef对象
  2. 实现Remote接口
  3. 是RemoteStub接口或获取不到原始类

4. 自定义Remote接口类

public static class CustomRemote implements Remote, Serializable {
    private RemoteRef ref;
    
    public CustomRemote(UnicastRef remoteref) {
        ref=remoteref;
    }
}

绕过序列化替换

通过反射修改ObjectOutputStream的enableReplace字段:

ReflectionHelper.setFieldValue(out, "enableReplace", false);

版本限制与绕过

8u231修复

  1. RegistryImpl_Skel报错时清除ref
  2. DGCImpl_Stub提前黑名单检查

An Trinh绕过方式

利用UnicastRemoteObject的readObject触发JRMP请求:

  1. 复写readObject执行UnicastRemoteObject逻辑
  2. 动态代理拦截createServerSocket调用
  3. RemoteObjectInvocationHandler发起JRMP连接

绕过原理:

  1. 不依赖ref填装,不受清除影响
  2. 不经过DGC层,避开黑名单

8u241修复

  1. String参数处限制反序列化
  2. RemoteObjectInvocationHandler增加方法验证

总结

关键知识点

  1. JRMP层无JEP290过滤器是绕过核心
  2. UnicastRef对象是发起JRMP连接的关键
  3. 多种封装方式各有适用场景
  4. An Trinh的绕过思路独特有效

版本影响

  • 8u121+: JEP290引入
  • 8u141+: bind地址验证
  • 8u231: 修复ref清除和黑名单
  • 8u241: 彻底修复已知绕过

参考工具

  1. Ysoserial的JRMPListener和JRMPClient模块
  2. Barmie工具的拦截器实现
  3. Ysomap框架的RMIConnect系列模块

防御建议

  1. 升级至最新JDK版本
  2. 限制RMI服务网络访问
  3. 使用安全管理器
  4. 监控异常JRMP连接

通过深入理解这些攻击技术,可以更好地防御Java RMI服务的安全风险。

针对RMI服务的九重攻击 - 下篇:绕过JEP290的JRMP利用方式 前言 本文重点讲述如何绕过JEP290限制,通过引入JRMP协议对RMI服务进行攻击。JEP290是Java的反序列化防御机制,默认只为RMI注册表和RMI分布式垃圾收集器(DGC)提供了内置过滤器,但最底层的JRMP协议没有过滤器,这成为绕过防御的关键。 利用JRMP反序列化绕过JEP290 JRMP服务端攻击JRMP客户端 攻击流程: 攻击者自实现恶意JRMP服务端 受害者作为JRMP客户端连接攻击者的服务端 恶意服务端返回包含payload的响应包 客户端反序列化响应时触发漏洞 关键点: JRMP客户端的反序列化点在 sun.rmi.transport.StreamRemoteCall#executeCall 服务端在 sun.rmi.transport.StreamRemoteCall#getResultStream 写入payload 绕过原理:JRMP层没有JEP290过滤器 与RMI服务端反序列化攻击RMI注册端结合 攻击流程: 攻击者发送payload让RMI注册端作为JRMP客户端连接恶意JRMP服务端 恶意JRMP服务端返回利用链 由于JRMP客户端反序列化无过滤器,payload被执行 关键点: 需要找到白名单内的Gadget让注册端发起JRMP连接 攻击链分为两部分:诱导连接和实际攻击 UnicastRef对象利用 调用栈分析 触发过程 readObject() 组装填入ref releaseInputStream() 统一处理ref 攻击方式实现 1. 绕过客户端-自实现协议 底层实现bind/lookup协议,直接发送UnicastRef对象,不依赖Remote接口。 2. 动态代理方式 自定义拦截器 RemoteObjectInvocationHandler Ysoserial的实现方式,利用JDK内置的RemoteObjectInvocationHandler。 3. 使用内置Remote接口类 RMIConnectionImpl_ Stub 其他可用类 需满足: 可填入UnicastRef对象 实现Remote接口 是RemoteStub接口或获取不到原始类 4. 自定义Remote接口类 绕过序列化替换 通过反射修改ObjectOutputStream的enableReplace字段: 版本限制与绕过 8u231修复 RegistryImpl_ Skel报错时清除ref DGCImpl_ Stub提前黑名单检查 An Trinh绕过方式 利用UnicastRemoteObject的readObject触发JRMP请求: 复写readObject执行UnicastRemoteObject逻辑 动态代理拦截createServerSocket调用 RemoteObjectInvocationHandler发起JRMP连接 绕过原理: 不依赖ref填装,不受清除影响 不经过DGC层,避开黑名单 8u241修复 String参数处限制反序列化 RemoteObjectInvocationHandler增加方法验证 总结 关键知识点 JRMP层无JEP290过滤器是绕过核心 UnicastRef对象是发起JRMP连接的关键 多种封装方式各有适用场景 An Trinh的绕过思路独特有效 版本影响 8u121+: JEP290引入 8u141+: bind地址验证 8u231: 修复ref清除和黑名单 8u241: 彻底修复已知绕过 参考工具 Ysoserial的JRMPListener和JRMPClient模块 Barmie工具的拦截器实现 Ysomap框架的RMIConnect系列模块 防御建议 升级至最新JDK版本 限制RMI服务网络访问 使用安全管理器 监控异常JRMP连接 通过深入理解这些攻击技术,可以更好地防御Java RMI服务的安全风险。