针对RMI服务的九重攻击 - 下
字数 1654 2025-08-25 22:59:02
针对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对象利用
调用栈分析
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)
触发过程
readObject()组装填入refreleaseInputStream()统一处理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);
其他可用类
需满足:
- 可填入UnicastRef对象
- 实现Remote接口
- 是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修复
- 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服务的安全风险。