前段時間因各種原因,終於下定決心好好總結一下反序列化漏洞,耗時兩周,且看且珍惜!
Vulnerability Introduction
反序列化漏洞首次出現在2015年。雖然漏洞較新,但利用十分熱門,主要原因還是太過信任客戶端提交的數據,容易被開發者忽略,該漏洞一般都可執行任意命令或代碼,造成的影響較大。
在身份驗證,文件讀寫,數據傳輸等功能處,未對反序列化接口做訪問控制,未對序列化數據做加密和籤名,加密密鑰使用硬編碼(如Shiro 1.2.4),使用不安全的反序列化框架庫(如Fastjson 1.2.24)或函數的情況下,由於序列化數據可被用戶控制,攻擊者可以精心構造惡意的序列化數據(執行特定代碼或命令的數據)傳遞給應用程式,在應用程式反序列化對象時執行攻擊者構造的惡意代碼,達到攻擊者的目的。
解析認證token、session的位置
將序列化的對象存儲到磁碟文件或存入資料庫後反序列化時的位置,如讀取json文件,xml文件等
將對象序列化後在網絡中傳輸,如傳輸json數據,xml數據等
參數傳遞給程序
使用RMI協議,被廣泛使用的RMI協議完全基於序列化
使用了不安全的框架或基礎類庫,如JMX 、Fastjson和Jackson等
自定義協議用來接收與發送原始的java對象
在Python和PHP中,一般通過構造一個包含魔術方法(在發生特定事件或場景時被自動調用的函數,通常是構造函數或析構函數)的類,然後在魔術方法中調用命令執行或代碼執行函數,接著實例化這個類的一個對象並將該對象序列化後傳遞給程序,當程序反序列化該對象時觸發魔術方法從而執行命令或代碼。在Java中沒有魔術方法,但是有反射(reflection)機制:在程序的運行狀態中,可以構造任意一個類的對象,可以了解任意一個對象所屬的類,可以了解任意一個類的成員變量和方法,可以調用任意一個對象的屬性和方法,這種動態獲取程序信息以及動態調用對象的功能稱為Java語言的反射機制。一般利用反射機制來構造一個執行命令的對象或直接調用一個具有命令執行或代碼執行功能的方法實現任意代碼執行。
以pickle模塊為例,假設瀏覽器傳遞序列化後的Cookie給伺服器保存,伺服器經過一些處理後反序列化還原Cookie:
利用pickle模塊和魔術方法__reduce__生成執行命令的Payload:
使用執行whoami命令的Payload替換序列化後的Cookie的值模擬RCE漏洞利用,當正常程序反序列化Cookie值時生成包含__reduce__函數的exec類,從而執行命令:
PHP中通常使用serialize函數進行序列化,使用unserialize函數進行反序列化。
serialize函數輸出格式
輸出的數字基本都是代表長度,在構造Payload時需要注意修改長度。
使用下面代碼創建一個類A並實例化一個對象a,然後輸出序列化對象a後的值:
PHP中序列化後的數據中並沒有像Python一樣包含函數__construct和print的信息,而僅僅是類名和成員變量的信息。因此,在unserialize函數的參數可控的情況下,還需要代碼中包含魔術方法才能利用反序列化漏洞。
使用下面代碼定義一個包含魔術方法__destruct的類A,然後實例化一個對象a並輸出序列化後的數據,在對象銷毀的時候程序會調用system函數執行df命令,然後通過GET方法傳遞參數arg的值給伺服器進行反序列化:
不傳入arg參數時,伺服器返回對象a序列化後的數據和df命令執行的結果,如圖:
利用對象a序列化後的值構造執行id命令的Payload:O"A"{s"test";s"id";},通過arg參數提交之後,在反序列化的過程中成功覆蓋變量test的值為id,並在對象銷毀時執行命令,如圖:
當然,現實環境中幾乎沒有這樣方便的攻擊鏈,需要花不少時間去尋找POP鏈。
PHP反序列化入門之尋找POP鏈(一)
PHP反序列化入門之尋找POP鏈(二)
Java中通常使用Java.io.ObjectOutputStream類中的writeObject方法進行序列化,java.io.ObjectInputStream類中的readObject方法進行反序列化。使用下面代碼將字符串進行序列化和反序列化:
Java序列化數據格式始終以雙字節的十六進位0xAC ED作為開頭,Base64編碼之後為rO0。之後的兩個字節是版本號,通常為0x00 05。
一個Java類的對象要想序列化成功,必須滿足兩個條件:
該類必須實現 java.io.Serializable接口。
該類的所有屬性必須是可序列化的,如果有一個屬性不是可序列化的,則該屬性必須註明是短暫的。
執行程序後生成a.ser文件,以十六進位格式查看文件內容,如圖:
最後5個字節分別為字符串長度和calc的ASCII值。因此,修改文件為下圖所示,即notepad的ASCII值和長度:
現實環境中也沒有這樣方便的攻擊鏈,需要去尋找POP鏈。
從反序列化到命令執行 – Java 中的 POP 執行鏈
FastJson作為史上最快的Json解析庫應用也十分廣泛,在1.2.69版本以下,其AutoType特性在反序列化過程中會導致反序列化漏洞,這個特性就是:在對JSON字符串進行反序列化的時候,會讀取@type參數指定的類,然後把JSON內容反序列化為此類的對象,並且會調用這個類的設置(setter)方法。
實驗環境
前端採用json提交用戶名密碼
後臺使用fastjson 1.2.24版本
源碼和WAR包GitHub地址
創建一個User類,用於查看序列化數據格式,如圖:
創建一個home類用於輸出user對象的序列化數據,如圖:
創建一個login類用於獲取前端頁面提交的json格式用戶名和密碼數據,並使用JSON.parseObject方法進行反序列化解析json數據,在後臺可看到提交的數據,如圖:
訪問home頁面可直接獲取user對象序列化後的結果,如圖:
@type的值為對象所屬的類,user和passwd分別為對象的用戶名屬性和密碼屬性。因此可以利用AutoType特性,構造一個使用@type參數指定一個攻擊類庫,包含類屬性或方法的JSON字符串提交到伺服器,在反序列化時調用這個類的方法達到執行代碼的目的。通常使用java.net.Inet4Address類或java.net.Inet6Address類,通過val參數傳遞域名,利用DnsLog進行漏洞檢測,即:{"@type":"java.net.Inet4Address","val":"DnsLog"}。在登錄頁面輸入用戶名和密碼提交,攔截數據包,修改提交的Json數據,如圖:
雖然伺服器返回錯誤信息,但Payload仍然被成功執行,在DnsLog網站可以看到解析記錄,如圖:
基於JNDI注入
基於ClassLoader
基於TemplatesImpl
由於本實驗僅使用最小依賴編寫,此處不再詳細分析POP鏈。
BinaryFormatter
JavaScriptSerializer
XmlSerializer
DataContractSerializer
。。。
採用Xml提交數據
使用.NET Framework 4.6.1
完整源碼GitHub地址
使用下面代碼定義一個Test類,包含執行ipconfig命令並返回執行結果的函數Run,使用XmlSerializer類將對象序列化後輸出到頁面:
使用下面代碼將提交的XML數據反序列化,並執行對象的Run函數:
對反序列數據加密或籤名,且加密密鑰和籤名密鑰不要使用硬編碼
對反序列化接口添加認證授權
設置反序列化服務僅在本地監聽或者設置相應防火牆策略
禁止使用存在漏洞的第三方框架庫
過濾、禁用危險函數
過濾T3協議或限定可連接的IP
設置Nginx反向代理,實現t3協議和http協議隔離
Java反序列化工具YSoSerial.jar
PHP反序列化工具PHPGGC
.NET反序列化工具YSoSerial.NET
深入理解 JAVA 反序列化漏洞
Java反序列化漏洞從入門到深入
Java反序列化漏洞分析
從反序列化到命令執行 – Java 中的 POP 執行鏈
微信公眾號 |墨守安全
官方網站 |moushou.org.cn