CC4

环境:

  • Commons-Collections 4.0

https://mvnrepository.com/artifact/org.apache.commons/commons-collections4/4.0

  • JDK 8u65

分析:

以 ChainedTransformer.transform 为出发点,往回找

找到 TransformingComparator.compare 中调用了 transform 方法

再找到 PriorityQueue 类中:

最后找到的链也就是:

PriorityQueue.

readObject

heapify

siftDown

siftDownUsingComparator

compare

TransformingComparator.compare.transform****

ChainedTransformer.transform

PriorityQueue.readObject.heapify.siftDown.siftDownUsingComparator.compare

->

TransformingComparator.compare.transform

->

ChainedTransformer.transform

编写 POC:

先将 CC3 的代码执行部分拿来:

对照源码进行构造:

写好之后,发现没有反应

调试来到此处:

看到 size:0 ,不会进入 siftDown.

for (int i = (size >>> 1) - 1; i >= 0; i--)

1. size >>> 1

  • >>> 是 无符号右移运算符。
  • size >>> 1 等价于 size / 2(向下取整),但不同于 /2 的地方在于:
    • /2 是普通的除法运算;
    • >>> 1 是位运算,执行效率更高(尤其是在底层算法里)。

所以 size >>> 1 就是 数组长度的一半。

2. (size >>> 1) - 1

  • 先取数组长度的一半,再减去 1。
  • 结果是 从数组的最后一个非叶子节点开始的位置。

例如在堆排序或优先队列的实现中:

  • 叶子节点的下标范围是 size/2 ~ size-1。
  • 非叶子节点的最后一个位置就是 (size/2)-1。

3. for (int i = ...; i >= 0; i--)

  • i 从 (size >>> 1) - 1 开始,递减到 0。
  • 也就是说:循环会从最后一个非叶子节点开始,往前遍历所有非叶子节点,直到根节点。

这是一个建堆(heapify)的实现。作用是:从最后一个非叶子节点开始,依次向前调整,直到把整个数组调整成一个堆结构。

所以要保证 size 为 2 才能进入 for 循环执行代码

于是添加:

此时执行代码后报错了

跟进 add 函数:

发现 add 函数也会调用 compare,而当 add 调用 compare 也就会调用 transform

而我们知道 _tfactory 在反序列化的时候才会被赋值 【CC3 中分析 利用 TemplatesImpl 加载字节码 时提及】

所以,先给 transformers/chainedTransformer 传一个没用的东西,让 add 执行时不会调用

compare.transform ,在反序列化时再赋给正常值。

这样就可以正常执行了

完整的POC:

CC2

分析:

CC2 和 CC4 并无太大区别,CC2 的前半部分同样为:PriorityQueue.readObject.heapify.siftDown.siftDownUsingComparator.compare

-> TransformingComparator.compare.transform

-> ChainedTransformer.transform

之后,CC4:

InstantiateTransformer.transform

-> TrAXFilter.TrAXFilter

-> TemplatesImpl.newTransforemer

-> TransletClassLoader.newInstance

CC2:

InvokerTransformer.transform

-> TemplatesImpl.newTransforemer

-> TransletClassLoader.newInstance

完整POC

CC5

分析:

CC5 的起点变成了 BadAttributeValueExpException,调用的是 TiedMapEntry.toString ,之后便是 LazyMap.get(CC1-LazyMap/CC6)。

这里从正向走比较方便,如果从 LazyMap.get 出发,会用很多调用 get 方法的类,不好找。

来到 BadAttributeValueExpException 类,跟进 toString() 方法

这里的 toString() 方法要找到调用它的类,然后就找到了 TiedMapEntry 类中调用的 toString

getValue 调用了 map.get,后续便是 LazyMap 调用 get。

POC:

CC7

分析:

CC7 的入口点变成了: HashTable.readObject

HashTable 中的 readObject 调用了 reconstitutionPut

reconstitutionPut中调用了 equals ,

因为 LazyMap 不存在 equals 方法,然后找到它的父类 AbstractMapDecorator 调用 equals

找到 AbstractMap.equals 中调用了 get 方法

构造POC:

AbstractMap.equals

Hashtable.reconstitutionPut

注意:首先满足 value != null,之后满足俩个元素的 hash 值相同,然后在判断 key 是否重复时触发 equals 方法

HashTable.readObject

构造:

ysoserial:

结合上述代码的分析,ysoserial 这里要写 俩个 put 的作用便是:

在 Hashtable 放入俩个 lazyMap ,制造“冲突”,迫使他们进入

同时要让俩个元素的哈希值相同【(e.hash == hash)】,然后就会触发 key.equals() 的比较。
而 equals() 内部会调用到 LazyMap.get(),最终触发 transformerChain 执行恶意代码

hash相同的值:

yy与zZ


Ea与FB

完整POC:

总结: