0x00、前言
重点上还是CC1、2两个链,后面都是变种或者加了一些新入口,依旧单独列出来方便整理,写一起太乱了。
0x01、Apache Commons Collections描述
引用CC1链分析中的描述
CC链即Commons Collections利用链,主要针对Commons Collections组件发现的利用链。
Apache Commons是Apache软件基金会的项目。Commons的目的是提供可重用的、开源的Java代码。
Apache Commons提供了很多工具类库,他们几乎不依赖其他第三方的类库,接口稳定,集成简单,可以大大提高编码效率和代码质量。
Apache Commons Collections 是对 java.util.Collection 的扩展。
Commons Collections包为Java标准的Collections API提供了相当好的补充。在此基础上对其常用的数据结构操作进行了很好的封装、抽象和补充。让我们在开发应用程序的过程中,既保证了性能,同时也能大大简化代码。
目前 Collections 包有两个 commons-collections 和commons-collections4,commons-collections 最新版本是3.2.2,3系列版本也是只有3.2.2是安全的,不支持泛型,目前官方已不在维护。collections4 目前最新版本是4.4,其中4.0是存在漏洞,最低要求 Java8 以上。相对于 collections 来说完全支持 Java8 的特性并且支持泛型,该版本无法兼容旧有版本,于是为了避免冲突改名为 collections4。推荐直接使用该版本。(注:两个版本可以共存,使用时需要注意)
0x02、环境准备
java版本:jdk8u66(版本无限制)
Commons Collections:3.2.1(漏洞版本在3.1-3.2.1)
maven项目pom.xml文件中添加依赖
<dependencies> |
在idea访问Commons Collections组件的文件时候点击上方的下载源代码就可以看到对应文件的.java文件了
0x03、分析
回顾
CC5链基于CC1-LazyMap链进行的变式延伸,回顾CC1-LazyMap链:
CC1(LazyMap链):
- 生成LazyMap对象,将
InvokerTransformer利用方法串起来的ChainedTransformer对象传入LazyMap构造方法,随后将LazyMap对象传入AnnotationInvocationHandler代理类。 - 通过动态代理,在生成二次代理对象时调用对象的
invoke方法,其中invoke方法中调用LazyMap.get()方法、get()方法调用ChainedTransformer.transform()方法最后实现InvokerTransformer.transform()执行命令;
分析
CC5在基于CC1-LazyMap链上引入了TiedMapEntry类和BadAttributeValueExpException类,其中TiedMapEntry类为commons.collections组件类,BadAttributeValueExpException类为jdk内置类。
import org.apache.commons.collections.keyvalue.TiedMapEntry; |
LazyMap
回到LazyMap链的调用,在get方法中,判断map对象中的Key是否存在传入的key,不存在就新建一个value值去put入map对象中,其中便调用了factory.transform(key)

其中factory为构造方法传入赋值的,因此构造参数传入ChainedTransformer类对象,变可执行ChainedTransformer.transform(key)方法执行代码

由于构造方法是保护限制,但存在decorate方法去返回实例化LazyMap对象并调用构造方法

利用点知道了,LazyMap链就是通过动态代理最后去调用到get方法。
TiedMapEntry
接下来就是找还有哪些类可以作为利用链调用的,就延伸出来TiedMapEntry类

其中map对象是由构造方法传入的Map类型的对象

接下来就找在哪调用了getValue()方法

其中在本类有3个方法进行了调用equals()、hashCode()、toString()
还需要一个序列化入口去调用其中的方法。
BadAttributeValueExpException
这时延伸出BadAttributeValueExpException类,BadAttributeValueExpException类为Exception类的子类,实现了序列化,并重写了readObject反序列化方法

相关注释:
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { |
因此为了执行TiedMapEntry.toString()方法,需要传入val字段值为TiedMapEntry对象,因为当前系统安全管理器默认不存在返回null,因此会执行valObj.toString()达到代码执行的目的
接下来只需要向val字段传入TiedMapEntry对象作为值即可,这里有BadAttributeValueExpException构造方法对val进行赋值

这里判断val是否为null,如果不为null,则直接执行val.toString()
由于该构造方法直接调用时,便会执行val.toString(),因此在编写poc生成序列化的时候,便会在本地触发val.toString()造成代码执行。

同时在反序列化时发现并不会触发漏洞,断点查看val值已经变成进程对象

因为在序列化过程前,代码执行到BadAttributeValueExpException调用构造方法触发代码后,val的值已经被运行完后赋值,返回getKey() + "=" + getValue()即上图中的值,此时的val值不再是TiedMapEntry对象,因此无法反序列化触发漏洞

可通过反射对val值进行赋值成TiedMapEntry对象,可规避这个问题,至此,利用链完成。
构造POC
首先CC1-LazyMap链的前半段没有变化(也就是ChainedTransformer触发点),可直接使用
Transformer[] transformers = new Transformer[]{ |
接着创建Map对象,通过调用decorate方法实例化LazyMap对象
//通过链转换器进行循环调用transformers数组 |
然后TiedMapEntry对象,为了通过toString()调用getValue()方法中的map.get()
//传入LazyMap对象,key随便设置例如下11 |
最后通过BadAttributeValueExpException类调用TiedMapEntry.toString()方法,并通过反射设置val值为TiedMapEntry对象
BadAttributeValueExpException BV=new BadAttributeValueExpException(111); |
最后序列化,反序列化触发代码执行。
完整POC:
public static void main(String[] args) throws Exception { |
实现demo:

0x04、总结
CC5在CC1-LazyMap链后半段通过BadAttributeValueExpException类调用TiedMapEntry.toString()方法达到执行效果,相对调用比较简单,但反向思维通过LazyMap跳到TiedMapEntry类去调用确实很难逆向去想到,调用类实在太多了,只有通过查看POC进行正向分析。