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进行正向分析。