[备份]CC7链HashTable触发点深入研究

起因

CC(CommonsCollections)链系列是Java安全必经之路,复习到CC7的lazyMap2.remove("yy");代码,网上文章解释的不是很清楚,不明白为什么要这样做,于是打算深入做一个分析

回顾

贴出网上广泛流传的CC第7链,笔者将带大家做个简单的回顾

链式调用中使用到InvokerTransformertransform方法,反射调用

LazyMap中的触发点,如果当前LazyMap中不包含传入的key才会顺利调用transform触发漏洞

HashTable被反序列化后的触发过程如下,遍历HashTable已有元素调用reconstitutionPut方法

(注意这里有细节,将在后文中重点关注)

跟入equals到达AbstractMap.equals,看到m.get(key)方法,其实是上文中的LazyMap.get,调用了transform方法,最终构造出整条链,这也是网上大部分文章所写的过程

深入分析

从Payload入手分析,将空的chainedTransformer传入LazyMap中,并设置keyyyzZ的元素

分析LazyMap源码可以看出并没有重写put,所以这里只是简单的普遍的map.put操作

继续分析,往新建的HashTable中放入上文两个LazyMap

问题一

为什么要放入两个LazyMap

首先来看HashTable.put,这里和reconstitutionPut处的代码类似,都包含了entry.key.equals(key)代码。其中key是传入的LazyMaptab是全局的一个Entry,根据hashcode算出一个index,只有entry中有元素才会进入for循环,从而进一步触发

所以可以看出,必须要两个或以上元素才能进入entry.key.equals(key)方法。类似地,反序列化的触发点reconstitutionPut处也是这样的逻辑,需要保证必须有两个或以上元素

进而可以得出的结论,能走到LazyMap.get方法的只有lazyMap2这一个对象

开头部分代码调试后,可以发现会执行两次LazyMap.get方法。第一次是制造反序列化对象的过程,也就是hashtable.put(lazyMap2, "test");会调用;第二次是模拟被反序列化后reconstitutionPut的调用。接下来我们针对这两次调用做深入分析

第一次调用:

注意到第一次传入的是空的一个Transformer数组

因此在transform的时候会原样返回,如果传入yy就会返回yy

结合代码分析,当lazyMap2put后,entry.key.equals(key)entry.key正是lazyMap1AbstractMap.equals方法中有部分被忽视的代码。其中i是全局变量,根据继承关系,其中正是lazyMap1保存的yy:1,所以取到的keyyy,最终在lazyMap2.get传入的是yy

进入lazyMap2,本身只有zZ:1这一个元素,不包含yy,所以成功执行transform。而上文分析传入的参数是yy所以经过transform一些系列的链式调用后返回的还是yy,将yy:yy设置到lazyMap2中,所以lazyMap2包含了:zZ:1yy:yy(链式调用原样返回是因为传入一个空的一个Transformer数组)

后文反射设置chainedTransformer为Payload

然后将lazyMap2的yy:yy移除

第二次调用:

这时候HashTable被反序列化,调用readObject方法,进入reconstitutionPut,重新看之前的代码。其中table参数是HashTable所包含的元素,由于刚被反序列化,所以不存在元素

进入reconstitutionPut的调用点,遍历获取的第一个key应该是lazyMap1->yy:1。由于tab是空,导致get操作的循环无法进入,跳到后续代码中,把lazyMap1->yy:1加入到了全局变量table

第二次循环进入reconstitutionPut,由于全局变量中已有值,所以可以调用到e.key.equals(key)方法

问题二

删除LazyMap2中key为yy的元素的根本原因是什么

观察到reconstitutionPut的代码,想要顺利执行,需要确保两个lazyMaphashcode一致,进而index计算结果一致才可以。Java中hashcode的计算方式比较复杂,这里简单理解为:如果lazymap1lazymap2包含相同数量的元素,并且每个元素的keyvalue都完全一致,那么计算得出的hashcode就相等

然而lazyMap1->yy:1lazyMap2->zZ:1hashcode为什么会相等呢?因为这是一处哈希碰撞,恰好而已。假设改成lazyMap2->zZ:2lazyMap2->zZZZZ:1都会导致无法运行

问题三

Payload中的yy和zZ能否改成其他字符串

参考问题二,要保证hashcode一致,理论上会有很多选择,实际上很难找出合适的

笔者给出一个可用的Payload,字符串AaAaAaBBAaBBhashcode相同,测试通过

成功触发 img

参考链接 https://xz.aliyun.com/t/9409#toc-7 https://cloud.tencent.com/developer/article/1809858