作者:
xcoder
·
2016/03/02 10:38
0x00 簡介
序列化的問題貌似在最近爆發的非常頻繁,最近有小夥伴在問我關於這兩天爆發的Xstream組建的反序列化的漏洞,最近公司非常忙,不過趕上週末剛好抽時間看了下,其實這次的漏洞和之前JRE的那個反序列化漏洞觸發的條件基本上差不多,不過關於JRE的那個序列化似乎沒人關注,有興趣的同學可以去找找關於那個JRE的序列化,影響力不亞於11月份我分析的那個Apache CommonsCollection的漏洞。好了,回到正文吧。在分析Xstream漏洞時發現,XStream漏洞的根源在於Groovy元件的問題,其實在15年的時候有人給Groovy報了一個CVE-2015-3253的Bug,不過網上似乎沒有太多細節,為什麼這次分析XStream的漏洞的時候要提到Groovy的那個CVE,因為漏洞的根源就來自於那個CVE。
先來說說那個Groovy的CVE-2015-3253的漏洞吧。
0x01 Groovy-CVE-2015-3253漏洞(影響範圍1.7.0-2.4.3)
網上貌似沒有對該漏洞的分析,所以只能透過cve的連線去看看具體是什麼問題,http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2015-3253,官方的描述如下:
The MethodClosure class in runtime/MethodClosure.java in Apache Groovy 1.7.0 through 2.4.3 allows remote attackers to execute arbitrary code or cause a denial of service via a crafted serialized object.
透過上述漏洞描述資訊,我們知道了問題大概出現在了MethodClosure類上,該類定義以及方法如下圖
該類的描述為Represents a method on an object using a closure which can be invoked at any time,大概意思就是透過構建一個指定物件以及呼叫方法的Closure的例項並且可以在任何時候進行呼叫。上圖紅色線標記的方法即為觸發構建好的物件以及指定方法的函式,我們跟進看看該方法最終是怎麼樣執行的。
透過該方法的註釋可以知道該方法的作用為呼叫指定物件的指定方法,所以MethodClosure類中構造方法中的兩個引數的意思為owner代表呼叫方法的物件,method為呼叫方法的名字,所以我們可以構造特定了物件從而實現執行特定函式,我們自己定義的物件以及方法最終會呼叫上圖中紅色框標記的函式進行執行。
舉個例子,例如我們想透過MethodClosure實現執行命令的功能,那麼程式碼如下
#!java
MethodClosure mc = new MethodClosure(new java.lang.ProcessBuilder("open","/Applications/Calculator.app"), "start");
mc.call();
注:這裡呼叫的call方法最終會呼叫doCall函式,有興趣的可以自己去除錯。
這樣上述程式碼就可以實現程式碼執行,關於該函式的功能我們基本上搞明白了,那麼我們回過頭來想想,難道這個CVE就是說了下這個函式可以執行特定程式碼麼?
既然我們知道了如何構建以及觸發相關函式從而導致程式碼的執行,那麼我們不妨去找找看看那些函式呼叫了存在缺陷的函式,透過eclipse我們可以很容易看出那些地方呼叫了MethodClosure#call()函式
如上圖所示,我們可以看到groovy.util.Expando類的hashcode以及toString等方法呼叫了MethodClosure#call()函式,到這裡從事java的小夥伴們應該比較激動,這裡的hashCode()方法呼叫了存在缺陷的函式,hashCode函式才是這個CVE比較核心的地方,首先我們需要知道hashCode函式的作用,當兩個物件比較是否相等的時候,會呼叫該物件的hashCode以及equals方法進行比較,如果這兩個方法返回的結果一致,那麼認為這兩個物件是相等,如果被呼叫物件沒有重寫hashCode以及equals方法,那麼會呼叫父類的預設實現。
這裡明白hashCode的作用之後,再來說說HashMap的put方法,該方法的定義如下
因為Map是一種key-value型別的資料結構,所以Map集合不允許有重複key,所以每次在往集合中新增鍵值對時會去判斷key是否相等,那麼在判斷是否相等時會呼叫key的hashCode方法,如果我們精心構造一個groovy.util.Expando物件作為Map集合的key,那麼在將物件新增進集合時就會觸發groovy.util.Expando的hashCode方法,從而觸發我們的惡意程式碼。
明白上面的知識後我們再來跟進groovy.util.Expando#hashCode方法,看看如何精心構造一個一刻執行惡意程式碼的物件,如下圖
這裡從上圖中可以看出呼叫getProperties().get("hashCode")
方法從而實現自定義的hashCode,我們只需要呼叫setProperties("hashCode",Expando例項)
去繫結hashCode屬性對於的實現就行了,這裡hashCode必須是Closure或者其子類才能最終呼叫call函式,MethodClosure類恰好是Closure的子類,所以結合這兩個地方,惡意程式碼就會成功觸發。
上面說到過透過呼叫Map#put
方法即可觸發我們構造好的程式碼,那麼有人可能會問了,那些場景下才會觸發Map的put方法,在反序列化時這樣的場景還是存在的,除了這次的Xstream反序列化之外java的其他反序列化類中很可能也是有這樣的場景的。
下面給出利用程式碼
0x02 XStream反序列化漏洞
Xstream的反序列化漏洞的根源就是上文所述的Groovy元件的問題,只不過在Xstream中進行反序列化時恰好有觸發存在缺陷函式的點,也就是Xstream在反序列化時呼叫了Map#put函式將構造好的Expando例項作為key新增到集合中時觸發了程式碼執行,如下圖
這裡的key就是我們構造的Expando的例項物件。
在構造EXP時,首先我們要構造一個Expando的一個物件例項,同時設定hashCode的實現為MethodClosure的例項,然後例項化一個HashMap物件呼叫put方法將Expando的例項化物件作為key,value任意新增到map中,然後讓Xstream對map進行序列化,這樣我們的Payload就OK了,
估計有很多人不明白漏洞作者部落格的POC是怎麼來的,這裡的序列化是基於xml的,所以得藉助Xstream相關函式將構造好的物件進行序列化然後生成xml,反序列化時解析xml,轉換成相關物件。
好人做到底,我就把POC的生成程式碼也發出來吧
執行程式後,我們的POC就生成成功,如下圖所示
至於怎麼執行其他的程式碼,這個還比較麻煩,除了執行命令之外,好像沒有什麼好的辦法,因為MethodClosure的建構函式中指定了要執行方法的物件以及執行的方法名稱,所以說只能呼叫一次建構函式,並且有一個無引數的方法可以執行,這樣才能實現函式的正常執行。
0x03 漏洞修復
這個漏洞的成因應該是兩方面的共同造成了,一方面等待Xstream官方的補丁,此外Groovy在2.4.3之後修復了程式碼執行的這個bug,禁用了MethodClosure的程式碼執行功能。
受影響的使用者可以透過升級Groovy的版本來緩解該漏洞造成的影響。
0x04 參考資料
- https://www.contrastsecurity.com/security-influencers/serialization-must-die-act-2-xstream?platform=hootsuite
- http://www.pwntester.com/blog/2013/12/23/rce-via-xstream-object-deserialization38/
本文章來源於烏雲知識庫,此映象為了方便大家學習研究,文章版權歸烏雲知識庫!