摘要:在本文中将先介紹java反序列化漏洞的原理,然後在此基礎上介紹安全工具如何檢測、掃描此類漏洞。
本文分享自華為雲社群《java反序列化漏洞及其檢測》,作者: alpha1e0。
java反序列化是近些年安全業界研究的重點領域之一,在Apache Commons Collections 、JBoss 、WebLogic 等常見容器、庫中均發現有該類漏洞,而且該類型漏洞容易利用,造成的破壞很大,是以影響廣泛。
在本文中将先介紹java反序列化漏洞的原理,然後在此基礎上介紹安全工具如何檢測、掃描此類漏洞。
Java 序列化是指把 Java 對象轉換為位元組序列的過程,序列化後的位元組資料可以儲存在檔案、資料庫中;而Java 反序列化是指把位元組序列恢複為 Java 對象的過程。如下圖所示:

序列化和反序列化通過ObjectInputStream.readObject()和ObjectOutputStream.writeObject()方法實作。
在java中任何類如果想要序列化必須實作java.io.Serializable接口,例如:
java.io.Serializable其實是一個空接口,在java中該接口的唯一作用是對一個類做一個 标記 讓jre确定這個類是可以序列化的。
同時java中支援在類中定義如下函數:
這兩個函數不是java.io.Serializable的接口函數,而是約定的函數,如果一個類實作了這兩個函數,那麼在序列化和反序列化的時候ObjectInputStream.readObject()和ObjectOutputStream.writeObject()會主動調用這兩個函數。這也是反序列化産生的根本原因
例如:
該類在反序列化的時候會執行指令,我們構造一個序列化的對象,name為惡意指令,那麼在反序列化的時候就會執行惡意指令。
在反序列化的過程中,攻擊者僅能夠控制“資料”,無法控制如何執行,是以必須借助被攻擊應用中的具體場景來實作攻擊目的,例如上例中存在一個執行指令的可以序列化的類(Hello),利用該類的readObject函數中的指令執行場景來實作攻擊
在這裡我們構造一個有漏洞的靶場進行漏洞複現測試:使用spring-boot編寫一個可以接收http資料并反序列化的應用程式。
使用 https://start.spring.io/ 生成一個spring-boot應用,選擇Maven Project、java8
下載下傳到本地,導入IDE,修改 pom.xml 加入 Apache Commons Collections 3.1 依賴(該版本存在反序列化漏洞)
修改 DemoApplication.java 為如下代碼
此時我們就完成了一個有 Apache Commons Collections 漏洞的驗證靶場,啟動該靶場應用
我們使用ysoserial 生成攻擊payload:
然後使用httpie 發送攻擊payload(poc)
這時候就可以看到poc中的指令執行了
在1.2 的示例中我們使用了 ysoserial 的 CommonsCollections5 這個payload,本節我們對此poc進行分析
可以最終反序列化的對象為 javax.management.BadAttributeValueExpException ,在該類提供了 readObject 方法,在其中有問題的地方為
這裡的 valObj 為 TiedMapEntry(lazyMap, “foo”) ,該類的toString方法
其中 this.getValue 為
而 this.map 為 lazyMap = LazyMap.decorate(innerMap, transformerChain),在 lazyMap 中
在其中看到,沒有找到key的時候,調用了 this.factory.transform(key)
而this.factory為我們構造的包含payload的執行鍊 transformerChain 該transformer會最終通過反射執行指令。
在1中的原理介紹中,我們可以看到,反序列化漏洞需要依賴執行鍊來完成攻擊payload執行。由于反序列化漏洞的特性,在檢測的時候漏洞掃描工具一般聚焦已知漏洞的檢測,而未知漏洞的檢測,安全工具能力非常有限,一般需要專業人員通過安全審計、代碼審計等方式發現。
java反序列化漏洞依賴于兩個因素:
應用是否有反序列化接口
應用中是否包含有漏洞的元件
是以對應的漏洞掃描工具也需要根據這兩個因素進行檢測。
白盒代碼審計工具,可通過在調用鍊中查找是否有發序列化的操作:
調用鍊的入口不同架構是不同的,例如在1.2例子中調用鍊的入口為spring-boot的controller。
調用鍊中一旦發現有發序列化操作ObjectInputStream.readObject()則該接口存在序列化操作
但僅僅依靠以上資訊不足以判斷是否存在漏洞,還需要判斷代碼中是否有存在*執行鍊**的三方依賴。在java中,一般通過分析 pox.xml build.gradle 檔案來分析是否包含有漏洞的元件。
web漏洞掃描器檢測原理和白盒工具不一樣。
首先漏洞掃描器要解決的是識别出反序列化的請求,在這裡需要注意的是web漏洞掃描是無法通過爬蟲方式直接發現反序列化接口的,是以往往需要配合其他web漏洞掃描器的元件(例如代理元件)來識别反序列化接口,如下圖所示
如今web漏洞掃描器都提供了代理元件來發現應用的http請求,爬蟲元件可通過前台頁面觸發請求進入代理元件;但在API場景下,還是需要測試人員進行API調用該操作才能夠産生http請求資料。
在截獲到http請求資料後,代理元件可以通過兩種方式判斷一個請求是否是序列化請求:
通過http請求的Content-Type,具體來說ContentType: application/x-java-serialized-object 是序列化請求的請求頭
檢查請求資料的開頭是否是 0xaced,有時候序列化請求不存在正确的content-type,此時需要根據資料來判斷是否是序列化請求
在确定一個接口是序列化接口的時候會漏洞掃描器會發送探測payload判斷接口是否有反序列化漏洞,這裡的攻擊payload類似于1.2節中使用的ysoserial 工具,由于絕大多數情況下不可能看到回顯(http傳回資料沒有攻擊執行結果),是以隻能進行盲注,即發送 sleep 10 這樣的指令,根據響應時間判斷是否有漏洞。
文末福利:華為雲漏洞掃描服務VSS 基礎版限時免費體驗>>>
點選關注,第一時間了解華為雲新鮮技術~