JAVA世界中不安全的反序列化風險

2018/01/05

INTRODUCTION

OWASP Top 10 Application Security Risks - 2017在上個月底熱騰騰的出爐啦!其中與先前2013版本新增的重大風險是:A4XML外部實體注入的風險、A8不安全的反序列化、A10不充分的記錄及監控。這次的專欄我們針對A8不安全的反序列化做介紹。我們將會以Java語言作為示範反序列化的過程、以及以CVE2017-12149 JBOSS Application Server 5.X/6.X的反序列化遠端代碼執行的弱點作為示範。

What is DESERIALIZATION?

要提到反序列化,想當然爾必須先了解何為"序列化"。
我們從Java的官網的tutorial中找到這段文字介紹:
To serialize an object means to convert its state to a byte stream so that the byte stream can be reverted back into a copy of the object.
(將一個物件轉換成一個"可還原"的byte stream)
讓我們來看以下這段簡單的SAMPLE CODE:

大致上的意思是在建立一個文字字串的物件,然後將它序列化成檔案"serialization.demo"之後儲存到硬碟中,隨後將檔案讀回程式,再反序列化後將字串輸出。執行過程如下:

其中的關鍵我們可以來觀察"serialization.demo"這個檔案:由於是byteStream格式,所以我們使用xxd將binary格式轉換一下,變成可以看懂的字元,可以看到我們寫好的字串的確被包裝於檔案中。
再來下一步我們進階"實作"一個Serializable的MyObject物件來嘗試做反序列化:

我們實作了一個MyObject物件,並且在readObject時印出serializable!!!字串。執行情況如下:

果真如預期的先是印出了serializable!!!字串,再行印出物件所帶出的字串。
有注意到兩次輸出之後我們都有再將資料做一次base64編碼嗎?可以注意到有趣的是Java的反序列化物件在編碼後都是以rO0AB開頭,這是一個在執行滲透測試時相當重要的特徵。

What is INSECURE DESERIALIZATION in Java?

在我們看完簡單的序列化原理後,就要進入正題啦!為什麼反序列化被當作參數後,遭到惡意攻擊者修改後,就能輕鬆的達到遠端代碼執行的攻擊效果呢?
我們用倒果為因的方式,先從當下時下攻擊Java反序列化攻擊必備的ysoserial這套攻擊代碼產生工具( https://github.com/frohoff/ysoserial )的思維和程式碼,來快速切入思考反序列化的問題。

從使用方法中我們可以看到必須輸入Payload類型,而每個類型的Payload有其對應有反序列化問題的Java函式庫Dependency,為什麼Payload會有相依性呢?

我們挑選其中最常見的CommonCollections這個系列的第五項,來做為這次探討反序列化發生的原因,並在最後以一個Real Case: CVE2017-12149 JBOSS Application Server 5.X/6.X的反序列化遠端代碼執行的弱點作為示範。
在ysoserial的CommonCollections5.Java原始碼中,我們可以看到最上面註解部分提到利用來執行反序列化達成遠端代碼執行的"Gadget Chain",所謂"Gadget Chain"是利用Java語言反射的特性,將前面Dependency函式庫中,所定義的類別裡的函式做組合後,構成繞過Java機制可強制被執行的函式鍊,再將此函式鍊反序列化後的byteStream送至伺服器端,讓伺服器成功達到我們想要的遠端代碼執行。

此Gadget Chain的撰寫技巧在於將一般正常的執行函式,以反射的方式與Apache CommonCollections 中的ChainedTransformer結合,只要可以觸發transform()這個函式,就能成功將鏈結裡的InvokerTransformer串起後執行。這時我們從頂層重新看起,觸發的部分就靠Java原有的AnnotationInvocationHandler當作初始的關鍵觸發元件,其中的entrySet()被稱作magic method,會觸發Map中的invoke方法,再傳遞至內層的AnnotationInvocationHandler的invoke方法,最後包裝於CommonCollections的LazyMap中,將前面的ChainedTransformer放在裡面,即可成功執行。

CVE2017-12149 JBOSS AS 5.X/6.X Insecure Deserialization to RCE

我們先針對JBOSS AS 做簡單的code review,這個漏洞發生於URL路徑/invoker/readonly之中,經過Trace之後發現實際程式位置在org.jboss.invocation.http.servlet中的ReadOnlyAccessFilter,其中針對了輸入資料做反序列化的動作。

既然程式端有反序列化,那麼我們就來開始構造我們的payload吧!
由於最後會執行Runtime.getRuntime().exec()的關係,加上最後解析時會出現一些問題,所以我們透過以下工具將執行語句( http://jackson.thuraisamy.me/runtime-exec-payloads.html )快速編碼成base64格式:

隨後再將編碼好之payload以ysoserial工具,將payload填入Gadget Chain中再反序列化。

最後就可以在我們預先設定好之nc之下得到reverse shell了!

Conclusion
由於攻擊者利用Java反射機制的副作用,在物件return之前就將所有動作執行完畢,導致反序列化在解開byteStream時並且跳出error之前就將Payload全數執行。導致攻擊者只要掌握後端程式中有何種函式庫,將函式庫中各種函式做組合,跨函式庫呼叫函式組合成Gadget Chain,最終執行Runtime.getRuntime().exec()以執行任意惡意代碼。

簡單來說就是一個使用者將110V的電器插入220V的插座中,雖然是一樣的插座孔,不過使用者並不知道插座的伏特數是多少,直到插座與電器通電、電器燒壞後才發現伏特數不合拍。同樣的,反序列化也是在含有遠端代碼執行的byteStream進入系統,在Payload執行後才發現,這byteStream格式是有問題的。



其他訊息