作者: 天融信阿爾法實驗室
公眾號:https://mp.weixin.qq.com/s/nj0sKPaXXw_a2sjJD660Bw
0x00 前言
本文銜接上一篇文章《Fastjson 1.2.24反序列化漏洞深度分析》,繼續探討一下FastJson的歷史漏洞。
在《Fastjson 1.2.24反序列化漏洞深度分析》一文中,我們以Fastjson 1.2.24反序列化漏洞為基礎,詳細分析fastjson漏洞的一些細節問題。
Fastjson 1.2.24 版本的遠程代碼執行漏洞可謂是開辟了近幾年來Fastjson漏洞的紀元。在Fastjson 1.2.24版本反序列化漏洞初次披露之后,官方針對這個漏洞進行了修補。然而這個修復方案很快就被發現存在繞過的風險。由于官方的修復方案或多或少都存在著一些問題,隨之而來的是一次又一次的繞過與修復。
回顧一下Fastjson反序列化漏洞,簡單來說就是:fastjson通過parse、parseObject處理以json結構傳入的類的字符串形時,會默認調用該類的共有setter與構造函數,并在合適的觸發條件下調用該類的getter方法。當傳入的類中setter、getter方法中存在利用點時,攻擊者就可以通過傳入可控的類的成員變量進行攻擊利用。com.sun.rowset.JdbcRowSetImpl這條利用鏈用到的是類中setter方法的缺陷,而TemplatesImpl利用鏈則用到的是getter方法缺陷。
官方主要的修復方案是引入了checkAutotype安全機制,通過黑白名單機制進行防御。在隨后的版本中,為了增強漏洞繞過的難度,又在checkAutotype中采用了一定的加密混淆將本來明文存儲的黑名單進行加密。
在簡單的介紹完Fastjson 1.2.24版本遠程代碼執行漏洞后,我們來看一下官方是怎么修復這個漏洞的。
0x01 checkAutotype安全機制
Fastjson從1.2.25開始引入了checkAutotype安全機制,通過黑名單+白名單機制來防御。
我們先來看一下1.2.25版本初次引入的checkAutotype安全機制的形式
首先看一下如下圖樣例

這是一個很普通的測試樣例,用來將字符串轉變為Java對象。
執行結果如下

這個測試樣例在1.2.24版本是可以執行成功的,但在1.2.25版本中卻爆出來autoType is not support錯誤。
這是因為Fastjson 1.2.25版本引入了checkAutotype安全機制。在默認情況下AutoTypeSupport關閉且測試樣例中的AutoTypeTest.ForTest類雖然不在黑名單上,但也并不在checkAutotype安全機制白名單上,因此使用上圖測試樣例在Fastjson 1.2.25版本中反序列化失敗。
將AutoTypeSupport設置為打開時,見下圖


開啟AutoTypeSupport后,AutoTypeTest.ForTest類反序列化成功
接下來修改測試樣例,換成惡意類,再進行一次實驗

這一次我們動態調試一下
經過動態調試可以發現,程序在執行到com/alibaba/fastjson/parser/ParserConfig.java 中checkAutoType安全機制時,com.sun.rowset.JdbcRowSetImpl類會觸發黑名單,程序將拋出錯誤


我們來看一下Fastjson 1.2.25版本中引入的黑名單中元素都有哪些,見下圖

這次實驗中的poc無法成功利用,是因為com.sun.rowset.JdbcRowSetImpl類命中了黑名單而不能反序列化成功
在這里額外介紹一下:當程序經過黑白名單的校驗之后,接下來會通過TypeUtils.loadClass方法對類進行反序列化加載

TypeUtils.loadClass方法的實現比較有意思,下文要介紹的幾處漏洞也與之有關。
0x02 Fastjson 1.2.41版本漏洞
1.2.41版本漏洞利用,其實就是針對1.2.24版本漏洞所打的補丁的繞過,本次漏洞影響了1.2.41版本以及之前的版本
首先來看一下1.2.41版本漏洞利用的poc

可以發現,@type字段值為”Lcom.sun.rowset.JdbcRowSetImpl;”
在”com.sun.rowset.JdbcRowSetImpl”類的首尾多出了一個L與;
經過上文的介紹,我們知道@type字段值會經過黑名單校驗

黑名單的檢測機制很簡單,就是用黑名單中的元素依次與@type字段值進行字符串匹配,從@type字段值從首位開始,匹配到了黑名單中的元素,就會拋出錯誤。
很明顯,”Lcom.sun.rowset.JdbcRowSetImpl;”并不會匹配到黑名單。但是有一個問題:黑名單匹配機制明顯不管@type字段值末位是什么,又是為何在末位加一個”;”呢?首位的”L”又是什么呢?我們隨后會介紹一下這個。
通過上文對checkAutotype安全機制的解釋可以發現,@type字段值首先會經過黑白名單的校驗。在成功通過校驗之后,程序接下來會通過TypeUtils.loadClass方法對類進行加載

讓我們來跟入位于com/alibaba/fastjson/util/TypeUtils.java中的loadClass方法

從上圖可見,在該方法中有兩次if語句,見上圖紅框處
從字面意義上來看,第一處的作用是匹配以”[”開頭的字符串,而第二處則是匹配以”L”開頭,以”;”結尾的字符串。
這一點看起來與poc中的結構很相似,poc中構造的結構應該就是為這里準備的。
我們接下來就來分析一下,”[”、 ”L”、”;”這些都是什么?以及為什么FastJson為什么要寫兩處if邏輯來處理他們。
JNI字段描述符
首先我們來思考一個題外話,如何獲取一個類的數組類型的Class對象? 可參考下圖

上圖中三種方式都是可行的

通過調試結果可見,clazz1、clazz2、clazz3的name是一樣的,都為”[LAutoTypeTest.ForTest;”
”[LAutoTypeTest.ForTest;”這種類型的字符串其實是一種對函數返回值和參數的編碼,名為JNI字段描述符(JavaNative Interface FieldDescriptors)。
AutoTypeTest.ForTest類的Class對象名為”AutoTypeTest.ForTest”;為了區分,AutoTypeTest.ForTest類的數組類型的Class對象名則為”[LAutoTypeTest.ForTest;”這樣的形式。
其中首個字符”[”用以表示數組的層數,而第二個字符則代表數組的類型。
這里舉例說明一下JNI字段描述符的格式:
1、double[][]對應的類對象名為"[[D"
2、int[]對應的類對象名則為"[I"
3、AutoTypeTest.ForTest[]對應的類對象名則為"[LAutoTypeTest.ForTest;"
前兩者比較好理解,而第三個”[LAutoTypeTest.ForTest;”,可見第二個字符是”L”,且最后一個字符是”;”。這種形式叫類描述符,”L”與”;”之間的字符串表示著該類對象的所屬類(AutoTypeTest.ForTest)
這就是為什么FastJson中要有這樣的兩處if邏輯

實際上是用來解析傳入的數組類型的Class對象字符串
漏洞利用
經過我們上文的分析,已經對漏洞以及構造有了一定的了解。接下來重點來看下圖FastJson解析類描述符的代碼

當傳入的類名以”L”開頭,且以”;”結尾時,程序將去除首尾后返回。
這意味著,如果在惡意類前后加上”L”與”;”,例如”LAutoTypeTest.ForTest;”,這樣不僅可以輕易的躲避黑名單,隨后在程序執行到這里時,還會將首尾附加的”L”與”;”剝去。剝皮處理之后字符串變成”AutoTypeTest.ForTest”,接著”AutoTypeTest.ForTest”被loadClass加載,惡意類被成功反序列化利用。
我們測驗以下是否有效

動態調試后發現,程序的確進入如下if分支,并且剝去前后”L”與”;”

Poc執行成功,計算器也可以順利的彈出

這個漏洞在Fastjson 1.2.42版本中被修復。

0x03 Fastjson 1.2.42版本漏洞
Fastjson 1.2.42版本在處理了1.2.41版本的漏洞后。很快又被發現存在著繞過方式。
我們來分析下Fastjson 1.2.42中的checkAutotype安全機制,看看是怎么處理的1.2.41版本漏洞。見下圖

從上圖可以發現,不同于之前的版本,程序并不是直接通過明文的方式來匹配黑白名單,而是采用了一定的加密混淆。此時的黑名單也變成如下的樣子

針對這里的黑名單的原文明文也是有人曾經研究過的,可以參考如下鏈接
https://github.com/LeadroyaL/fastjson-blacklist
除了引入了黑名單加密混淆機制外,checkAutotype中也加入了一些新的機制,如下圖這里

與之前的版本相比,這里多出了上圖這一段代碼。從代碼上大體可以猜測出來,這是用來判斷類名的第一個字符與最后一個字符是否滿足一定條件,然后將首尾剝去
又看到熟悉的剝皮操作了,我們不難猜測到這兩個滿足條件的字符大概率是”L”與”;”。開發者的用意大概是想針對于1.2.41版本的利用”Lcom.sun.rowset.JdbcRowSetImpl;”,先剝去傳入類名首尾的”L”與”;”, 以便將惡意數據暴露出來,再經過黑名單校驗。
我們寫個小測試,看看這倆字符是不是”L”與”;”


實踐證明,FastJson這里要去除的還真是首尾的”L”與”;”
漏洞利用
因為這里很容易猜出怎么繞過,因此我們先不貼poc,一步步分析下執行流程最終構造處正確的poc。
首先程序進入checkAutoType后,進入如下兩個if分支進行處理

第一個if分支,是用來限制傳入的類名長度的,這里只要我們傳入的poc中類名長度在3與128之間即可。而第二個分支,我們上文已經分析過了,目的是剝去類名中的首尾”L”與”;”
因此構造下圖poc即可輕易繞過1.2.42版本


0x04 Fastjson 1.2.45版本漏洞
在Fastjson 1.2.45版本中,checkAutotype安全機制又被發現了一種繞過方式。
之前的幾次繞過都是針對checkAutoType的繞過,而這次則是利用了一條黑名單中不包含的元素,從而繞過了黑名單限制。
本次繞過利用到的是mybatis庫。如果想測試成功,需要額外安裝mybatis庫。下文測試用例中安裝的版本是3.5.5。
首先簡單介紹下mybatis,maven上的簡介如下:“MyBatis SQL映射器框架使將關系數據庫與面向對象的應用程序結合使用變得更加容易。MyBatis使用XML描述符或注釋將對象與存儲過程或SQL語句耦合。相對于對象關系映射工具,簡單性是MyBatis數據映射器的最大優勢。”
漏洞利用
本次利用poc如下

從poc中不難發現,@type指定了JndiDataSourceFactory類,而在properties屬性中的data_source變量中指定惡意數據源。由于JndiDataSourceFactory并不在黑名單上,因此可以順利通過黑名單校驗,在接下來的反序列化過程中,在為Properties變量賦值時調用其setter方法,可見下圖動態調試結果

在上圖setProperties方法中,程序將取出poc中構造的DATA_SOURCE值并觸發漏洞
0x05 寫在最后
除了上文分析的漏洞之外,FastJson還有幾個很精彩的漏洞,例如Fastjson 1.2.47版本和1.2.68版本的漏洞。因為篇幅有限,要寫的實在太多了,因此我把它們抽出來放在后續文章中介紹。
本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.bjnorthway.com/1319/
暫無評論