作者: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
本文由 Seebug Paper 發佈,如需轉載請註明來源。本文地址:https://paper.seebug.org/1481/