由于傳播、利用此文所提供的信息而造成的任何直接或者間接的后果及損失,均由使用者本人負(fù)責(zé),雷神眾測(cè)以及文章作者不為此承擔(dān)任何責(zé)任。
雷神眾測(cè)擁有對(duì)此文章的修改和解釋權(quán)。如欲轉(zhuǎn)載或傳播此文章,必須保證此文章的完整性,包括版權(quán)聲明等全部?jī)?nèi)容。未經(jīng)雷神眾測(cè)允許,不得任意修改或者增減此文章內(nèi)容,不得以任何方式將其用于商業(yè)目的。
Unsafe deserialization occurs within a Dubbo application which has HTTP
remoting enabled. An attacker may submit a POST request with a Java object in
it to completely compromise a Provider instance of Apache Dubbo, if this
instance enables HTTP.
上面這部分是官方描述,也就是說當(dāng)HTTP remoting 開啟的時(shí)候,存在反序列化漏洞。有一點(diǎn)在描述中值得注意的,也就是說它影響不只是dubbo,還有spring-web(5.1.9.RELEASE)之前。
This vulnerability can affect users using Dubbo-Rpc-Http (2.7.3 or lower) and
Spring-Web (5.1.9.RELEASE or lower).
影響版本:
Dubbo 2.7.0 to 2.7.4
Dubbo 2.6.0 to 2.6.7
Dubbo all 2.5.x versions
下載地址:https://github.com/apache/dubbo-samples/tree/master/java/dubbo-samples-http
把 dubbo-samples-http 這個(gè)demo下載下來,然后修改部分內(nèi)容,首先將 pom.xml 文件中的 dubbo.version 修改為 2.7.3 版本。
其次在 pom.xml 文件中加入反序列化的 gadget ,為了方便,我選擇了 CC2 的利用鏈中的 commons-collections4
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-collections4 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.0</version>
</dependency>
由于 dubbo 和 zookeeper 還有關(guān)系,所以搭建環(huán)境的時(shí)候需要安裝這個(gè),并且啟動(dòng)。當(dāng)這些一切就緒之后啟動(dòng)demo中的org/apache/dubbo/samples/http/HttpProvider。
$ java -jar ysoserial-master-55f1e7c35c-1.jar CommonsCollections2 "open /System/Applications/Calculator.app" > payload.ser
從右圖中的報(bào)錯(cuò)調(diào)用棧其實(shí)很明顯定位到了最開始入口是javax.servlet.http.HttpServlet.service。
在javax.servlet.http.HttpServlet.service處下個(gè)斷點(diǎn),可以看到這個(gè)地方處理HTTP請(qǐng)求進(jìn)來的 request 和 response 。
然后進(jìn)入到org.apache.dubbo.remoting.http.servlet.DispatcherServlet中,這里會(huì)有個(gè)判斷,如果handler對(duì)象等于null,就返回404告訴對(duì)方,服務(wù)沒有找到,否則繼續(xù)調(diào)用相關(guān)對(duì)象的handle方法進(jìn)行處理。
事實(shí)上這里dubbo支持這幾種方式或者用協(xié)議來說更為合適,來進(jìn)行數(shù)據(jù)的傳輸交互,而本次的處理HTTP協(xié)議的進(jìn)入到自然是org.apache.dubbo.rpc.protocol.http.HttpProtocol這個(gè)類里面。
而在org.apache.dubbo.rpc.protocol.http.HttpProtocol這個(gè)類中,首先會(huì)判斷請(qǐng)求方式是否是POST,不是的話會(huì)拋出500錯(cuò)誤,其次就是從request對(duì)象中獲取遠(yuǎn)程remoteAddr和remotePort,這個(gè)不多贅述,接著就進(jìn)入skeleton.handleRequest進(jìn)行處理。
跟進(jìn)之后skeleton.handleRequest之后會(huì)發(fā)現(xiàn)調(diào)用的是 spring 的 httpinvoker ,其中 readRemoteInvocation 會(huì)處理我們傳入的 request 對(duì)象。
繼續(xù)跟進(jìn) readRemoteInvocation 會(huì)發(fā)現(xiàn),它返回的是RemoteInvocation.readRemoteInvocation的處理結(jié)果。
繼續(xù)跟進(jìn)RemoteInvocation.readRemoteInvocation
調(diào)用 RemoteInvocationSerializingExporter#doReadRemoteInvocation 來針對(duì)數(shù)據(jù)流進(jìn)行處理。
跟進(jìn) RemoteInvocationSerializingExporter#doReadRemoteInvocation ,反序列化入口就在這里了。
實(shí)際上可以看到的一點(diǎn)就是,dubbo在進(jìn)行HTTP協(xié)議做數(shù)據(jù)傳輸?shù)臅r(shí)候,走的是Java序列化,我通過wireshark抓了一個(gè)包,相信這個(gè)大家一定不陌生,從 ContentType: application/x-java-serialized-object 和報(bào)文 Body 部分的 ASCII 碼可以看出,使用的是 Java Serialize 序列化,而這部分功能的實(shí)現(xiàn)使用的自然是 spring 的httpinvoker 功能。
細(xì)看 spring 的docs,也做了下圖可能存在反序列化的風(fēng)險(xiǎn)提示。
前面說過dubbo的http處理是通過org.apache.dubbo.rpc.protocol.http.HttpProtocol,然后是實(shí)例化一個(gè)JsonRpcServer對(duì)象skeleton來處理uri,緊接著調(diào)用 skeleton.handle
也就是com.googlecode.jsonrpc4j.JsonRpcBasicServer#handle
在com.googlecode.jsonrpc4j.JsonRpcBasicServer#handle當(dāng)中沒辦法處理我們傳輸Java序列化字節(jié)流,因此就會(huì)拋出異常,也就是說這里的org.apache.dubbo.rpc.protocol.http.HttpProtocol后續(xù)處理不是通過spring httpinvoker 了,而是通過 jsonrpc 。
wireshark再抓個(gè)包確認(rèn)一下,嗯,確實(shí)是這樣。