CC链
根据apache commons提供的一个组件apache commons collections所提供的类触发的一些列反序列化链子 简称CC链

CC1
Apache Commons Collections反序列化漏洞 (CC1)
添加依赖包 jdk版本为8u20

调用需要的类:
ConstantTransformer
InvokeTrasnformer
ChainedTransformer
TransformMap
AnnotationInvocationHandler
ConstantTransformer
transform方法将传入的类对象原封不动的返回

InvokeTrasnformer
该类的transform方法主要实现了反序列化执行rce
构造实例的时候传入方法和参数类型与参数
当调用transform方法的时候 会先在传入的对象的Class类中查找该方法 并执行传入的方法


通过InvokeTrasnformer rce
1 | import org.apache.commons.collections.functors.InvokerTransformer; |
ChainedTransformer
ChainedTransformer类的transform方法会链式调用transformers数组中的每个Transformer的transform方法

通过ChainedTransformer rce
1 | import org.apache.commons.collections.Transformer; |
TransformerMap
我们需要TransformedMap类将我们上面构造好的链子打包在这个类中 等待触发
由于该类的构造器是受保护的 我们可以直接通过decorate方法创建一个实例


我们现在需要寻找的是一个可以触发transform方法的方法
只要触发了transform方法 就可以导致rce 执行我们已经构造好的恶意链中的代码
TransformMap类中的checkSetValue/put/putall都会触发transform方法
调用SetValue()方法的时候 最后还是会调用我们的(受保护的方法)checkSetValue()方法



核心类AnnotationInvocationHandler
全限定包名sun.reflect.annotation.AnnotationInvocationHandler
由于这个是一个jdk中的内部包 所有无法直接创建实例 只能通过放射的方法创建实例
该类允许序列化 并且当反序列化的时候 会触发getValue() 因此也是我们的触发类

调用代码
1 | import org.apache.commons.collections.Transformer; |
调用链
1 | ObjectInputStream.readObject() |
简单调试

在AnnotationInvocationHandler的readobject方法上打个断点

注意var3的key为value
这个键值对来自var10 = AnnotationType.getInstance(this.type) 之后会触发var10.memberTypes() 获得成员类型
进入循环 触发之前传入AnnotationInvocationHandler的成员方法 this.memberValues.entrySet()
this.memberValues 实际就是我们的TransformedMap对象

TransformedMap没有重写entrySet实际触发父类的entrySet()
进而触发类成员的entrySet() 也就是我们之前传入的HashMap


返回键值对给var5 类型为AbstractInputCheckedMapDecorator$MapEntry 因为TransformMap没有重写entrySet方法 所以实际返回AbstractInputCheckedMapDecorator中的内部类AbstractInputCheckedMapDecorator$MapEntry
下面var3会尝试获取var6 实际就是key值value
这里肯定是成功取出 然后进入后面的循环
所以这里传入到TransformMap里面的HashMap 一定要有值 并且key还得是value 这样才能进入下面的循环

我们想要的就是触发 AbstractInputCheckedMapDecorator$MapEntry的setValue()

在之前触发entrySet方法的时候 内部类里面的parent已经被赋值了

parent实际为我们的TransformMap对象
进而触发他的checkSetValue方法

然后触发transform方法

valueTransformer实际就是传入的Transform对象


后续就是循环触发Transform对象数组里面的各种类型的Transform 最后命令执行

注意点

传入到TransformMap里面的Hashmap的键值对的key一定要为value
上面的各种Transform最后实际调用的时候 为什么要这么写 最好自己调试一下 看看具体情况
sun.reflect.annotation.AnnotationInvocationHandler 这个类反序列化的时候 触发TransformMap的setEntry方法 进而触发父类的AbstractInputCheckedMapDecorator的内部类方法MapEntry 最后触发setValue 感觉还是比较精妙的 也不好看出来 很细节
CC3
cc3将InvokeTrasnformer这个类换成InstantiateTransformer触发
其他地方也有点改动
核心类
- TemplatesImpl(sink)
- ChainedTransformer
- ConstantTransformer
- InstantiateTransformer
- TrAXFilter
- TransformedMap
- AnnotationInvocationHandler (触发类)
TemplatesImpl

TemplatesImpl这个类属于jdk的原生类
常常用来当做最后的sink类 实际上是加载任意字节码并实例化

关注方法newTransformer和getOutputProperties方法
getOutputProperties方法实际会触发newTransformer方法 所以关注newTransformer方法就可以了
跳转到getTransletInstance()

defineTransletClasses方法会加载字节码
AbstractTranslet translet = (AbstractTranslet) _class[_transletIndex].newInstance();
这一串又会实例化_class数组中的类
跳转到defineTransletClasses方法

判断_bytecodes属性是否为null
下面会获得类加载器 然后回加载_bytecodes数组里面的字节码 并把这个类传入_class数组中 后续就是实例化然后触发rce
_class[i] = loader.defineClass(_bytecodes[i])
注意加载完类之后 会判断是否继承了父类

所以加载的字节码 必须先继承AbstractTranslet
现在查看属性是否可控

_bytecodes是个二维数组 还是个私有属性
可以直接反射修改值

有好几个构造器
这些条件就足够构造一个恶意的TemplatesImpl对象了
我们现在已经知道了**getTransletInstance()**最后会任意加载字节码并实例化
getOutputProperties()和newTransformer()都会调用getTransletInstance()
实际上都是尝试去触发**getOutputProperties()和newTransformer()**这2个方法
1 | String path = "D:\\123\\Calc.class"; |
创建一个TemplesImpl恶意类
现在需要去触发getOutputProperties或者newTransformer方法
根据cc1可以直接使用InvokeTrasnformer方法调用
但是ysoserial工具里面使用了InstantiateTransformer和TrAXFilter这2个类
来看看这2个类有啥用
InstantiateTransformer和TrAXFilter

InstantiateTransformer这个类的transform方法会获取intpu的构造器 类型为iParamTypes 并且实例化

TrAXFilter这个类的构造器会触发一个传入的Templates对象的newTransformer方法 很自然的想到TemplatesImpl对象的newTransformer方法
InstantiateTransformer和TrAXFilter这2个类结合起来可以实现触发TrAXFilter的有参构造器 然后触发 TemplatesImpl对象的newTransformer方法 最后成功恶意加载字节码实现rce
尝试写poc
1 | import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; |

现在的Transform数组是这样的 后续调试会明白效果的
和cc1相比变化不大直接调试
简单调试

这次在ChainedTransformer这里的transform方法打断点

首先根据ConstantTransformer的transform的特性会直接返回TrAXFilter类
进入InstantiateTransformer的transform

TrAXFilter类作为input变量的值获得有参构造器
后续就是创建TrAXFilter实例对象 参数为TemplatesImpl对象

进入TrAXFilter的有参构造器 触发TemplatesImpl对象的newTransformer()
这里已经结束了 后续就是正常加载字节码触发rce

总结一下
InstantiateTransformer和TrAXFilter的组合实现了 TrAXFilter实例化对象 后续触发TemplatesImpl的newTransformer方法
限制
- CC包 <= 3.2.1
- jdk < 8u71
- TemplatesImpl 模板实现类
- ChainedTransformer 链式转换器
- ConstantTransformer 常量转换器
- InvokeTrasnformer 调用转换器
- InstantiateTransformer 实例化转换器
- TrAXFilter TrAX过滤器
- TransformedMap 转换映射
- AnnotationInvocationHandler 注解调用处理器
学java顺便学一下英语()
CC6
最有常用的链子
触发类使用HashMap触发
核心类
- HashMap(触发类)
- TiedMapEntry 关联映射条目
- LazyMap 延迟映射
- ChainedTransformer
- ConstantTransformer
- InvokeTrasnformer(sink类)
我们先来看看LazyMap和TriedMap
LazyMap和TiedMapEntry
这2个类主要是为了替换TransformedMap类

LazyMap的get方法会触发factory对象的transform方法

factory对象来自于上面的decorate方法 可以让我们传Transformer对象
那直接传ChainedTransform对象就完事了
现在需要一个类可以触发LazyMap的get方法

TiedMapEntry的getValue方法刚好可以触发map对象的get方法
那么只要map对象为LazyMap就可以了
上面的有参构造器就可以实现
看看什么方法可以触发getValue方法

equals hashCode toString方法均可触发 这些也都是Map接口有的方法
这个hashCode应该不难想到HashMap在反序列化的时候可以触发 URLDNS链就是这么打的

跟进hash方法

触发key的hashcode
POC
1 | import org.apache.commons.collections.Transformer; |
注意这些地方 都是细节点

调试过程就会明白
调试一下
CC6的调试一定要先关掉idea中 调试器的2个参数

断点打在hashmap的put方法上面

跟进put方法



put方法也会触发hash方法 进而触发key.hashCode 导致提前触发TiedMap的getValue方法 进而提早的就会弹计算器 所以这里为了避免提早触发

ChainedTransformer里面的Transform数组不是恶意的Transform数组
后续反射修改了
但是下面为什么要删除LazyMap的这个key呢
继续跟进getValue 触发LazyMap的get方法


这里的HashMap按理来说应该是空的才对 但是这里没关调试效果

调试默认开启这2个设置 在显示变量的值的时候会自动触发toString 和其他某些方法
由于触发了toString 导致这里的HashMap莫名其妙多了一个键值对 后续调试是不会成功的
因为这个键值对删不掉()
后续这里的判断就会跳过 导致不执行transform方法
现在关掉 继续调试到这里

现在就正常了 正常来说这里的hashmap就是空的
由于找不到key 所以这里进入这个判断里面去了
factory不是我们的恶意Transformer数组所以不会触发rce
但是后面会往hashmap里面放东西 所以我们后面需要删掉里面的键值对


这下就正常了
后面反射调用将恶意Transformer数组放进去就成功构造好了恶意对象
后续就是一路畅通



后面就正常rce了

总结一下
这个链子是CC里面通用链 首选就打他
限制
- CC包 <= 3.2.1 / CC包 = 4.0
- jdk无限制
CC包为4.0的时候需要修改一下LazyMap的构造 因为4.0版本没有decorate方法了 直接反射调用构造器就可以
CC5
将AnnotationInvocationHandler 替换为BadAttributeValueExpException
核心类
- TemplatesImpl(sink)
- ChainedTransformer
- ConstantTransformer
- InstantiateTransformer
- TrAXFilter
- TiedMapEntry
- LazyMap
- BadAttributeValueExpException(触发类)
BadAttributeValueExpException

BadAttributeValueExpException在反序列化的时候会触发val对象的toString方法
可以使用TiedMapEntry和LazpMap来触发

val成员可以直接反射赋值
基本上就是替换了触发类而已 和CC3没啥区别
POC
1 | import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; |

注意这里
调试一下
在BadAttributeValueExpException实例化的地方打个断点


BadAttributeValueExpException的构造器会触发val的toString方法
如果这这里就把TiedMapEntry放进去会提前触发rce
所以这里得在后面再把val成员改成TiedMapEntry

后续调用到TiedMapEntry的toString方法进而触发getKey方法 后面就和cc3一样了
限制
- CC包 <= 3.2.1
- jdk < 8u76
CC2与CC4
这2个没什么区别 就是一个使用了InvokeTrasnformer 另一个InstantiateTransformer
CC2和CC4都是在 CC包version = 4.0的时候打的
核心类
- PriorityQueue(触发类)
- TransformingComparator
- InstantiateTransformer/InvokeTrasnformer
- TemplatesImpl(sink)
PriorityQueue和TransformingComparator
来看看这2个类是干什么的
先来看看entryset类

compare方法会触发 实例对象成员transformer的transform方法 所以可以尝试将ChainedTransfomer传入TransformingComparator里面 上面的构造器就可以实现
现在需要一个类来触发TransformingComparator的compare方法
来看看PriorityQueue类

他的反序列化方法会触发heapify方法 跟进一下



多次调用来到siftDownUsingComparator方法
在下面就会触发comparator.compare()
现在只需要成员comparator 为我们的TransformingComparator对象就可以了

使用这个有参构造器就可以传入我们的TransformingComparator对象了
POC
1 | import com.deseriazler; |

注意这些地方
调试一下
在priorityQueue.add()这里打个断点


跟进offer方法

由于第一次add的时候 PriorityQueue的长度为0 所以后面不会进入siftUp方法

在第二次add的时候就会进入siftUp方法
由于会提前触发rce 所以这里不能直接传入我们的恶意类 得后续反射修改

至于为什么要在priorityQueue里面填入成员

是因为后面会间接的判断长度大小 如果是空的话是没法进入 siftUpUsingComparator方法

又为什么要add两次
是因为headpify方法会对长度进行右移操作 如果为1的话后续反序列化没法进入siftDown方法中
来正常简单过一下流程
在heapify方法这里打个断点


由于size为2 右移结果大于0可以进入循环

成功进入compare方法

这里的TemplatesImpl对象来自于 PriorityQueue.add添加的对象

触发TemplatesImpl的newTransformer 后面就成功加载恶意字节码

总结一下
引入的PriorityQueue和TransformingComparator 就为了替换ChainedTransform
CC7
CC6的变种 将HashMap替换为HashTable了
核心类
- HashTable(触发类)
- TiedMApEntry
- LazyMap
- ChainedTransformer
- ConstantTransformer
- InvokerTransformer(sink类)
HashTable

在反序列化的时候会触发 reconstitutionPut方法

reconstitutionPut方法里面会触发key的hashcode
只要让key为TiedMapEntry就可以了
POC
1 | import org.apache.commons.collections.Transformer; |
总结一下
Transformer的实现类是核心
ConstantTransformer InvokeTrasnformer ChainedTransformer TransformMap
调试过程最好自己动手调试一下
触发类特性
AnnotationInvocationHandler
readObject方法 会触发成员变量的entrySet方法 获得到的Map.Entry后续又会触发setvalue方法
这个触发类算是cc专属了应该

invoke方法 里面会触发成员变量的get()方法
AnnotationInvocationHandler类引用了InvocationHandler 可以作为动态代理里面需要的InvocationHandler对象
可以用来动态代理对象 触发entrySet方法 进而触发invoke方法 也不怎么好用
总的来说和Map实现类有关
jdk 8u71被削弱了

PriorityQueue
readObject方法会触发heapify方法


后面间接的触发comparator.compare方法 和Comparator实现类绑定
在CC包里面就是TransformingComparator
BadAttributeValueExpException
readObject方法会触发成员对象的toString方法
所有类都有这个方法 再别的链子里比较容易用到
jdk 8u75被削弱了

HashMap
readObject方法会触发key的hashcode方法


HashTable
也是反序列化的时候触发hashcode方法


说些什么吧!