作者:隱形人真忙

0x00 前言

先說一下JEP290這個增強建議本身其實在2016年就提出來了,本身是針對JAVA 9的一個新特性,但是隨后官方突然決定向下引進該增強機制,分別對JDK 6,7,8進行了支持:

https://blogs.oracle.com/java-platform-group/filter-incoming-serialization-data-a-little-of-jdk-9-goodness-available-now-in-current-release-families

當時pwntester大神還專門發了個推標慶祝了一下:

所以官方從8u121,7u13,6u141分別支持了這個JEP。

我為什么現在才來說這個case,因為最近測一個RMI的漏洞過程中,發現居然默認情況下把反序列化給攔截掉了,看了異常信息,發現是JDK本身造成的。后來才知道,原來這個java 9 的特性早就移植到6,7,8了。因此打算著重討論下這個新的機制對RMI序列化的影響。

0x01 JEP290介紹

JEP290主要描述了這么幾個機制:

(1)提供一個限制反序列化類的機制,白名單或者黑名單

(2)限制反序列化的深度和復雜度

(3)為RMI遠程調用對象提供了一個驗證類的機制

(4)定義一個可配置的過濾機制,比如可以通過配置properties文件的形式來定義過濾器

實際上就是為了給用戶提供一個更加簡單有效并且可配置的過濾機制,以及對RMI導出對象執行檢查。

其核心實際上就是提供了一個名為 ObjectInputFilter 的接口,用戶在進行反序列化操作的時候,將 filter 設置給 ObjectInputStream 對象。這里就是調用 setInternalObjectInputFilter 即可:

每當進行一次反序列化操作時,底層就會根據 filter 中的內容來進行判斷,從而防止惡意的類進行反序列化操作。此外,還可以限制反序列化數據的信息,比如數組的長度、字節流長度、字節流深度以及使用引用的個數等。filter 返回 accept,reject 或者 undecided 幾個狀態,然后用戶根據狀態進行決策。

而對于RMI來說,主要是導出遠程對象前,先要執行過濾器邏輯,然后才進行接下來的動作,即對反序列化過程執行檢查。

此外,還提供了兩種可配置過濾器的方式:

(1)通過設置jdk.serialFilter這個System.property

(2)直接通過conf/security/java.properties文件進行配置

具體規則方面的內容可以直接參考原始鏈接:

http://openjdk.java.net/jeps/290

0x02 RMI的過濾機制

RMI這個就不多介紹了,給出一張原理圖:

RMI將網絡通信的部分進行了抽象,這部分邏輯對于用戶來說是透明的,其實是通過動態代理機制來實現的,通過Stub和Skel這兩個代理對象,完成了對遠程對象的調用。

扯遠了,我們還是看看RMI中新加入的過濾機制。

首先先要復現該問題,自己寫一個 RMI Server,然后啟動起來。之后,寫一個 RMI Client,調用 bind 方法將惡意的類發送給 Server。結果直接拋出異常:

同時 Server 的控制臺打印出來錯誤日志:

可以看到,是在處理遠程對象代理的時候,沒有通過 filter 的校驗從而報錯。

我使用的是 8u131 進行調試,直接來到 RegistryImpl_Skel 類中的46行代碼,這里就是導出遠程對象:

readObject 正是在執行反序列化操作,單步跟進,就來到了 ObjectInputStream 的 readObject 方法中,調用的是 readObject0 方法:

接下來是 readOrdinaryObject-> radClassDesc->readProxyDesc,然后獲取里面的接口并調用 filterCheck 方法一個個檢查,最后再對對象本身進行一次檢查:

來到filterCheck方法中:

可以看到這里調用了 ObjectInputStream 中的 serialFilter 屬性的 checkInput 方法,最后真正檢查的是 RegistryImpl.registryFilter 方法,針對遠程對象的檢查條件如下:

return String.class != var2 &&   
!Number.class.isAssignableFrom(var2) &&   
!Remote.class.isAssignableFrom(var2) &&   
!Proxy.class.isAssignableFrom(var2) &&   
!UnicastRef.class.isAssignableFrom(var2) && !RMIClientSocketFactory.class.isAssignableFrom(var2) && !RMIServerSocketFactory.class.isAssignableFrom(var2) && !ActivationID.class.isAssignableFrom(var2) &&   
!UID.class.isAssignableFrom(var2) ? Status.REJECTED : Status.ALLOWED;  

可以看到直接把 AnnotationInvocationHandler 給禁用掉了,所以這個方法肯定是要返回 REJECTED 狀態了,因此直接就拋了異常出來。

0x03 思考

ObjectInputFilter 的引入是給了用戶一個非常方便并且很官方的反序列化過濾機制,因此用好它可以很方便的寫出過濾代碼,思考一下反序列化剛出現的那會兒,還得自己去編碼實現過濾機制,稍有不慎就會出問題。但是有了 Filter 機制,并不代表一定不會出問題,原因是開發者使用黑名單機制還是有可能漏掉一些lib或者有新的 gadgets 出現。所以以后反序列化漏洞還是可以玩一段時間,畢竟底層開發者技術跟進需要時間。

最后說一句,RMI 這種粗暴的過濾我實在保留意見,因為可能會讓很多基于 RMI 的程序面臨兼容性問題。


Paper 本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.bjnorthway.com/454/