您好,登錄后才能下訂單哦!
如何進行Fastjson 1.2.24反序列化漏洞深度分析,相信很多沒有經驗的人對此束手無策,為此本文總結了問題出現的原因和解決方法,通過這篇文章希望你能解決這個問題。
FastJson是alibaba的一款開源JSON解析庫,可用于將Java對象轉換為其JSON表示形式,也可以用于將JSON字符串轉換為等效的Java對象。近幾年來fastjson漏洞層出不窮,下面將會談談近幾年來fastjson
RCE漏洞的源頭:17年fastjson爆出的1.2.24反序列化漏洞。以這個漏洞為基礎,詳細分析fastjson漏洞的一些細節問題。
關于Fastjson 1.2.24反序列化漏洞,自從17年以來已經有很多人分析過了,一些基礎內容本文就不再陳述了。此次漏洞簡單來說,就是Fastjson通過parseObject/parse將傳入的字符串反序列化為Java對象時由于沒有進行合理檢查而導致的
小編將著重分析一下這個漏洞沒有被詳細介紹過的細節問題,如下:
1、parseObject(String text) 、parse (String text)、 parseObject(String text, Class\ clazz)三個方法從代碼層面上來看,究竟有何不同?
2、使用TemplatesImpl攻擊調用鏈路構造poc時,為什么一定需要構造_tfactory以及_name字段?
3、_outputProperties與其getter方法getOutputProperties()方法名字并不完全一致是如何解決的?
除此之外,在介紹TemplatesImpl攻擊調用鏈路時,以模擬尋找漏洞利用鏈的思路,從最終的執行點開始向上尋找入口,模擬還原出挖掘這個TemplatesImpl利用鏈的完整過程。
關于parse (String text) 、parseObject(String text)、 parseObject(String text, Class\ clazz)三個方法,我們進行一個測試
FastJsonTest類中變量以及其setter/getter關系如下表
| | public String t1 | private int t2 | private Boolean t3 | private Properties t4 | private Properties t5 |
| ------ | ---------------- | -------------- | ------------------ | --------------------- | --------------------- |
| setter | 有 | 有 | 無 | 無 | 有 |
| getter | 有 | 有 | 有 | 有 | 有 |
接下來,我們分別使用下圖三種方式分別將JSON字符串反序列化成Java對象
1、Object obj = JSON.parse(jsonstr);
2、Object obj = JSON.parseObject(jsonstr, FastJsonTest.class);
3、Object obj = JSON.parseObject(jsonstr);
首先我們運行一下Object obj = JSON.parse(jsonstr);這種方式
結果:
setT1() 、setT2() 、getT4() 、setT5() 被調用
JSON.parse(jsonstr)最終返回FastJsonTest類的對象
接著我們運行下Object obj = JSON.parseObject(jsonstr, FastJsonTest.class);
結果:
與JSON.parse(jsonstr);這種方式一樣setT1() 、setT2() 、getT4() 、setT5() 被調用
JSON.parse(jsonstr)最終返回FastJsonTest類的對象
最后我們運行下Object obj = JSON.parseObject(jsonstr);
結果:
這次結果與上兩次大不相同,FastJsonTest類中的所有getter與setter都被調用了,并且JSON.parseObject(jsonstr);返回一個JSONObject對象
通過上文運行結果,不難發現有三個問題
1. 使用JSON.parse(jsonstr);與JSON.parseObject(jsonstr, FastJsonTest.class);兩種方式執行后的返回結果完全相同,且FastJsonTest類中getter與setter方法調用情況也完全一致,parse(jsonstr)與parseObject(jsonstr, FastJsonTest.class)有何關聯呢?
2. 使用JSON.parse(jsonstr);與JSON.parseObject(jsonstr, FastJsonTest.class);兩種方式時,被調用的getter與setter方法分別為setT1()、setT2()、setT5()、getT4()。FastJsonTest類中一共有五個getter方法,分別為getT1()、getT2()、getT3()、getT4()、getT5(),為什么僅僅getT4被調用了呢?
3. JSON.parseObject(jsonstr);為什么返回值為JSONObject類對象,且將FastJsonTest類中的所有getter與setter都被調用了
經過調試可以發現,無論使用JSON.parse(jsonstr);或是JSON.parseObject(jsonstr,
FastJsonTest.class);方式解析json字符串,程序最終都會調用位于com/alibaba/fastjson/util/JavaBeanInfo.java中的JavaBeanInfo.build()
方法來獲取并保存目標Java類中的成員變量以及其對應的setter、getter
首先來看下JSON.parse(jsonstr)這種方式,當程序執行到JavaBeanInfo.build()
方法時情景如下圖
此時的調用鏈如下圖
此時傳入JavaBeanInfo.build() 方法的參數值如下圖
再來看下JSON.parseObject(jsonstr,
FastJsonTest.class)這種方式,當程序執行到JavaBeanInfo.build() 方法時情景如下圖
此時的調用鏈如下圖
此時傳入JavaBeanInfo.build() 方法的參數值如下圖
二者執行到JavaBeanInfo.build() 方法時調用鏈對比如下
可見二者后面的調用鏈是完全一樣的。二者不同點在于調用JavaBeanInfo.build()
方法時傳入clazz參數的來源不同:
JSON.parseObject(jsonstr, FastJsonTest.class)在調用JavaBeanInfo.build()
方法時傳入的clazz參數源于parseObject方法中第二個參數中指定的“FastJsonTest.class”。
JSON.parse(jsonstr);這種方式調用JavaBeanInfo.build()
方法時傳入的clazz參數獲取于json字符串中\@type字段的值。
關于JSON.parse(jsonstr);從json字符串中\@type字段獲取clazz參數,具體代碼如下
程序通過解析傳入的json字符串的\@type字段值來獲取之后傳入JavaBeanInfo.build()
方法的clazz參數
因此,只要Json字符串的\@type字段值與JSON.parseObject(jsonstr,
FastJsonTest.class);中第二個參數中類名一致,見下圖
JSON.parse(jsonstr)與JSON.parseObject(jsonstr,FastJsonTest.class)這兩種方式執行的過程與結果是完全一致的。二者唯一的區別就是獲取clazz參數的途徑不同
> 使用JSON.parse(jsonstr)與JSON.parseObject(jsonstr, FastJsonTest.class)兩種方式時,被調用的getter與setter方法分別為setT1()、setT2()、setT5()、getT4()。FastJsonTest類中一共有五個getter方法,分別為getT1()、getT2()、getT3()、getT4()、getT5(),為什么僅僅getT4被調用了呢?
這個問題要從JavaBeanInfo.build() 方法中獲取答案:
通過上文的分析可以發現,程序會使用JavaBeanInfo.build()
方法對傳入的json字符串進行解析。在JavaBeanInfo.build()
方法中,程序將會創建一個fieldList數組來存放后續將要處理的目標類的 setter
方法及某些特定條件的 getter
方法。通過上文的結果可見,目標類中所有的setter方法都可以被調用,但只有getT4()這一個getter被調用,那么到底什么樣的getter
方法可以滿足要求并被加入fieldList數組中呢?
在JavaBeanInfo.build() 方法可見如下代碼
程序從clazz(目標類對象)中通過getMethods獲取本類以及父類或者父接口中所有的公共方法,接著進行循環判斷這些方法是否可以加入fieldList中以便后續處理
條件一、方法名需要長于4
條件二、不是靜態方法
條件三、以get字符串開頭,且第四個字符需要是大寫字母
條件四、方法不能有參數傳入
條件五、繼承自Collection \|\| Map \|\| AtomicBoolean \|\| AtomicInteger \|\|
AtomicLong
條件六、此getter不能有setter方法(程序會先將目標類中所有的setter加入fieldList列表,因此可以通過讀取fieldList列表來判斷此類中的getter方法有沒有setter)
JSON.parseObject(jsonstr)為什么返回值為JSONObject類對象,且將FastJsonTest類中的所有getter與setter都被調用了
通過上文的分析可以發現,JSON.parse(jsonstr)與JSON.parseObject(jsonstr,
FastJsonTest.class)兩種方式從執行流程幾乎一樣,結果也完全相同;然而使用JSON.parseObject(jsonstr)這種方式,執行的結果與返回值卻與前兩者不同:JSON.parseObject(jsonstr)返回值為JSONObject類對象,且將FastJsonTest類中的所有getter與setter都被調用。
通過閱讀源碼可以發現JSON.parseObject(String text)實現如下
parseObject(String text)其實就是執行了parse(),隨后將返回的Java對象通過JSON.toJSON()轉為
JSONObject對象。
JSON.toJSON()方法會將目標類中所有getter方法記錄下來,見下圖
隨后通過反射依次調用目標類中所有的getter方法
完整的調用鏈如下
總結:
上文例子中,JSON.parse(jsonstr)與JSON.parseObject(jsonstr, FastJsonTest.class)可以認為是完全一樣的,而parseObject(String text)是在二者的基礎上又執行了一次JSON.toJSON()
parse(String text)、parseObject(String text)與parseObject(String text, Class\ clazz)目標類Setter\\Getter調用情況
| | parse(String text) | parseObject(String text) | parseObject(String text, Class\ clazz) |
| -------------- | ------------------ | ------------------------ | ------------------------------------------ |
| Setter調用情況 | 全部 | 全部 | 全部 |
| Getter調用情況 | 部分 | 部分 | 全部 |
此外,如果目標類中私有變量沒有setter方法,但是在反序列化時仍想給這個變量賦值,則需要使用Feature.SupportNonPublicField參數。(在下文中,為TemplatesImpl類中無setter方法的私有變量_tfactory以及_name賦值運用到的就是這個知識點)
針對于上文的分析可以發現,無論使用哪種方式處理JSON字符串,都會有機會調用目標類中符合要求的Getter方法
如果一個類中的Getter方法滿足調用條件并且存在可利用點,那么這個攻擊鏈就產生了。
TemplatesImpl類恰好滿足這個要求:
com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl中存在一個名為_outputPropertiesget的私有變量,其getter方法中存在利用點,這個getter方法恰好滿足了調用條件,在JSON字符串被解析時可以調用其在調用FastJson.parseObject()序列化為Java對象時會被調用,下面我們詳細說明一下:
首先我們從漏洞點開始,一層層往入口分析:首先看一下TemplatesImpl類中的getTransletInstance方法
其中455行調用_class[_transletIndex]的newInstance( )方法來實例化對象的操作
我們看一下_class[_transletIndex]是如何獲取的,是否可以控制
\_class與_transletIndex值皆由451行處defineTransletClasses()方法中獲取
我們跟入defineTransletClasses()方法中一探究竟
在defineTransletClasses()方法中,首先在393行判斷_bytecodes值是否為空
值得注意的是,_bytecodes變量是TemplatesImpl類的成員變量
因此_bytecodes變量可以在構造json字符串時傳入,在構造poc時屬于可控變量
\_bytecodes變量非空值時,程序將會繼續執行至下圖紅框處
此時,需要滿足_tfactory變量不為null,否則導致程序異常退出。這就是為什么公開的poc中需要設置設置_tfactory為{
}的原因。因為_tfactory為私有變量,且無setter方法,這里需要指定Feature.SupportNonPublicField參數來為_tfactory賦值
接下來,程序將會把_bytecodes變量中的值循環取出并通過loader.defineClass處理后賦值給_class[i]
我們首先來看下loader.defineClass方法是什么
可見,loader.defineClass方法其實就是對ClassLoader.
defineClass的重寫。defineClass方法可以從傳入的字節碼轉化為Class
回頭分析下上述流程
\_bytecodes變量非空值時,程序將會把_bytecodes數組中的值循環取出,使用loader.defineClass方法從字節碼轉化為Class對象,隨后后賦值給_class[i]。
如果此時的class為main
class,_transletIndex變量值則會是此時_bytecodes數組中的下標值
因此當我們構造出_bytecodes:[evilCode]這樣的json字符串(evilCode字符串為我們構造的惡意類的字節碼)后,程序會將evilCode化為Class對象后賦值給_class[0]
現在回到getTransletInstance()方法中
此時的_class[_transletIndex]即為我們構造傳入的evilCode類
程序通過調用evilCode類的newInstance()方法來實例化對象的操作,這將導致我們構造的evilCode類中的惡意代碼被執行
但在此之前,需要在poc構造json字符串時使得成員變量_name不為空,否則程序還未執行到將evilCode類實例化就提前return
注意:由于私有變量_name沒有setter方法,在反序列化時想給這個變量賦值則需要使用Feature.SupportNonPublicField參數。
在分析完存在漏洞的getTransletInstance方法,我們需要找到一條調用鏈,這條調用鏈需要在使用fastjson處理json字符串時成功串連到存在漏洞的getTransletInstance方法上。
我們繼續向上跟蹤代碼
com/sun/org/apache/xalan/internal/xsltc/trax/TemplatesImpl.java
newTransformer()方法中調用了getTransletInstance()
繼續向上跟蹤
com/sun/org/apache/xalan/internal/xsltc/trax/TemplatesImpl.java
getOutputProperties()方法中調用了newTransformer()
getOutputProperties()方法為_outputProperties成員變量的getter方法
細心的讀者可能會發現,成員變量_outputProperties與其getter方法getOutputProperties()方法名字并不完全一致,多了一個下劃線,fastjson是如何將其對應的呢?
實際上,fastjson在解析的時候調用了一個smartMatch() 方法
在尋找_outputProperties的getter方法時,程序將下劃線置空,從而產生了成員變量_outputProperties與getter方法getOutputProperties()對應的形式
首先說TemplatesImpl類。經過上文分析可發現:TemplatesImpl中存在一個反序列化利用鏈,在反序列化過程中,如果該類的getOutputProperties()方法被調用,即可成功觸發代碼執行漏洞。
再來分析下FastJson:經過上文對FastJson三種不同途徑處理JSON字符串時關于getter方法被調用的條件來看,TemplatesImpl類_outputProperties成員變量的getter方法滿足被調用條件。無論通過fastjson哪種方式解析json字符串,都會觸發getOutputProperties()方法。
二者放在一起一拍即合:FastJson在反序列化TemplatesImpl類時會恰好觸發TemplatesImpl類的getOutputProperties()方法;TemplatesImpl類的getOutputProperties()方法被觸發就會引起反序列化代碼執行漏洞。所以說這個漏洞利用很是巧妙。
看完上述內容,你們掌握如何進行Fastjson 1.2.24反序列化漏洞深度分析的方法了嗎?如果還想學到更多技能或想了解更多相關內容,歡迎關注億速云行業資訊頻道,感謝各位的閱讀!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。