Apache Druid 遠程代碼執行漏洞分析(CVE-2021-25646)

作者:Skay@QAX A-TEAM
原文鏈接:https://mp.weixin.qq.com/s/m7WLwJX-566WQ29Tuv7dtg

一、調試環境

https://archive.apache.org/dist/druid/0.20.1/

這裏嘗試了幾種常規的調試方法都不行,然後看到conf目錄下存在jvm.config,一搜好多,因爲我們的啓動腳本爲start-micro-quickstart,所以最後範圍鎖定在這幾個

圖片

一開始踩了一個坑,D:\java\druid\druid run\apache-druid-0.20.0\conf\druid\single-server\micro-quickstart\router\runtime.properties 中我看到了8888端口,這恰恰也是我們的web服務端口,下意識就改了這個jvm.config

圖片

某些斷點可以停住,但是我們漏洞出發點並不行,後來經過Smi1e師傅的提示,需要我改錯了

圖片

在此感謝~

二、漏洞分析

往上分析文章就只有阿里發出的,拿出上學的時候做閱讀理解的本事,使勁看吧....

1.定位JavaScriptDimFIlter

阿里爸爸文章一開始就提出了利用的關鍵類JavaScriptDimFilter,反思一下,因爲對Java相關的JavaScript知識儲備幾乎是0,看到fasterxml就一心想着反序列化了.........

這裏簡單提一下Javascript,Druid支持在運行時動態注入JavaScript,且不會在沙箱中執行Java腳本,因此它們具有對計算機的完全訪問權限。因此,JavaScript函數允許用戶在druid進程內執行任意代碼。因此,默認情況下,JavaScript是禁用的。但是,在開發/登臺環境或受保護的生產環境中,可以通過設置configuration屬性 來啓用它們druid.javascript.enabled = true。

這裏提到了關於漏洞的關鍵點:druid.javascript.enabled,留個坑,稍後提到。

2.關於JsonCreator註解、JsonProperty註解、以及CreatorProperty類型

文章中提到了兩個註解,以及一個類(參數類型),這裏需要去補一下Jackson的一些知識了

@JasonCreator

該註解用在對象的反序列時指定特定的構造函數或者工廠方法。在反序列化時,Jackson默認會調用對象的無參構造函數,如果我們不定義任何構造函數,Jvm會負責生成默認的無參構造函數。但是如果我們定義了構造函數,並且沒有提供無參構造函數時,Jackson會報錯 再回到@JsonCreator註解,其作用就是,指定對象反序列化時的構造函數或者工廠方法,如果默認構造函數無法滿足需求,或者說我們需要在構造對象時做一些特殊邏輯,可以使用該註解。該註解需要搭配@JsonProperty使用

@JsonProperty

此註解作用於屬性上,作用是把該屬性的名稱序列化成另一個自己想要的名稱 對屬性名進行重命名,在java裏我們墨守規定駝峯命名,但是在一些特殊的場合下,比如數據庫是下劃線等,再此我們就可以進行映射 對屬性名稱重命名,比如在很多場景下Java對象的屬性是按照規範的駝峯書寫,但在數據庫設計時使用的是下劃線連接方式,此處在進行映射的時候

有了對註解的理解,再來看這句話,以及JavaScriptDimFilter的構造函數

圖片

圖片

JavaScriptDimFilter的構造函數是用JasonCreator修飾的,也就說JavaScriptDimFilter這個類在反序列化(這裏指的是從Json數據轉化爲對象)時,Jackson會調用這個構造方法,且由於dimesion、function、extractionFn、filterTuning都有@JasonProperty註解修飾,Jackson在在反序列化處理解析到JavaScriptDimFilter時,都會被封裝爲CreatorProterty類型,而對於沒有被標記@JasonProperty的config參數,會創建一個name爲””的CreatorProperty

圖片

跟到這裏提出了一個疑問,Jackson是怎樣將org.apache.druid.js.JavaScriptConfig注入到裏面的?留坑,稍後回答。

圖片

3.Jackson解析用戶輸入

(1) Jersey的初始配置注入部分

1.HTTP Server端採用的是Jersey框架,所有的配置信息都由Guice框架在啓動的時候進行綁定注入,比如利用的JavaScriptConfig,初始化的時候讀取配置文件中的druid.javascript.enabled綁定到JavaScriptConfig的enabled field,這部分是非本地用戶不可控的。

對應了上文提到的druid.javascript.enabled,我嘗試在druid中的配置文件找這個開關的配置,反編譯後全局搜javascript關鍵字都沒找到,後來請教了下Litch1師傅,說這個是默認配置,沒有具體去跟,具體可以看下有個test測試裏有這部分:

圖片

(2) Jackson獲取對應的creatorProperty(JavaScriptConfig)

文中直接定位到了com.fasterxml.jackson.databind.deser.BeanDeserializer#_deserializeUsingPropertyBased方法

會拿解析到的json串中的“鍵名”去查找當前解析對象中對應的creatorProperty,這步對應的是findCreatorProperty方法,findCreatorProperty方法會去_propertyLookup 這個HashMap中查找”鍵名”對應的屬性,在_propertyLookup中可以看到其中沒有用JsonProperty註釋修飾的JavaScriptConfig的鍵爲””,要是json串中的鍵也爲””,就能匹配上,取出JavaScriptConfig對應的creatorProperty

跟着文章將視線集中到com.fasterxml.jackson.databind.deser.impl.PropertyBasedCreator#findCreatorProperty(java.lang.String)

圖片

可以看到它在一個for循環中,for循環遍歷了p(JsonParser,這裏稍微不準確的理解爲存儲了待處理的Json數據),按照Json層級結構一層一層的處理Json數據,因爲payload較長,快進到“”處理邏輯,從P中取到propName,然後進入findCreatorProperty

圖片

this._propertyLookup是個數組,直接取到我們想要的JavaScriptConfig對應的creatorProperty

圖片

還是之前留的坑,這個Hashmap是怎麼初始化的?Jackson是怎樣將org.apache.druid.js.JavaScriptConfig注入到裏面的?這裏仔細跟一下

圖片

這個this,也就是creator

圖片

圖片

這裏仔細跟下調用棧跳到上一步

deserializeFromObjectUsingNonDefault:1287, BeanDeserializerBase,Hashmap被存在了((BeanDeserializer)this)._propertyBasedCreator中

圖片

再往上,deserializeFromObject:326, BeanDeserializer 還是this._propertyBasedCreator,也就是BeanDeserializer的屬性

圖片

一直向上跟進,看到了BeanDeserializer調用deserialize方法

可以看到這裏的deser中已經初始化完畢了_propertyLookup Hashmap

圖片

再去看deser的初始化情況

圖片

其實跟到這裏就想停了,因爲payload裏我們給的type是javascript,反序列化時會Jackson會解析到相應的實體類也就是JavaScriptDimFilter

圖片

繼續走跟到com.fasterxml.jackson.databind.jsontype.impl.TypeDeserializerBase#_findDeserializer中

圖片

經過一層層的變量傳遞,來到了deserializeTypedFromObject:97, AsPropertyTypeDeserializer

圖片

deserialize:527, SettableBeanProperty 這時這個hashmap 被賦值到了SettableBeanProperty的_valueTypeDeserializer參數上

圖片

然後向前追蹤SettableBeanProperty,最終又回到了BeanDeserializer

圖片

可以看到creatorProp通過findCreatorProperty中獲得

圖片

又回來了.....這時候就懵逼了一會兒,不過感謝idea

圖片

_deserializeUsingPropertyBased:417, BeanDeserializer 被調用了四次,回頭看下我們的payload Json數據是四層,嗯,對上了....大概是這個樣子的吧.....這裏有點懵

這裏放一下部分調用棧吧,

_findDeserializer:198, TypeDeserializerBase (com.fasterxml.jackson.databind.jsontype.impl)
_deserializeTypedForId:113, AsPropertyTypeDeserializer (com.fasterxml.jackson.databind.jsontype.impl)
deserializeTypedFromObject:97, AsPropertyTypeDeserializer (com.fasterxml.jackson.databind.jsontype.impl)
deserializeWithType:254, AbstractDeserializer (com.fasterxml.jackson.databind.deser)
deserialize:527, SettableBeanProperty (com.fasterxml.jackson.databind.deser)
_deserializeWithErrorWrapping:528, BeanDeserializer (com.fasterxml.jackson.databind.deser)
_deserializeUsingPropertyBased:417, BeanDeserializer (com.fasterxml.jackson.databind.deser) [4]
deserializeFromObjectUsingNonDefault:1287, BeanDeserializerBase (com.fasterxml.jackson.databind.deser)
deserializeFromObject:326, BeanDeserializer (com.fasterxml.jackson.databind.deser)
deserialize:159, BeanDeserializer (com.fasterxml.jackson.databind.deser)
deserialize:530, SettableBeanProperty (com.fasterxml.jackson.databind.deser)
_deserializeWithErrorWrapping:528, BeanDeserializer (com.fasterxml.jackson.databind.deser)
_deserializeUsingPropertyBased:417, BeanDeserializer (com.fasterxml.jackson.databind.deser) [3]
deserializeFromObjectUsingNonDefault:1287, BeanDeserializerBase (com.fasterxml.jackson.databind.deser)
deserializeFromObject:326, BeanDeserializer (com.fasterxml.jackson.databind.deser)
deserialize:159, BeanDeserializer (com.fasterxml.jackson.databind.deser)
deserialize:530, SettableBeanProperty (com.fasterxml.jackson.databind.deser)
_deserializeWithErrorWrapping:528, BeanDeserializer (com.fasterxml.jackson.databind.deser)
_deserializeUsingPropertyBased:417, BeanDeserializer (com.fasterxml.jackson.databind.deser) [2]
deserializeFromObjectUsingNonDefault:1287, BeanDeserializerBase (com.fasterxml.jackson.databind.deser)
deserializeFromObject:326, BeanDeserializer (com.fasterxml.jackson.databind.deser)
deserialize:159, BeanDeserializer (com.fasterxml.jackson.databind.deser)
deserialize:530, SettableBeanProperty (com.fasterxml.jackson.databind.deser)
_deserializeWithErrorWrapping:528, BeanDeserializer (com.fasterxml.jackson.databind.deser)
_deserializeUsingPropertyBased:417, BeanDeserializer (com.fasterxml.jackson.databind.deser) [1]
deserializeFromObjectUsingNonDefault:1287, BeanDeserializerBase (com.fasterxml.jackson.databind.deser)
deserializeFromObject:326, BeanDeserializer (com.fasterxml.jackson.databind.deser)
_deserializeOther:194, BeanDeserializer (com.fasterxml.jackson.databind.deser)
deserialize:161, BeanDeserializer (com.fasterxml.jackson.databind.deser)
_deserializeTypedForId:130, AsPropertyTypeDeserializer (com.fasterxml.jackson.databind.jsontype.impl)
deserializeTypedFromObject:97, AsPropertyTypeDeserializer (com.fasterxml.jackson.databind.jsontype.impl)
deserializeWithType:254, AbstractDeserializer (com.fasterxml.jackson.databind.deser)
deserialize:68, TypeWrappedDeserializer (com.fasterxml.jackson.databind.deser.impl)
_bind:1682, ObjectReader (com.fasterxml.jackson.databind)
readValue:977, ObjectReader (com.fasterxml.jackson.databind)
readFrom:814, ProviderBase (com.fasterxml.jackson.jaxrs.base)
getEntity:490, ContainerRequest (com.sun.jersey.spi.container)
getValue:123, EntityParamDispatchProvider$EntityInjectable (com.sun.jersey.server.impl.model.method.dispatch)
getInjectableValues:86, InjectableValuesProvider

(3) 反序列化相應參數

拿到對應的creatorProperty之後就會將用戶輸入的json串中這個鍵對應的根據類型去反序列相應的參數,

圖片

(4) 觸發漏洞

最後漏洞的利用點就是利用config爲true之後繞過了對於config的檢查

圖片

然後進行JavaScript的執行。

三、總結一下

這個漏洞主要就是根據Jackson解析特性(解析name爲""時)會將value值綁定到對象(JavaScriptDimFilter,type爲javascript時指定的)的對應參數(config)上,造成JavaScriptDimFilter中function屬性中的javascript代碼被執行。

四、補丁分析

最新版本payload會報錯

圖片

官方修復

圖片

看下調試過程this._propertyLookup hashmap中只剩下了四個

圖片

直接幹掉了""對應的解析,這就阻斷了JavaScriptConfig對應的creatorProperty生成,hashmap中將不存在JavaScriptConfig,後面的鏈也就斷了.....無法打開JavaScript執行開關。

其實問了下洞主,說實際上還應該跟下具體Jackson執行邏輯具體是怎麼修復的,但是那個遞歸搞得我太頭疼了,暫時先這樣吧....

五、參考鏈接

https://mp.weixin.qq.com/s/McAoLfyf_tgFIfGTAoRCiw

https://druid.apache.org/docs/0.19.0/tutorials/index.html

https://blog.csdn.net/u010900754/article/details/105859959

PS:感謝Litch1 和 Smi1e 指點....wtcl


Paper 本文由 Seebug Paper 發佈,如需轉載請註明來源。本文地址:https://paper.seebug.org/1481/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章