看完URLDNS之后再看CC链,,这尼玛简直就是一个天一个地。。前前后后大概看了三四天才能勉强说是理解了。然后又花了两天多来写这篇文章。。我感觉我尽力了,,不知道是不是因为我的思维方式还停留在php上,感觉java的文章真是有点难写。。。也许是我太想把每个流程调用都贴出来吧。。后面我会学习下大佬们都是怎么写java文的。。这篇文章就先将就着这样看吧,我尽力了555。。。
CommonsCollections。用于提供更好用的数据结构,方便开发快速进行代码开发。
由于这个链子不在 ysoserial 中,但这个系列的主题为 ysoserial,于是就把这个链子当作番外来命标题了。
环境
jdk < 8u71
maven
Poc:
1 | import org.apache.commons.collections.Transformer; |
从POC中学漏洞,我们可以先去查手册,看看对应POC中的类、函数大概干什么的,然后调试跟一跟,看看变量和参数是何时被赋值,进行了什么判断,执行了什么操作。这样在脑海中编织一个大概的轮廓,也许能够有助于我们理解一个漏洞。
由于这是一个反序列化洞,反序列化类型的漏洞的POC都包含两个大块:
- gadgets - 利用链
- readObject() - 需要能够跳到gadgets
gadgets
从Poc中抽出 gadgets 的相关代码:
1 | Transformer[] transformers = new Transformer[]{ |
这里用到的CC库的类有:Transformer、ConstantTransformer、InvokerTransformer、ChainedTransformer、TransformedMap。挨个分析,看Java就得坐的住。
Transformer
手册中定义如下:
public interface Transformer
用于将一个对象转换换成另一个对象。通常用于对象转换或从对象中解析数据
查看源码,只有一个方法 Object transform()
1 | package org.apache.commons.collections; |
ConstantTransformer
手册中定义如下:
public class ConstantTransformer
extends Object
implements Transformer, Serializable
Transformer的实现类,任何时候只返回一个相同的“常量”
查看源码,列出和本漏洞相关的方法和属性(初初看的时候可以都大概瞄一瞄,这里为了篇幅和演示的原因就只列出和本漏洞相关的方法和属性了)
1 | package org.apache.commons.collections.functors; |
InvokerTransformer
手册中定义如下:
public class InvokerTransformer
extends Object
implements Transformer, Serializable
Transformer的实现类,通过反射创建一个新对象
查看源码,列出和本漏洞相关的方法和属性
1 | package org.apache.commons.collections.functors; |
ChainedTransformer
手册中定义如下:
public class ChainedTransformer
extends Object
implements Transformer, Serializable
Transformer的实现类。将指定的 Transformer 像链子一样串起来。
输入的Object会按顺序进入指定的Transformer,得到输出后将结果再传入到第二个Transformer中,以此类推。
(有点像 Linux的管道操作,前一个输出作为后一个的输入)
查看源码,列出和本漏洞相关的方法和属性
1 | package org.apache.commons.collections.functors; |
TransformedMap
手册中定义如下:
public class TransformedMap
extends AbstractMapDecorator
implements Serializable
修饰Map,通过Transformer转换成对应类型。
TransformedMap的父类AbstractMapDecorator实现了Map接口。
TransformedMap重写了Map put()方法,TransformedMap父类AbstractInputCheckedMapDecorator重写了Map.MapEntry setValue()方法
查看源码,列出和本漏洞相关的方法和属性(不完全)
1 | package org.apache.commons.collections.map; |
至此gadgets大致分析完毕,读者可以动手调试一下,加深理解。
总结一下
- InvokerTransformer类的
transform()
方法中存在可控的反射操作,这个操作就是这个链子的漏洞点。 - Poc中gadgets的最后调用了
TransformedMap.decorate()
,该方法返回TransformedMap类实例。 TransformedMap类中 调用 指定类transform()
的操作只有checkSetValue()
,transformKey()
和transformValue()
方法中存在 - 根据 1. 和 2. 并结合Poc来进行推测:该Poc在试图调用到
TransformedMap.checkSetValue()
,TransformedMap.transformKey()
,TransformedMap.transformValue()
这三者之一来执行InvokerTransformer.transform()
,从而RCE。
分析gadgets
这一节我们仅分析 gadgets,目的是理解 gadgets 中如何触发到RCE的(这个漏洞的RCE点是 InvokerTransformer类的 transform()
方法)。所以我们先不找反序列化入口点,先手工调用 gadgets手工触发RCE,代码如下:
1 | Transformer[] transformers = new Transformer[]{ |
简单画了个流程图,当然自己动手跟代码理解起来效果更好

关于 InvokerTransformer 反射调用的细节
估计大家都看到了,Poc中为了执行 xcalc
命令,连续用了三次InvokerTransformer()
才得解。为什么需要调用那么多次呢?
首先,我们得先了解下,.class
;.class.getClass()
;实例.getClass()
之间都有什么区别。我们可以使用如下代码来测试:
1 | System.out.println(Runtime.class); //class java.lang.Runtime |
了解完毕后回到漏洞上来,这是一个反序列化漏洞,Payload需要被序列化。若我们直接给 ConstantTransformer类传入 Runtime.getRuntime()
,会由于 Runtime 没有实现 Serializable 接口而在序列化时报错。
所以为了能够让 InvokerTransformer.transform()
反射 Runtime类。Poc中采取了折中的办法,这里将Poc和InvokerTransformer抽象成下面的代码,方便理解:
1 | //获得一个Class对象 |
至此gadgets就分析完了,接下来开始寻找能够跳到 TransformedMap触发点 的反序列化入口点了。
readObject() - AnnotationInvocationHandler
gadgets这一节最后说,我们需要寻找调用入口点,找到能够调用 TransformedMap
类方法的点。
根据Poc发现其序列化了 sun.reflect.annotation.AnnotationInvocationHandler
类,并将 gadgets中最后一行经过 TransformedMap.decorate()
修饰的 Map outerMap
传入其构造方法中
1 | Class<?> aClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); |
这个漏洞是一个反序列化漏洞,所以我们应当去瞄瞄 AnnotationInvocationHandler类的 readObject()
方法。
1 | package sun.reflect.annotation; |
该readObject()
的流程,可分为两个并行分支:
- 调用到 TransformedMap 的代码
setValue()
,他是如何调用到 TransformedMap 的? - 进入
setValue()
之前有一判断if (var7 != null)
,如何确保一定能进入这个分支?
如何调用到TransformedMap
我们发现 readObject()
方法 中和 setValue()
相关的类成员是 Map类型的memberValues
。而Poc强制实例化 AnnotationInvocationHandler类 时就将 memberValues
设置为 TransformedMap类型的 outerMap
了。所以 AnnotationInvocationHandler类 中对 var4、var5 的操作我们去到 TransformedMap类去看源码和调试即可。
TransformedMap 关系图如下(红圆点是关键类):

经过调试和代码追踪可了解到触发流程:

如何进入if判断
进入 if判断 的代码中,有几个重要的变量。具体这些变量是如何被赋值的就不详细写出来了,自行跟进一下代码就能知道。我也不想把一堆篇幅写在跟着代码跳来跳去上。这里仅说思路和流程:
1 | //在Poc中,一些类成员已经被赋值: |
总结一下这一节,其实这个if判断的思想是:只有Map的key值和注解的方法名一致,才会调用 setValue()
。
总结
CC1 TransformedMap 链的核心思路如下:
InvokerTransformer.transform()
中存在可控的反射操作ChainedTransformer.transform()
对Transformer[]
遍历调用transform()
,为执行多段反射提供了可能执行到
ChainedTransformer.transform()
的入口为:AbstractInputCheckedMapDecorator.MapEntry.setValue()
反序列化入口点 为
sun.reflect.annotation.AnnotationInvocationHandler.readObject()
,该方法执行了 可控Map类型 类成员的setValue()
操作
最后提一下,jdk >= 8u71 这条链子就失效了。这是因为 AnnotationInvocationHandler类的 readObject()
代码有变化。原本的 setValue()
没了。唯一有点希望的 var7.put(var10, var11)
结果 var7 是个新new的LinkedHashMap,不可控。所以就冇得了。
1 | private void readObject(ObjectInputStream var1) throws IOException, ClassNotFoundException { |
Reference:
- Java Class Object
- 【代码审计】知识星球 - Java安全漫谈