Dubbo HttpInvoker反序列化分析

2021-12-30 雁行安全團隊

之前審計的過程中,遇到過Dubbo這個組件,雖然知道這個組件存在反序列化漏洞,但是關於漏洞的詳情和利用一概不知,所以下面對Dubbo的漏洞進行分析。

背景

Dubbo是一款開源的RPC和微服務治理的框架,最早是阿里開發的後來歸到了Apache下面,支持多種協議,比如gRPC、Thrift、JsonRPC、Hessian2、REST、RMI、HTTP。

下面是官網對於Dubbo架構的介紹。

主要包含消費者、服務提供者、註冊中心、監控中心、容器等等。

provider在啟動時會將服務註冊到註冊中心,consumer在使用時會從註冊中心獲取到已經註冊的服務,再根據consumer中配置的路由決定調用哪個服務,consumer通過RPC協議調用provider的服務並獲取返回結果。

環境搭建

github下載官方提供的https://github.com/apache/dubbo-samples項目,導入simple-http。

直接下載的項目跑不起來,要更改http-provider.xml和http-consumer.xml的內容。

<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://code.alibabatech.com/schema/dubbo
http://code.alibabatech.com/schema/dubbo/dubbo.xsd">

由於漏洞影響的版本為2.7.3所以要更改pom.xml的配置,注意是在dependency下面新增一個version標籤,<version>2.7.3</version>。

還有坑點,原本的配置是用${}包含配置文件的,但是低版本解析過程並沒有解析${}中的內容,所以要進行更改。

更改後zookeeper更改後如下,port和server的參數更改類似。

下面依次啟動zookeeper,provider和Consumer即可,調試好後可以添加低版本的CC依賴,可以直觀的看到利用結果。

<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.0</version>
</dependency>

漏洞分析

在漏洞分析前我們還要再鋪墊一些前置知識,provider在導出服務並註冊到註冊中心zookeeper時,實際上註冊的是一個URL地址,但是這個URL並不是java.net.URL而是Dubbo自定義的URL,包含了下面幾個屬性。

dubbo://192.168.1.6:20880/moe.cnkirito.sample.HelloService?timeout=3000
zookeeper://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=demo-consumer&dubbo=2.0.2&interface=org.apache.dubbo.registry.RegistryService&pid=1214&qos.port=33333&timestamp=1545721981946

其中訪問的path可以在dubbo service的bean配置中通過path屬性進行設置,如果沒有設置,則默認使用interface的名稱當作path。

同樣,由於Dubbo在進行RPC調用時支持多種協議,也可以在Bean配置中通過protocol的name屬性進行配置,由於CVE-2019-17564是Dubbo以http協議通信時出現的問題,因此我們這裡配置也使用http的方式。

<dubbo:protocol name="http" id="http" port="8080" server="tomcat"/>

通過上面的配置,假如我們要訪問DemoService服務,可以通過下面的url訪問。

http://127.0.0.1:8080/org.apache.dubbo.samples.http.api.DemoService

現在我們再回到這個漏洞本身,通過官網的介紹Dubbo在實現http協議時使用的是spring的HttpInvoker來實現的。

我們再來了解HttpInvoker的介紹,其實現是基於標準的Java的序列化和反序列化機制,這裡之所以要通過序列化和反序列化來實現,是因為在進行RPC調用時,調用的參數和返回值不一定是基本類型,也有可能是對象,而對象的傳遞肯定是要基於序列化和反序列,值得一提的是Dubbo在實現RPC調用時,會將我們要調用的方法和參數封裝到RemoteInvocation對象中,服務端再接收到請求後,會將請求的內容反序列化為RemoteInvocation對象再去做其他操作,當我們了解了這些以後,即使不去看實現也能想到,在服務端provider接收到請求並進行反序列化時可能出現反序列化漏洞。

客戶端

之前在分析Dubbo源碼時已經分析了Http請求的處理過程,這裡就不仔細分析了,把重點的代碼說明一下即可。在AbstractHttpInvokerRequestExecutor#executeRequest中,會對RemoteInvocation對象進行序列化。

在RemoteInvocation對象中封裝了調用的方法名、請求參數類型、參數值等信息。

下面通過SimpleHttpInvokerRequestExecutor#doExecuteRequest發送請求,prepareConnection主要完成請求發送前的一些設置。

在SimpleHttpInvokerRequestExecutor#prepareConnection中可以看到通過POST發送請求。

writeRequestBody中將序列化的RemoteInvocation對象作為post請求的內容發送給服務端。

服務端

當請求過來時,交給DispatcherServlet會將請求交給HttpInvokerServiceExporter#handleRequest處理,這個方法會通過readRemoteInvocation將request中的內容轉換為RemoteInvocation對象。

獲取request對象中的請求內容,在doReadRemoteInvocation中通過readObject反序列化,如果反序列化的對象不是RemoteInvocation對象則拋出異常。

通過上面的分析,我們可以看出Dubbo Provider服務端在執行Consumer過來的請求時直接從POST中獲取對象並進行反序列化,在反序列化之前並沒有做其他安全的判斷,所以我們以POST發送任意一個對象到Provider都會被反序列化,如果我們傳入一個特殊構造的對象,Provider反序列化後則有可能造成命令執行或其他操作。

是不是一定要知道要調用的Provider提供服務的method和參數才能觸發反序列化?

這裡需要了解服務導出的操作,我是以Tomcat做為服務端的,可以看到在構造Tomcat伺服器時,添加了ServletMapping為*,也就是所有的請求都會被我們註冊的handler處理。

但是在InternalHandler#handle中會做如下處理。

首先獲取請求uri並判斷是否能找到對應的Exporter處理,如果找不到則會導致異常,SkeletonMap中保存的路徑如果沒有在provider的service元素中配置path屬性,則默認使用interface的內容。

所以並不是訪問服務端的任意接口都能觸發漏洞的

漏洞修復

在Dubbo 2.7.4或2.6.8 or 2.6.9修復了這個漏洞,我們看下漏洞的修復,首先看下服務導出的變化,在HttpProtocol#doExport中,不再使用HttpServiceExporter而是使用JsonRpcServer作為Exporter。

當我們請求服務時,也不再使用HttpServiceExporter處理請求而是使用JsonRpcBasicServer處理請求。

handleNode中根據類型的不同做不同的處理。

從ObjectNode中獲取參數和調用方法等信息。

得到要調用方法的method對象和參數值和實現類的代理對象後調用 invoke方法。

將參數類型做轉換後通過反射完成調用。

這個過程中並沒有涉及到JAVA本身的反序列化,不過還是涉及到JSON的反序列化操作,使用JACKSON進行反序列化,不過我並不熟悉JACKSON的漏洞,所以先放過吧。

參考文章

相關焦點

  • 源碼分析Dubbo服務調用-服務提供者如何處理請求命令與再談Invoker
    源碼分析Dubbo NettyServer與HeaderExchangeServer微信公眾號:[中間件興趣圈]作者簡介:《RocketMQ技術內幕》作者本文將重點剖析服務調用流程,也就是消費端通過網絡發起RPC服務調用時,服務提供者是如何進行服務請求響應的。
  • Dubbo中使用高效的Java序列化(Kryo和FST)
    但這裡實際不是原生的hessian2序列化,而是阿里修改過的hessian lite,它是dubbo RPC默認啟用的序列化方式json序列化:目前有兩種實現,一種是採用的阿里的fastjson庫,另一種是採用dubbo中自己實現的簡單json庫,但其實現都不是特別成熟,而且json這種文本序列化性能一般不如上面兩種二進位序列化。
  • Dubbo幾個常見面試題
    在容器啟動的時候,通過ServiceConfig解析標籤,創建dubbo標籤解析器來解析dubbo的標籤,容器創建完成之後,觸發ContextRefreshEvent事件回調開始暴露服務通過ProxyFactory獲取到invoker,invoker包含了需要執行的方法的對象信息和具體的URL地址再通過DubboProtocol的實現把包裝後的invoker轉換成exporter,然後啟動伺服器server
  • java反序列化之Commons Collections分析(一)
    前言在學習java反序列化的過程中,Commons Collections幾乎是反序列化學習中無法繞過的一關。在中間查閱了一些資料,發現在3.1-3.2.1版本中TransformingComparator並沒有去實現Serializable接口,也就是說這是不可以被序列化的。所以在利用鏈上就不能使用他去構造。
  • 已經有了 dubbo 協議 + hessian2,Dubbo 為什麼又推出了 HTTP/2 + gRPC?
    很久沒分享過 Dubbo 相關的文章了,眾所周知,Dubbo 默認的協議是 dubbo 協議(協議和框架同名),而 dubbo 協議默認指定的序列化方式是 hessian2,可能小夥伴們或多或少會有以下這些疑問:dubbo + hessian2 這一套組合一直用的好好,怎麼又要折騰出一套 gRPC + HTTP2 呢,這樣豈不是喪失了 Dubbo 框架的特點了嗎?
  • 從沉睡到滿血復活,阿里開源框架 Dubbo 有沒有讓你踩過坑?
    確實,面試的時候把源碼一波分析,絕對能令面試官虎軀一震!然而在一陣前戲過後,不料面試官突然來了句令劇情發生了反轉:「你對Dubbo源碼這麼熟悉,那請問你使用的時候有沒有遇到什麼坑?」——毫無準備的他頓時面臨著唬住了50K、唬不住就只能5K的局面,慌了!
  • PHP反序列化漏洞說明
    序列化可以將對象轉換成字符串,但僅保留對象裡的成員變量,不保留函數方法。PHP序列化的函數為serialize,反序列化的函數為unserialize.舉個慄子:<?反序列化反序列化就是序列化的逆過程,即對於將對象進行序列化後的字符串,還原其成員變量的過程。接上述慄子:<?
  • 告訴你 Dubbo 的底層原理,面試不再怕!
    Dubbo支持的協議很多,包括:dubbo、rmi、hessian、http、webservice、thrift、memcached、redis等。默認使用 dubbo 協議。在發送過去之前,還得對請求進行序列化。序列化有多種方式可以選擇,比如Json、Protobuf、Protostuff、Hessian、Kryo等、Java序列化等等。服務消費者接受到請求後的處理那麼服務提供者怎麼才能收到這個請求呢?
  • Java安全之反序列化漏洞分析
    方法,而反序列化是由ObjectInputStream的readObject方法實現的,下圖是作者畫的一個序列化示意圖:呵呵,意外往往就發生在不經意之間,如果反序列化過程中提供了命令執行的機會,那麼任意命令執行漏洞就產生了,如下我們在Session對象的readObject函數中增加了執行命令的代碼:
  • 文庫 | 反序列化漏洞匯總
    請注意,以任何序列化的對象格式修改數據類型時,務必記住也要更新序列化數據中的任何類型標籤和長度指示符,這一點很重要。否則,序列化的對象將被破壞並且不會被反序列化.實驗地址http://r6d.cn/Mgb9,任務是: 編輯會話cookie中的序列化對象以訪問administrator帳戶。
  • java反序列化——XMLDecoder反序列化漏洞
    前言最近學習java反序列化學到了weblogic部分,weblogic之前的兩個反序列化漏洞不涉及T3協議之類的,只是涉及到了XMLDecoder
  • php反序列化
    name=|O:4:%22test%22:2:{s:3:%22str%22;s:25:%22%3Cscript%3Ealert(1)%3C/script%3E%22;s:3:%22int%22;i:10;}再訪問http://luoke.cn:81/test/1.php觸發反序列化
  • 深入剖析 Java 反序列化漏洞
    2016 年 Spring RMI 反序列化爆出漏洞,攻擊者可以通過 JtaTransactionManager 這個類,來遠程執行惡意代碼。2017 年 4月15 日,Jackson 框架被發現存在一個反序列化代碼執行漏洞。
  • 常見的 Web 漏洞 —— 反序列化漏洞
    前段時間因各種原因,終於下定決心好好總結一下反序列化漏洞,耗時兩周,且看且珍惜!Vulnerability Introduction反序列化漏洞首次出現在2015年。雖然漏洞較新,但利用十分熱門,主要原因還是太過信任客戶端提交的數據,容易被開發者忽略,該漏洞一般都可執行任意命令或代碼,造成的影響較大。
  • PHP反序列化筆記
    \x00 + 類名 + \x00 + 變量名 ‐> 反序列化為private變量\x00 + * + \x00 + 變量名 ‐> 反序列化為protected變量<?file=Tzo1OiJTb0Z1biI6Mjp7czo3OiIAKgBmaWxlIjtzOjg6ImZsYWcucGhwIjt9PHP Session 反序列化PHP的3種序列化處理器PHP 內置了多種處理器用於存取$_SESSION數據時會對數據進行序列化和反序列化,常用的有以下三種,對應三種不同的處理格式
  • Java 8u20反序列化漏洞分析
    二、Java序列化過程及數據分析在8u20的POC中需要直接操作序列化文件結構,需要對Java序列化數據寫入過程、數據結構和數據格式有所了解。分析Java序列化文件,使用SerializationDumper工具可以幫助我們理解,這裡使用SerializationDumper查看這個序列化文件看下STREAM_MAGIC - 0xac edSTREAM_VERSION - 0x00 05Contents
  • PHP反序列化漏洞基礎
    一、PHP序列化和反序列化1、PHP反序列化:將變量或者對象轉換成字符串的過程,用於存儲或傳遞PHP的值的過程種,同時不丟失其類型和結構。②在反序列的時候php會根據s所指定的字符長度去讀取後面的字符。如果指定的長度錯誤則反序列化就會失敗。2、session反序列化(1)session概念PHP session時一個特殊的變量,用於存儲有關用戶會話的信息,或更改用戶會話的設置。session變量保存的信息是單一用戶的,並且可供應用程式中的所有界面使用。
  • 當當網開源 Dubbox,擴展 Dubbo 服務框架支持REST風格遠程調用
    當當網近日開源了Dubbox項目,可為Dubbo服務框架提供多項擴展功能,包括REST風格遠程調用、Kryo/FST序列化等等。
  • Laravel8反序列化POP鏈分析挖掘
    相比較於目前被分析較多的Larave 5.X版本的POP鏈,Laravel 8 部分組件版本較新,部分類加上了__wake方法進行過濾或者直接禁止了反序列化,故利用方式有所差異。本文分析並挖掘了當前Laravel 8版本中的反序列化鏈。
  • Java 序列化的這三個坑千萬要小心
    /dev-guide/images/dubbo-extension.jpg圖片來源:https://dubbo.apache.org/zh/docs/v2.7/dev/design/從Dubbo的調用鏈可以發現是有一個序列化節點的