针对RMI服务的九重攻击(上)技术分析文档
前言
本文详细分析针对Java RMI服务的多种攻击方式,重点介绍上篇内容:针对已知RMI接口的三种攻击方式与针对RMI层(RMI注册端、RMI服务端)/DGC层的攻击方法。这些攻击方式主要利用RMI协议的反序列化漏洞,通过不同层面的交互实现远程代码执行。
RMI基础知识回顾
RMI(Remote Method Invocation)是Java的远程方法调用机制,主要由以下组件构成:
- RMI注册端(Registry):运行在1099端口,负责管理RMI服务
- RMI服务端(Server):提供远程服务实现
- RMI客户端(Client):调用远程服务
RMI通信过程中存在多处反序列化点,这些点是攻击的主要入口。
探测利用开放的RMI服务
BaRMIe工具分析
BaRMIe工具提供两种模式:
- enum枚举模式:探测RMI服务信息
- attack攻击模式:尝试攻击发现的RMI服务
枚举流程:
- 通过
LocateRegistry.getRegistry获取目标RMI注册端 - 使用
reg.list()获取注册端上所有服务端Endpoint对象 - 尝试解绑不存在的服务名,判断是否可操控注册端
- 通过代理服务器获取服务端详细信息(类名等)
攻击模块分类:
- RMI客户端探测利用RMI服务(如Axiom组件的文件操作)
- RMI客户端反序列化攻击RMI服务端(利用Object类型参数)
- RMI服务端攻击RMI注册端(Bind类攻击)
RMI客户端反序列化攻击RMI服务端
利用Object类型参数
攻击条件:
- 服务端提供RMI服务,且方法参数为Object类型
- 服务端存在可利用的反序列化链
漏洞触发点:
服务端在sun.rmi.server.UnicastServerRef#dispatch中处理请求时:
- 根据Method hash验证方法是否存在
- 遍历入参类型,从输入流反序列化入参
- 当参数非基础数据类型时,执行
readObject()触发反序列化
绕过Object类型参数限制
研究发现非基础数据类型的参数均可利用,包括:
- String
- Integer(注意:Integer不是基础类型,int才是)
- 数组类型等
实现方式:
- 修改RMI底层源码
- 运行时添加调试器hook客户端
- 使用Javassist工具更改字节码
- 使用代理替换序列化对象中的参数
RMI服务端反序列化攻击RMI注册端
攻击接口分析
RMI注册端存在4个反序列化触发点:
bind(String, Remote)lookup(String)rebind(String, Remote)unbind(String)
关键发现:
- 不仅Remote类型参数可利用,String参数位置同样可放置payload
- 攻击不限于bind接口,四个接口均可利用
工具实现分析
BaRMIe - Bind攻击:
- 建立RMI代理服务器
- 重构数据包,将String参数替换为payload
- 第二个Remote参数设为null
Ysoserial-RMIRegistryExploit:
- 使用动态代理将payload封装成Remote接口对象
- 通过正常bind接口发送
- 动态代理仅用于接口转换,不涉及触发逻辑
RMIattack - 回显实现:
- 修改CC链底层调用为write()方法
- 写入临时class文件
- 通过异常机制回显命令执行结果
RMI DGC层反序列化
DGC机制简介
DGC(Distributed Garbage Collection)用于维护远程引用,主要方法:
dirty():注册远程引用clean():清除远程引用
攻击原理
漏洞触发点:
在sun.rmi.transport.DGCImpl_Skel#dispatch中:
- dirty和clean方法都会反序列化客户端提供的ObjID[]参数
- 通过构造恶意序列化数据触发漏洞
Ysoserial JRMP-Client实现:
- 建立socket连接
- 手动构造DGC协议数据包:
- 魔术字符:0x4a524d49
- 协议版本:2
- 协议类型:0x4c
- 指令:0x50(RMI call)
- 写入DGC固定格式数据
- 插入payload对象
JEP290修复与绕过
JEP290限制
在JDK 6u141/7u131/8u121后引入:
-
RMI注册表过滤器:
- 深度限制:20层
- 数组长度限制:10000
- 白名单:String、Number、Remote、Proxy等
-
DGC过滤器:
- 类似注册表过滤器
- 白名单:ObjID、UID、VMID、Lease
受影响攻击
以下攻击被JEP290阻止:
- RMI服务端攻击注册端(bind/rebind/unbind)
- DGC层攻击
- lookup攻击注册端
例外:
RMI客户端利用参数反序列化攻击服务端不受限,因为:
- 参数反序列化是RMI正常功能
- 无法使用白名单限制,否则会破坏正常RMI通信
版本差异
-
8u141后新增注册端对服务端的地址验证:
- 先验证再反序列化
- 使服务端bind攻击失效
- lookup攻击(客户端发起)不受影响
-
但JEP290(8u121)已阻止所有注册端攻击,地址验证改动影响有限
小结(上篇)
-
探测利用开放的RMI服务:
- 基于已知组件特征识别
- 需要预先知道接口定义
-
RMI客户端反序列化攻击RMI服务端:
- 不限于Object参数,所有非基础类型参数均可利用
- 通过参数替换技术实现
-
RMI服务端反序列化攻击RMI注册端:
- 四个接口均可利用
- String和Remote参数位置均可放置payload
- 动态代理仅用于接口适配
-
DGC层攻击:
- 攻击面更广(所有RMI服务都开启DGC)
- 需要手动构造DGC协议数据包
-
JEP290影响:
- 阻止了注册端和DGC层攻击
- 参数反序列化攻击不受限
下篇将重点介绍绕过JEP290的JRMP利用方式及其他高级攻击技术。