武永興,陳力波,姜開(kāi)達(dá)
(上海交通大學(xué)網(wǎng)絡(luò)空間安全學(xué)院,上海 200240)
2015年,Lawrence和Frohoff首先提出了Java反序列化漏洞。隨后,Breenmachine利用Java反序列化漏洞成功攻擊WebLogic、WebSphere、JBoss、Jenkins、OpenNMS等Java應(yīng)用,并實(shí)現(xiàn)了遠(yuǎn)程代碼執(zhí)行。Fastjson、Jackson、Hessian、XStream 等第三方反序列化庫(kù)被爆出存在反序列化漏洞,在反序列化攻擊者可控的數(shù)據(jù)時(shí)可能會(huì)執(zhí)行攻擊者構(gòu)造的惡意代碼。攻擊者通常需要通過(guò)組合應(yīng)用中存在的代碼片段,即gadgets,形成反序列化利用鏈,才能挾持反序列化器的執(zhí)行控制流,達(dá)到執(zhí)行對(duì)應(yīng)危險(xiǎn)操作的目的。因此,反序列化利用鏈?zhǔn)荍ava反序列化漏洞利用的關(guān)鍵,研究如何自動(dòng)化挖掘Java反序列化利用鏈至關(guān)重要。
序列化是指將Java對(duì)象轉(zhuǎn)換成字節(jié)序列、JSON字符串、XML字符串等,用于存儲(chǔ)、網(wǎng)絡(luò)傳輸?shù)?。反序列化是序列化的逆過(guò)程,將字節(jié)序列、JSON字符串、XML字符串等,轉(zhuǎn)換成Java對(duì)象。Java反序列化漏洞常見(jiàn)于RPC調(diào)用過(guò)程中,客戶端將參數(shù)對(duì)象序列化成字節(jié)碼,通過(guò)網(wǎng)絡(luò)傳輸給服務(wù)端,服務(wù)端將收到的數(shù)據(jù)反序列化成參數(shù)對(duì)象,然后調(diào)用相應(yīng)的處理函數(shù)。另一種常見(jiàn)的場(chǎng)景是對(duì)HTTP請(qǐng)求的參數(shù)進(jìn)行反序列化,將用戶傳遞的JSON/XML格式的數(shù)據(jù)轉(zhuǎn)換成Java對(duì)象。按照反序列化的機(jī)制,Java反序列化器可以分為兩類(lèi),一類(lèi)是基于bean,通過(guò)調(diào)用setter方法對(duì)field進(jìn)行賦值;另一類(lèi)是基于field,通過(guò)反射直接對(duì)field進(jìn)行賦值。常見(jiàn)的基于bean的反序列化器有:Fastjson和Jackson。常見(jiàn)的基于field的反序列化器包括:JND(java naive deserialization)、XStream、Hessian。攻擊者根據(jù)反序列化器的特點(diǎn),在類(lèi)路徑中搜索合適的gadget,將這些gadget組裝成反序列化利用鏈。通過(guò)反序列化利用鏈可以挾持程序的控制流,執(zhí)行危險(xiǎn)的代碼片段,達(dá)到執(zhí)行任意代碼的目的。由于反序列化是一項(xiàng)正常的功能,修復(fù)時(shí)不能簡(jiǎn)單地關(guān)閉反序列化功能。因此,針對(duì)反序列化漏洞,常見(jiàn)的修復(fù)手段是將已知的利用鏈加到黑名單中。換言之,如果攻擊者能夠找到新的利用鏈,那么就可以繼續(xù)利用反序列化漏洞進(jìn)行攻擊。
然而在反序列化漏洞利用鏈的搜索中面臨以下3個(gè)方面的困難。
首先,在反序列化的過(guò)程中用戶可以控制生成對(duì)象的類(lèi)型和屬性,field實(shí)際對(duì)應(yīng)的類(lèi)型可能是聲明類(lèi)型的任意子類(lèi),從而造成所調(diào)用的函數(shù)不確定,需要排查所有子類(lèi)情況。在確定被調(diào)函數(shù)的時(shí)候,可能要排查N倍的具體函數(shù)(N為實(shí)現(xiàn)方法的數(shù)量)。隨著調(diào)用鏈長(zhǎng)度的增長(zhǎng),要排查的函數(shù)數(shù)量將呈指數(shù)性增長(zhǎng),導(dǎo)致搜索空間爆炸。
其次,Java多態(tài)特性導(dǎo)致調(diào)用方法不確定,需要通過(guò)指針?lè)治鲇?jì)算其實(shí)際運(yùn)行中對(duì)應(yīng)的類(lèi)型。但是,指針?lè)治鲂枰拇罅坑?jì)算資源。
最后,當(dāng)前整個(gè)JDK的代碼量已然十分龐大,以Java(1.8.0_171)為例,共有38 466個(gè)類(lèi)和32 4595個(gè)方法。龐大的代碼數(shù)量,給自動(dòng)化分析帶來(lái)巨大挑戰(zhàn)。
與Java反序列化漏洞相關(guān)的工具大多是payload生成工具,如Marshalsec和Ysoserial,這些工具將已知的利用鏈封裝成payload,但不具備挖掘未知利用鏈的能力;同時(shí),有一部分工業(yè)界的工具,如GadgetInspector,提供了自動(dòng)化搜索gadget的能力,但其沒(méi)有進(jìn)行指針?lè)治?,?dǎo)致誤報(bào)率和漏報(bào)率都很高。當(dāng)前學(xué)術(shù)界大多數(shù)的工作是關(guān)于如何檢測(cè)注入類(lèi)漏洞,對(duì)于自動(dòng)化挖掘Java反序列化利用鏈沒(méi)有探索過(guò)。
綜上所述,針對(duì)序列化利用鏈搜索的空間十分大,人工審計(jì)難以覆蓋的現(xiàn)狀,提出一種能夠協(xié)助安全研究人員自動(dòng)化挖掘利用鏈的方法是相當(dāng)有必要的。針對(duì)該問(wèn)題,本文主要貢獻(xiàn)如下。
1) 提出了基于混合分析的Java反序列化利用鏈挖掘方法,設(shè)計(jì)并實(shí)現(xiàn)了在字節(jié)碼級(jí)別進(jìn)行自動(dòng)化分析挖掘Java反序列化利用鏈工具——GadgetSearch。
2) 實(shí)驗(yàn)表明GadgetSearch的誤報(bào)率和漏報(bào)率遠(yuǎn)小于現(xiàn)有的工具GadgetInspector,并且利用GadgetSearch挖掘出1條Jackson未公開(kāi)利用鏈、4條XStream未公開(kāi)的JDK利用鏈和1條Hessian未公開(kāi)的JDK利用鏈,申請(qǐng)分配了5個(gè)CVE(CVE-2021-39141、CVE-2021-39144、 CVE-2021- 39146、CVE-2021-39153、CVE-2021-43297)和1個(gè)CNVD編號(hào)(CNVD-2021-44381)。
指針?lè)治觯╬ointer analysis)[1-4]是計(jì)算程序變量在運(yùn)行期間可能指向的對(duì)象集合。Java多態(tài)的性質(zhì),需要根據(jù)變量運(yùn)行時(shí)指向?qū)ο蟮念?lèi)型解析被調(diào)用函數(shù),所以指針?lè)治鍪瞧渌治龅幕A(chǔ)。Bravenboer[5]等提出了一種聲明式指針?lè)治隹蚣蹹oop,他們通過(guò)在Datalog中描述完整的端到端分析,并使用專(zhuān)門(mén)針對(duì)遞歸的Datalog程序進(jìn)行優(yōu)化,推動(dòng)了聲明式指針?lè)治龅陌l(fā)展。Antoniadis[6]等將Doop中的Datalog推理引擎從LogicBlox換成了Souffle ,Souffle支持通過(guò)共享內(nèi)存并行分析,切換推理引擎之后,Doop整體提速 4倍。
污點(diǎn)分析(taint flow analysis)[7-11]是信息流分析(information flow analysis)的一種。Livshits[12]等探索使用Datalog進(jìn)行污點(diǎn)分析,但沒(méi)有將該污點(diǎn)分析與指針?lè)治鼋y(tǒng)一起來(lái)。Tripp[13]等認(rèn)為污點(diǎn)分析是一個(gè)需求驅(qū)動(dòng)問(wèn)題,不需要進(jìn)行完整的全程序分析,他們提出了一套名為Andromeda的工具,利用數(shù)據(jù)流方程和污點(diǎn)集進(jìn)行數(shù)據(jù)流分析。FlowDroid[14]是一種Android應(yīng)用污點(diǎn)分析框架,可以進(jìn)行流敏感(flow-sensitive)的污點(diǎn)分析。FlowDroid對(duì)常見(jiàn)的Android特性(如回調(diào)等)進(jìn)行了建模,并且手工篩選出了700多條Source、Sink以及轉(zhuǎn)移函數(shù)。這些轉(zhuǎn)移函數(shù)包含了常見(jiàn)數(shù)據(jù)結(jié)構(gòu)的操作。但是FlowDroid沒(méi)有將信息流分析和調(diào)用圖構(gòu)造統(tǒng)一起來(lái)。Pidgin[15]通過(guò)程序依賴圖(program dependence graph)的方式實(shí)現(xiàn)了信息流分析。程序依賴圖可以被用來(lái)模擬任何形式的數(shù)據(jù)以及指令之間的控制依賴。DroidInfer[16]使用約束流圖的可達(dá)性算法進(jìn)行污點(diǎn)分析,它依靠WALA8在進(jìn)行主要分析之前產(chǎn)生一個(gè)控制流圖。
Grech等提出了P/Taint[17],將指針?lè)治龊臀埸c(diǎn)分析結(jié)合起來(lái),他們通過(guò)很小的改動(dòng)把信息流分析整合到了Doop指針?lè)治隹蚣堋Mㄟ^(guò)擴(kuò)充堆對(duì)象的定義域來(lái)實(shí)現(xiàn)這一目標(biāo),并增加污點(diǎn)分析的內(nèi)容。程序分析通常將污點(diǎn)分析和指針?lè)治鲆暈椴煌倪^(guò)程,但該項(xiàng)工作表明,同樣的算法可以同時(shí)計(jì)算污點(diǎn)信息和指針信息這兩種相互關(guān)聯(lián)但獨(dú)立的信息。同時(shí),由于反序列化漏洞的污點(diǎn)源不是來(lái)自Source函數(shù),而是來(lái)自隱式創(chuàng)建的對(duì)象,無(wú)法直接利用Doop的信息流分析功能來(lái)跟蹤反序列化的污點(diǎn)數(shù)據(jù)流。
Dahse[18]等介紹了PHP反序列化漏洞,他們提出了一個(gè)過(guò)程間字段敏感和對(duì)象敏感的數(shù)據(jù)流分析方法,利用該方法自動(dòng)化挖掘PHP反序列化利用鏈。Shcherbakov[19]等研究了.NET中的反序列化漏洞。他們先將.NET字節(jié)碼轉(zhuǎn)換成.NET通用的中間語(yǔ)言;然后實(shí)現(xiàn)了一個(gè)高效、可擴(kuò)展的過(guò)程間污點(diǎn)分析;最后基于該方法實(shí)現(xiàn)了自動(dòng)化挖掘.NET利用鏈工具SerialDetector。由于他們的方法只針對(duì)PHP和.NET這兩種特定語(yǔ)言,無(wú)法直接應(yīng)用到Java反序列化利用鏈的挖掘。
JackOfMostTrades在BlackHat上介紹了一款挖掘利用鏈的工具 GadgetInspector[20]。GadgetInspector利用ASM庫(kù)解析字節(jié)碼,通過(guò)模擬操作棧實(shí)現(xiàn)污點(diǎn)分析,支持JND、XStream,Jackson這3種反序列化利用鏈的挖掘。杜笑宇等[21]將GadgetInspector中的廣度優(yōu)先搜索算法改造成了深度優(yōu)先算法,找到了一些新的利用鏈。Threedr3am對(duì)GadgetInspector進(jìn)行了改造,增加了對(duì) Fastjson和 Hessian的支持。但是GadgetInspector構(gòu)造調(diào)用圖時(shí)沒(méi)有進(jìn)行指針?lè)治?,?dǎo)致調(diào)用圖不準(zhǔn)確,包含大量不存在的調(diào)用關(guān)系,誤報(bào)率和漏報(bào)率非常高。
通過(guò)分析Java反序列化器的實(shí)現(xiàn)可知,Java反序列化漏洞能被利用的原因包含以下3點(diǎn)。反序列化的數(shù)據(jù)用戶可控,用戶能夠控制對(duì)象的類(lèi)型以及其屬性值;反序列化器在反序列化的時(shí)候會(huì)自動(dòng)調(diào)用入口函數(shù);類(lèi)路徑(包括JDK和第三方庫(kù))中存在合適的gadget,通過(guò)構(gòu)造將gadget串成一條鏈,從自動(dòng)調(diào)用的入口函數(shù)開(kāi)始鏈?zhǔn)秸{(diào)用到危險(xiǎn)函數(shù)。
從上述原因中,可以提取出反序列化漏洞利用環(huán)節(jié)的三要素。污染源:攻擊者可控的變量;入口函數(shù):反序列化時(shí)自動(dòng)調(diào)用的接口函數(shù);危險(xiǎn)函數(shù):能夠執(zhí)行命令的函數(shù)。
因此,挖掘反序列化利用鏈?zhǔn)紫刃枰鶕?jù)反序列化器的實(shí)現(xiàn),分析出入口函數(shù)和污點(diǎn)源;然后從入口函數(shù)開(kāi)始,根據(jù)聲明類(lèi)型初始化污點(diǎn)源,跟蹤污點(diǎn)傳播。同時(shí)在追蹤的過(guò)程中,會(huì)遇到新的函數(shù)調(diào)用,這時(shí)通過(guò)指針?lè)治觯馕銎湔鎸?shí)的調(diào)用,進(jìn)行過(guò)程間污點(diǎn)傳播分析,判斷污點(diǎn)數(shù)據(jù)能否傳播到危險(xiǎn)函數(shù)。
下面將根據(jù)圖1所示的反序列化利用的示例,解釋尋找Java反序列化利用鏈的詳細(xì)過(guò)程。
圖1 反序列化利用鏈?zhǔn)纠?Figure1 Example of a gadget chain
其中,hashCode是常見(jiàn)的入口函數(shù),本文從類(lèi)A的hashCode函數(shù)開(kāi)始分析調(diào)用路徑。A#hashCode先調(diào)用A#getInterfaceC,然后調(diào)用InterfaceC#getHashCode。變量interfaceC聲明類(lèi)型是InterfaceC接口類(lèi),但是由于Java多態(tài)的性質(zhì),根據(jù)聲明類(lèi)型InterfaceC,無(wú)法判斷出interfaceC.getHashCode在運(yùn)行時(shí)調(diào)用的是CImpl#getHashCode還是CImplOther# getHash-Code。因此,為了得到準(zhǔn)確的調(diào)用圖,需要進(jìn)行指針?lè)治?,?jì)算變量interfaceC在實(shí)際運(yùn)行過(guò)程中可能指向的對(duì)象類(lèi)型,再根據(jù)指針信息解析實(shí)際調(diào)用。在圖1所示的例子中,通過(guò)指針?lè)治?,可以?jì)算出變量interfaceC在運(yùn)行過(guò)程中指向CImpl對(duì)象,因此interfaceC.getHashCode對(duì)應(yīng)的實(shí)際調(diào)用方法是 CImpl#getHashCode。在CImpl#getHashCode中調(diào)用了interB.get,由于變量interB也是接口類(lèi)型,所以需要指針?lè)治鲇?jì)算其運(yùn)行時(shí)可能指向的對(duì)象類(lèi)型。變量interB來(lái)自變量iobj,iobj為A的field。同時(shí)由于通過(guò)反序列化創(chuàng)建的A類(lèi)型對(duì)象的iobj field不像變量interfaceC有顯式的創(chuàng)建對(duì)象代碼,所以無(wú)法通過(guò)常規(guī)的指針?lè)治鲇?jì)算interB的指針信息。在反序列化的過(guò)程中能夠控制類(lèi)型和屬性值,所以可以控制iobj為InterfaceB的所有實(shí)現(xiàn)類(lèi),即圖1示例中的BImpl或者BImplOther。因此,interB.get可能會(huì)調(diào)用BImpl#get或者BImplOther#get,需要遍歷追蹤這兩個(gè)接口,發(fā)現(xiàn)BImplOther#get不會(huì)調(diào)用危險(xiǎn)函數(shù),但這個(gè)分支屬于不得不排查的無(wú)效分支;接下來(lái),在BImpl#get分支中調(diào)用了method#invoke,其中clazz和methodName都是field,在反序列化時(shí)對(duì)應(yīng)的值是攻擊者可控的,因此變量method也是攻擊者可控的,所以該分支最終可以調(diào)用method.invoke危險(xiǎn)函數(shù),而且變量method和obj都是攻擊者可控的。至此,本文找到了一條以A#hashCode為起點(diǎn),Method#invoke為終點(diǎn)的反序列化利用鏈。完整的調(diào)用鏈如圖1中的紅色箭頭所示:A#hashFigure→ CImpl# getHashFigure→ BImpl#get → Method# invoke。
過(guò)程間的污點(diǎn)傳播鏈為圖1中藍(lán)色箭頭所示:A#hashCode@iobj → CImpl#getHashCode@ interB → BImpl#get@this。
從上面示例中可以看出,尋找利用鏈過(guò)程的本質(zhì)是污點(diǎn)分析:分析污點(diǎn)源能否從入口函數(shù)傳播到危險(xiǎn)函數(shù)。同時(shí),過(guò)程間的污點(diǎn)傳播需要準(zhǔn)確的函數(shù)調(diào)用圖,但Java多態(tài)導(dǎo)致不能只根據(jù)聲明類(lèi)型確定調(diào)用關(guān)系。所以需要根據(jù)指針?lè)治龅慕Y(jié)果解析準(zhǔn)確的函數(shù)調(diào)用。反序列化的過(guò)程中會(huì)隱式創(chuàng)建對(duì)象,而且創(chuàng)建的對(duì)象可能不唯一,可以是聲明類(lèi)型的所有實(shí)現(xiàn)子類(lèi),導(dǎo)致傳統(tǒng)的指針?lè)治鰺o(wú)法直接應(yīng)用在這項(xiàng)任務(wù)中,需要進(jìn)一步遍歷可能的所有接口。但是反序列化入口函數(shù)多,類(lèi)路徑代碼量大,反序列化時(shí)創(chuàng)建的field可能是聲明類(lèi)型所有子類(lèi)對(duì)象,這些因素導(dǎo)致直接從所有入口函數(shù)進(jìn)行指針?lè)治?,?jì)算量非常大,在有限的時(shí)間和受限的內(nèi)存情況下無(wú)法得出指針?lè)治龅慕Y(jié)果。
另外,如果只通過(guò)聲明類(lèi)型構(gòu)造簡(jiǎn)單調(diào)用圖,然后在調(diào)用圖中尋找從入口函數(shù)到危險(xiǎn)函數(shù)的調(diào)用鏈作為反序列化利用鏈。這種做法將存在大量的誤報(bào):一是因?yàn)楦鶕?jù)聲明類(lèi)型構(gòu)造的調(diào)用圖不準(zhǔn)確,調(diào)用關(guān)系在實(shí)際運(yùn)行的過(guò)程中可能不存在;二是即使調(diào)用鏈存在,如果污點(diǎn)源無(wú)法傳播到危險(xiǎn)函數(shù),調(diào)用鏈也將無(wú)法利用。
綜上所述,雖然通過(guò)調(diào)用圖分析挖掘反序列化利用鏈的速度快,但調(diào)用圖不準(zhǔn)確導(dǎo)致存在大量的誤報(bào);同時(shí)為了構(gòu)造更準(zhǔn)確的調(diào)用圖用于過(guò)程間信息流分析,需要進(jìn)行指針?lè)治?。引入指針?lè)治龊?,?zhǔn)確的調(diào)用圖讓污點(diǎn)傳播更準(zhǔn)確,但指針?lè)治鲇?jì)算復(fù)雜導(dǎo)致效率較低。
因此,為了準(zhǔn)確且高效地挖掘反序列化利用鏈,本文提出了一種基于混合分析的Java反序列化利用鏈挖掘方法。首先,通過(guò)調(diào)用圖分析找出可能到達(dá)危險(xiǎn)函數(shù)的入口函數(shù);然后,利用篩選出的入口函數(shù)作為混合信息流分析的入口,構(gòu)建包含指針信息和污點(diǎn)信息流向的混合信息流圖;最后,基于混合信息流圖判斷從入口函數(shù)開(kāi)始的污點(diǎn)是否能傳播到危險(xiǎn)函數(shù),從而高效查找實(shí)際可行的反序列化利用鏈。
判斷一條利用鏈?zhǔn)欠裼行У某浞直匾獥l件是:攻擊者控制的污點(diǎn)輸入能否從利用鏈的入口函數(shù)傳播到利用鏈的敏感危險(xiǎn)函數(shù)。如果利用鏈有效,一定存在從入口函數(shù)到危險(xiǎn)函數(shù)的調(diào)用路徑。因此,有效的反序列化利用鏈一定在通過(guò)調(diào)用圖分析找到的調(diào)用路徑集合中,可以通過(guò)調(diào)用圖分析進(jìn)行篩選,得到可能到達(dá)危險(xiǎn)函數(shù)的入口點(diǎn)集合。
進(jìn)行調(diào)用圖分析首先需要構(gòu)造調(diào)用圖,調(diào)用圖中的節(jié)點(diǎn)是方法,邊為調(diào)用關(guān)系。Java中的函數(shù)調(diào)用分為3類(lèi):靜態(tài)方法調(diào)用、特殊方法調(diào)用、虛擬方法調(diào)用,其中特殊方法調(diào)用包括私有方法調(diào)用、超級(jí)方法調(diào)用和構(gòu)造方法調(diào)用。靜態(tài)方法調(diào)用和特殊方法調(diào)用,不需要根據(jù)指針信息進(jìn)行解析,能夠根據(jù)聲明類(lèi)型確定被調(diào)用函數(shù)。由于Java多態(tài)的特性,虛擬方法不能根據(jù)聲明類(lèi)型確定被調(diào)函數(shù)。如何解析虛擬方法調(diào)用的被調(diào)函數(shù)是調(diào)用圖分析面臨的主要難題。為了防止調(diào)用圖不全導(dǎo)致的漏報(bào),本文假設(shè)變量在實(shí)際運(yùn)行時(shí),指向聲明類(lèi)型所有的子類(lèi)。按照這種策略,在圖1所示的代碼中變量interfaceC可能指向CImpl或者CImplOther,調(diào)用interfaceC.getHashCode被解析成CImpl#getHashCode或者CImplOther# getHashCode;同理,調(diào)用interB.get被解析成BImpl#get或者BImplOther#get。最終調(diào)用圖如圖2所示,構(gòu)造好調(diào)用圖之后,根據(jù)調(diào)用圖從危險(xiǎn)函數(shù)開(kāi)始回溯,尋找能夠到達(dá)危險(xiǎn)函數(shù)的入口函數(shù)。圖2中能夠到達(dá)危險(xiǎn)函數(shù)的入口函數(shù)為A#hashCode,另外一個(gè)入口函數(shù)AOther# hash-Code無(wú)法到達(dá)危險(xiǎn)函數(shù)。在后續(xù)污點(diǎn)分析時(shí),將污點(diǎn)的入口函數(shù)設(shè)置為A#hashCode即可。入口函數(shù)到危險(xiǎn)函數(shù)的調(diào)用路徑有兩條分別是:A#hashCode→ CImpl#getHashCode→ BImpl#get →Method#invoke 和 A#hashcode→ CImplOther#getHashCode→Method#invoke。第一條是有效的利用鏈,第二條是調(diào)用圖不準(zhǔn)確導(dǎo)致的誤報(bào),即圖2中紅色邊表示的調(diào)用圖中覆蓋但程序運(yùn)行時(shí)不存在的調(diào)用。
圖2 調(diào)用圖 Figure 2 Call graph
需要指出的是,在實(shí)際的Java應(yīng)用分析中,由于代碼量過(guò)大,為了能在有限的時(shí)間內(nèi)得到簡(jiǎn)單調(diào)用圖的分析結(jié)果,在構(gòu)造調(diào)用圖時(shí)增加了如下兩類(lèi)約束:一是通過(guò)設(shè)置參數(shù)MAXSTEPS,限制從入口函數(shù)出發(fā)最大的調(diào)用長(zhǎng)度,當(dāng)達(dá)到最大的調(diào)用長(zhǎng)度之后,就不再繼續(xù)解析新的調(diào)用;二是由于JDK中的java.util.*內(nèi)部調(diào)用相對(duì)復(fù)雜,而且對(duì)尋找利用鏈幫助不大,所以限制以java.util開(kāi)頭的類(lèi)的方法,不允許再調(diào)用其內(nèi)部函數(shù)。
通過(guò)調(diào)用圖分析,能夠找到入口函數(shù)到達(dá)危險(xiǎn)函數(shù)的路徑集合。但是調(diào)用關(guān)系在程序?qū)嶋H運(yùn)行中是否真實(shí)存在,還需要指針?lè)治鲞M(jìn)一步驗(yàn)證。
下面根據(jù)圖1的信息流分析示例介紹面向指針?lè)治龅男畔⒘鲌D構(gòu)造。
(1)指針信息初始化
傳統(tǒng)指針?lè)治鲋械闹羔樞畔⒊跏蓟僮魇莕ew語(yǔ)句,如圖3中代碼第9行b3=new B(),通過(guò)new創(chuàng)建的對(duì)象為OB9,賦值操作將對(duì)象OB9的信息傳播到變量b3,信息流圖中OB9指向b3。
圖3 信息流分析示例 Figure 3 Example code for information flow analysis
而在反序列化的過(guò)程中,程序會(huì)隱式創(chuàng)建對(duì)象,需要在信息流圖中加上隱式對(duì)象流向邊。隱式創(chuàng)建的對(duì)象是聲明類(lèi)型的所有實(shí)現(xiàn)子類(lèi)。圖3中,反序列化時(shí)創(chuàng)建的隱式對(duì)象設(shè)為OA0,OA0被賦值給A#entry@this,在信息流圖中OA0指向A#entry@this。實(shí)際上反序列化時(shí)也會(huì)隱式對(duì)field創(chuàng)建對(duì)象,但靜態(tài)分析時(shí)沒(méi)有必要對(duì)所有field創(chuàng)建對(duì)象,可以在加載field時(shí)再實(shí)例化field對(duì)象,這樣就可以做到只對(duì)用到的field才實(shí)例化,而且利用該規(guī)則可以對(duì)field對(duì)象的field進(jìn)行實(shí)例化。在第7和12行中,加載了OA0.fldb1,OA0.fldstr,此時(shí)才創(chuàng)建隱式對(duì)象OB0、OString0,并在信息流圖中將其指向OA0.fldb1、OA0.fldstr。
(2)過(guò)程內(nèi)信息流圖構(gòu)造
在Java中過(guò)程內(nèi)變量之間的信息傳播有Assign、Load、Store這3種語(yǔ)句。
Assign表示賦值,如圖3中的第8行,變量b1包含的信息將傳播給變量b2,信息流圖中b1指向b2。
Load表示獲取一個(gè)對(duì)象的filed值,如第7行,對(duì)象OA0流向變量A#entry@this,所以O(shè)A0.fldb1包含的信息傳播給 b1,OA0.fldb1指向b1。
Store表示對(duì)一個(gè)對(duì)象的field進(jìn)行賦值,第10行,對(duì)象OA0流向變量A#entry@this,所以是b3包含的信息傳播給OA0.fldb2,b3指向OA0.fldb2。
(3)過(guò)程間信息流構(gòu)造
Call表示函數(shù)調(diào)用,涉及過(guò)程間變量信息傳播。例如圖3中第11行,遇到函數(shù)調(diào)用時(shí),需要根據(jù)變量指針信息解析真實(shí)的調(diào)用。因?yàn)樾畔⒘鲌D中對(duì)象OA0流向變量A#entry@this,因此變量A#entry@this運(yùn)行中指向OA0對(duì)象,OA0類(lèi)型為A,因此,this.func2實(shí)際調(diào)用的是A#func2。函數(shù)調(diào)用涉及3種變量傳播:實(shí)際參數(shù)向形式參數(shù)傳播,比如b1向paramb傳播;函數(shù)內(nèi)返回值向函數(shù)調(diào)用返回值傳播,如ret向b4傳播;對(duì)象向調(diào)用方法的this傳播,如A#entry@this向A#func2@this傳播。因此,在控制流圖中b1指向paramb,ret指向b4,A#entry@this指向A#func2@this。
雖然通過(guò)指針?lè)治瞿軌虻玫綔?zhǔn)確的調(diào)用圖,但準(zhǔn)確的調(diào)用關(guān)系還不足以判斷一條調(diào)用鏈?zhǔn)欠裾鎸?shí)有效,還需要通過(guò)污點(diǎn)分析判斷外部污點(diǎn)數(shù)據(jù)能否傳播到危險(xiǎn)函數(shù)。
表面上,污點(diǎn)分析和指針?lè)治鰝鞑ゲ煌男畔ⅲ褐羔樂(lè)治鍪窃谧兞恐g傳遞指針指向的對(duì)象集合;污點(diǎn)分析在變量傳遞內(nèi)容時(shí)根據(jù)污點(diǎn)傳播策略傳遞變量對(duì)應(yīng)的污點(diǎn)屬性。但是,污點(diǎn)分析和指針?lè)治霰举|(zhì)上均為信息流分析,變量之間傳播的實(shí)際均為對(duì)象信息??梢愿鶕?jù)指針變量指向的對(duì)象是不是污點(diǎn),判斷當(dāng)前指針是否需要標(biāo)記污點(diǎn)屬性,從而將兩者合并成同一個(gè)過(guò)程。例如,在圖4中,對(duì)象OB0在信息流圖上能夠傳播到變量b1,所以b1指向?qū)ο驩B0。如果OB0含有污點(diǎn)標(biāo)記,那么就可以認(rèn)為指針變量b1是污點(diǎn)。
基于污點(diǎn)分析等價(jià)于傳播帶污點(diǎn)標(biāo)記的對(duì)象,本文提出了混合分析——即同時(shí)進(jìn)行指針?lè)治龊臀埸c(diǎn)分析。具體地說(shuō),在對(duì)象初始化時(shí)打上是否為污點(diǎn)的標(biāo)記,即可在指針?lè)治龅耐瑫r(shí)進(jìn)行污點(diǎn)分析。相較于傳統(tǒng)的指針?lè)治觯疚母鶕?jù)反序列應(yīng)用的場(chǎng)景,需要針對(duì)性地增加規(guī)則來(lái)完成混合分析,其主要包含污點(diǎn)源初始化、污點(diǎn)轉(zhuǎn)移規(guī)則。
(1)污點(diǎn)源初始化
污點(diǎn)信息的初始化是將反序列化時(shí)隱式創(chuàng)建的對(duì)象作為污點(diǎn)源,打上污點(diǎn)標(biāo)記。OA0是污點(diǎn)源,將其標(biāo)記為圖4中的紅框。
(2)污點(diǎn)轉(zhuǎn)移規(guī)則
污點(diǎn)轉(zhuǎn)移規(guī)則包含3種。第一種是當(dāng)加載污點(diǎn)對(duì)象的field時(shí),如z=x.f,如果x是污點(diǎn),那么z也是污點(diǎn)。因?yàn)镺A0是污點(diǎn)源,OB0和OString0是加載OA0field時(shí)隱式創(chuàng)建的對(duì)象,所以O(shè)B0和OString0也是污點(diǎn)源。第二種是當(dāng)調(diào)用函數(shù)的對(duì)象是污點(diǎn)時(shí),如z = x.func(),如果x是污點(diǎn)那么認(rèn)為 z也是污點(diǎn)。在圖 3中有語(yǔ)句str2=str1.substring(1), 所以str1有條指向str2的污點(diǎn)轉(zhuǎn)移流向的邊。第三種是當(dāng)調(diào)用函數(shù)的參數(shù)是污點(diǎn)時(shí),如z=x.func(y),如果y是污點(diǎn),那么z也是污點(diǎn)。在實(shí)際測(cè)試中發(fā)現(xiàn)第二種規(guī)則會(huì)帶來(lái)誤報(bào),如this.getClass().getClassLoader()這種情況,由于this是污點(diǎn)數(shù)據(jù),getClass()返回的也是污點(diǎn)數(shù)據(jù),所以getClassLoader()返回的也是污點(diǎn)數(shù)據(jù),但實(shí)際情況下getClassLoader()返回值的攻擊者并不可控。類(lèi)似還有this.set.Iterator(),this.map.keys()等的返回值攻擊者都不可控。本文通過(guò)限制返回值的類(lèi)型,阻止這種傳播,減少誤報(bào)。
因此,綜合利用污點(diǎn)分析和指針?lè)治鰞煞矫娴男畔⒘鱾鞑ヒ?guī)則,得到圖3對(duì)應(yīng)的混合信息流圖,如圖4所示?;旌闲畔⒘鲌D節(jié)點(diǎn)中包括變量節(jié)點(diǎn)(藍(lán)框表示),隱式對(duì)象節(jié)點(diǎn)(紅框表示),顯式對(duì)象節(jié)點(diǎn)(黑框表示),對(duì)象field 節(jié)點(diǎn)(綠框表示)。信息流圖邊包括隱式對(duì)象流向、顯式對(duì)象流向、信息流向、污點(diǎn)轉(zhuǎn)移流向。
車(chē)地?zé)o線通信采用成熟的LTE技術(shù)。該技術(shù)具備高可靠的抗干擾能力,可滿足互聯(lián)互通CBTC系統(tǒng)車(chē)地之間數(shù)據(jù)在高速移動(dòng)環(huán)境下的穩(wěn)定傳輸[7]。同時(shí),針對(duì)空口消息的偽裝風(fēng)險(xiǎn),可采用安全加密技術(shù)防護(hù),將其直接部署在TAU(車(chē)載終端)和BBU(軌旁基帶單元)上來(lái)實(shí)現(xiàn)鑒權(quán)和加密機(jī)制,保障車(chē)地?zé)o線通信的信息安全。安全加密技術(shù)采用滿足LTE國(guó)際加密標(biāo)準(zhǔn)的國(guó)密算法——祖沖之(ZUC)算法。
圖4 混合信息流圖 Figure 4 Hybrid information flow graph
根據(jù)指針?lè)治?,變量b1、b2、paramb、ret、b4指向?qū)ο驩B0,變量str1、str2指向?qū)ο驩String0。由于對(duì)象OB0和OString0都帶污點(diǎn)標(biāo)記所以變量b1、b2、paramb、ret、b4、str1、str2都是污點(diǎn)。
綜上所述,本文提出的混合分析方法先通過(guò)調(diào)用圖分析篩選出可能達(dá)到危險(xiǎn)函數(shù)的入口函數(shù);然后,再以這些入口函數(shù)作為入口進(jìn)行同時(shí)面向指針和污點(diǎn)變量的混合信息流分析,最后基于信息流分析找出從入口函數(shù)開(kāi)始污點(diǎn)源流向危險(xiǎn)函數(shù)的路徑。
混合分析示意如圖5所示。其中A函數(shù)是調(diào)用圖分析提取的潛在利用鏈入口函數(shù)。將A函數(shù)作為混合信息流分析的起點(diǎn),通過(guò)傳播污點(diǎn)標(biāo)記的對(duì)象,進(jìn)行指針?lè)治龊臀埸c(diǎn)分析。
圖5 混合分析示意 Figure 5 Hybrid analysis method diagram
基于上述方法,本文實(shí)現(xiàn)了一套自動(dòng)化挖掘Java反序列化利用鏈的原型系統(tǒng)——Gadget Search,GadgetSearch支持挖掘Jackson、Fastjson、XStream、JND、Hessian 5種常見(jiàn)反序列化器的利用鏈。
GadgetSearch首先利用Soot解析Java字節(jié)碼,將字節(jié)碼轉(zhuǎn)化中間語(yǔ)言Jimple。然后,從Jimple中解析出類(lèi)的基本信息,如類(lèi)修飾符、父類(lèi)、field、方法等以及方法中的操作指令new、Assign、Load、Store、Call,將這些信息保存成facts。接著用Datalog語(yǔ)法表達(dá)第4節(jié)描述的規(guī)則,用Souffle執(zhí)行Datalog文件進(jìn)行混合分析。分析完成后產(chǎn)生兩個(gè)主要文件:Nodes.csv和Calls.csv。Nodes.csv記錄各個(gè)Method,有3類(lèi)標(biāo)簽,分別是EntryPoint、Method和SinkMethod;Calls.csv記錄方法之間的調(diào)用關(guān)系。最后,通過(guò)Neo4j-admin 將Nodes.csv和Calls.csv文件導(dǎo)入Neo4j 數(shù)據(jù)庫(kù),并通過(guò)Cypher語(yǔ)句查詢從EntryPoint到SinkMethod的路徑,對(duì)方法調(diào)用路徑可視化,方便安全排查調(diào)用鏈?zhǔn)欠窨衫谩?/p>
為了評(píng)估本文提出混合分析方法的有效性,需要設(shè)計(jì)實(shí)驗(yàn)回答下面3個(gè)問(wèn)題。
RQ1:GadgetSearch能否挖掘到未公開(kāi)的利用鏈?
RQ3:如何評(píng)估本文提出的混合分析方法的必要性?
測(cè)試的實(shí)驗(yàn)環(huán)境為 Ubuntu16.04,Java 1.8.0_171, 內(nèi)存36 GB,24核Intel(R) Xeon(R) CPU E5-2630處理器。
為了驗(yàn)證GadgetSearch能夠挖掘未知利用鏈,本文對(duì)JDK和一些流行的Jar包進(jìn)行了測(cè)試。最終發(fā)現(xiàn)5條未公開(kāi)利用鏈,具體數(shù)據(jù)如表1所示。其中CVE-2021-43297是第一條僅依賴JDK就能夠命令執(zhí)行的Hessian利用鏈。換句話說(shuō),只要存在Hessian反序列化漏洞,攻擊者無(wú)須關(guān)心類(lèi)路徑包含哪些第三方庫(kù),直接利用這條鏈即可攻擊,危害十分嚴(yán)重。本文成功利用該鏈攻擊了Dubbo。
表1 未公開(kāi)利用鏈列表Table 1 List of CVE/CNVD
挖掘Java反序列化的工具有Jack Of-MostTrades版本的 GadgetInspector(記為JGadgetInspector),以及Threedr3am 改進(jìn)后的GadgetInspector(記為 TGadgetInspector) 。JGadgetInspector支持檢測(cè)Jackson、XStream、JND這3種反序列化利用鏈。GadgetSearch和TGadgetInspector都支持檢測(cè)Jackson、Fastjson、XStream、JND、Hessian這5種反序列化利用鏈。
為了只找出能夠命令執(zhí)行的利用鏈,本文去掉了JGadgetInpsector和TGadgetInspector中不能夠造成命令執(zhí)行的危險(xiǎn)函數(shù)(如ssrf,文件讀寫(xiě)等)。TGadgetInspector檢測(cè)Fastjson利用鏈時(shí),其為了檢測(cè)出新的利用鏈,將已知的利用鏈加入了黑名單,對(duì)黑名單中的鏈不再檢測(cè)。為了實(shí)驗(yàn)的公平性,本文將TGadgetInspector中Fastjson的黑名單置為空。
為了對(duì)比GadgetSearch和JGadget Inspector,TGadgetInspector檢測(cè)反序列化利用鏈的誤報(bào)率、漏報(bào)率以及運(yùn)行效率,本文在Marshalsec,Ysoserial、Jackson CVE、XStream CVE數(shù)據(jù)集上進(jìn)行測(cè)試。Marshalsec和Ysoserial是兩個(gè)業(yè)界較出名的Java反序列化利用鏈Payload生成工具,包含一些已知的反序列化利用鏈。Marshalsec包含Jackson、XStream、Hessian 3種反序列化利用鏈,Ysoserial只包含JND反序列化利用鏈,數(shù)據(jù)集詳情如表2所示。Jackson和XStream官方會(huì)為反序列化利用鏈申請(qǐng)CVE,所以本文從Jackson歷史CVE中篩選出15個(gè)能夠命令執(zhí)行的CVE,從XStream歷史CVE中篩選出14個(gè)能夠命令執(zhí)行的CVE作為另外兩組數(shù)據(jù)集。
表2 Marshalsec 數(shù)據(jù)集Table 2 Marshalsec dataset
Jackson檢測(cè)結(jié)果如表3所示。Marshalsec數(shù)據(jù)集中總計(jì)有 2條 Jackson利用鏈。JGadgetInspector挖掘出8條鏈,其中0條有效鏈,9條誤報(bào)鏈,漏報(bào)率為100%,誤報(bào)率為100%; TGadgetInspector挖掘出9條鏈,其中0條有效鏈,9條誤報(bào)鏈,漏報(bào)率為100%,誤報(bào)率為100%;GadgetSearch一共挖掘出18條利用鏈,且均為18條有效鏈,漏報(bào)率為0%,誤報(bào)率0%。
表3 Jackson 檢測(cè)結(jié)果Table 3 Results of Jackson
Fastjson檢測(cè)結(jié)果如表4所示。Marshalsec數(shù)據(jù)集中沒(méi)有相應(yīng)的Fastjson利用鏈, JGadgetInspector不支持 Fastjson利用鏈的檢測(cè)。TGadgetInspector一共檢測(cè)出633條利用鏈,其中0條有效鏈,漏報(bào)率100%,誤報(bào)率100%;GadgetSearch一共檢測(cè)出85條利用鏈,均為有效鏈,誤報(bào)率為0%,漏報(bào)率為0%。
表4 Fastjson 檢測(cè)結(jié)果Table 4 Results of Fastjson
XStream檢測(cè)結(jié)果如表5所示。Marshalsec數(shù)據(jù)集中包含 5條 XStream 利用鏈,JGadgetInspector檢測(cè)出101條利用鏈,其中有14條有效利用鏈,漏報(bào)了5條,誤報(bào)率為86%,漏報(bào)率為100%;TGadgetInspector一共檢測(cè)出269條利用鏈,其中12條有效利用鏈,漏報(bào)了5條,誤報(bào)率為95%,漏報(bào)率為100%;GadgetSearch一共檢測(cè)出126條利用鏈,且均為有效鏈,誤報(bào)率為0%,漏報(bào)率為0%。
表5 XStream 檢測(cè)結(jié)果Table 5 Results of XStream
Hessian檢測(cè)結(jié)果如表6所示。Marshalsec數(shù)據(jù)中包含3條Hessian利用鏈,JGadgetInspector不支持Hessian反序列化利用鏈的挖掘。TGadgetInspector一共檢測(cè)出1021條利用鏈,其中6條有效利用鏈,誤報(bào)率為99.4%,沒(méi)有檢測(cè)出Marshalsec數(shù)據(jù)集中包含的利用鏈,漏報(bào)率為100%;GadgetSearch一共檢測(cè)出36條利用鏈,且均為有效鏈,誤報(bào)率為0%,漏報(bào)率為0%。
表6 Hessian 檢測(cè)結(jié)果Table 6 Results of Hessian
JND檢測(cè)結(jié)果如表7所示。Ysoserial數(shù)據(jù)集中包含13條JND反序列化利用鏈。JGadgetInspector一共檢測(cè)出24條利用鏈,分析發(fā)現(xiàn)24條全是誤報(bào),漏報(bào)率為100%,誤報(bào)率100%。針對(duì)JGadgetInspector報(bào)告的common- collections反序列化利用鏈,如圖6所示,經(jīng)過(guò)分析可認(rèn)為此條鏈并不能直接利用。因?yàn)樽罱KMethod#invoke 調(diào)用時(shí),第一個(gè)參數(shù)input是Class類(lèi),無(wú)法造成危害,不能達(dá)到命令執(zhí)行的效果,如圖7所示。TGadgetInspector一共發(fā)現(xiàn)了26條利用鏈,26條利用鏈均為誤報(bào),漏報(bào)率100%,誤報(bào)率100%。GadgetSearch一共檢測(cè)出118條利用鏈,均為有效鏈,針對(duì)bsh-2.0b5.jar利用鏈過(guò)長(zhǎng)超過(guò)了GadgetSearch設(shè)置的最大長(zhǎng)度,未被檢測(cè)出有效利用鏈, 漏報(bào)率為7%,誤報(bào)率為0%。
圖6 JGadgetInspector 檢測(cè)common-collections結(jié)果 Figure 6 The results of common-collections detected by JGadgetInspector
圖7 common-collections 無(wú)效利用鏈 Figure 7 common-collections Invalid gadget chain
表7 JND檢測(cè)結(jié)果Table 7 Results of JND
在Jackson CVE數(shù)據(jù)集上JGadgetInspector 和TGadgetInspector均未檢測(cè)出相應(yīng)包含關(guān)鍵類(lèi)的利用鏈,漏報(bào)率100%,GadgetSearch則全部檢測(cè)出,漏報(bào)率0%,檢測(cè)結(jié)果如表8所示。
表8 Jackson CVE檢測(cè)結(jié)果Table 8 Results of Jackson CVE
在XStream CVE數(shù)據(jù)集上的檢測(cè)結(jié)果如表9所示,JGadgetInspector 和TGadgetInspector能檢測(cè)出3個(gè)CVE,GadgetSearch能夠檢測(cè)出12個(gè)CVE。其中CVE-2021-39141、CVE-2021-39144、CVE-2021-39146、CVE-2021-39153為上報(bào)給XStream官方的,此外CVE-2021-39149基本等同 于本文發(fā)現(xiàn)的CVE-2021-39144,CVE-2021-39154基本等同于本文發(fā)現(xiàn)的CVE-2021-39146。CVE-2020-21344和CVE-2020-21345利用鏈過(guò)長(zhǎng),導(dǎo)致GadgetSearch無(wú)法檢測(cè)出相應(yīng)的利用鏈。
表9 XStream CVE檢測(cè)結(jié)果Table 9 Results of XStream CVE
從實(shí)驗(yàn)結(jié)果可以看出,GadgetSearch的漏報(bào)率和誤報(bào)率都遠(yuǎn)低于 JGadgetInspector和TGadgetInspector,主要原因是JGadgetInspector和TGadgetInspector入口函數(shù)和危險(xiǎn)函數(shù)設(shè)置不全,以及沒(méi)有進(jìn)行指針?lè)治銮艺{(diào)用圖不準(zhǔn)確。
GadgetSearch 檢測(cè) Jackson、XStream、Hessian、JND的運(yùn)行效率優(yōu)于JGadgetInspector和TGadgetInspector;檢測(cè)Fastjson的效率低于JGadgetInspector和 TGadgetInspector。Gadget Search設(shè)置的危險(xiǎn)函數(shù)比TGadget Inspector全,在測(cè)試resin時(shí)GadgetSearch找到的從入口函數(shù)到危險(xiǎn)函數(shù)的路徑比TGadgetInspector多,導(dǎo)致GadgetSearch后續(xù)指針?lè)治龊臀埸c(diǎn)分析消耗了較多的時(shí)間。
為了驗(yàn)證本文提出的混合分析方法的有效性,本文做了3組對(duì)比實(shí)驗(yàn),分別利用調(diào)用圖分析、混合信息流分析以及混合分析,實(shí)驗(yàn)結(jié)果如表10所示。在入口函數(shù)設(shè)置為toString和危險(xiǎn)函數(shù)設(shè)置為Method#invoke的情況下,對(duì)JDK中的XStream利用鏈進(jìn)行檢測(cè)。實(shí)驗(yàn)結(jié)果表明,調(diào)用圖分析效率最高,但誤報(bào)數(shù)也最高?;旌闲畔⒘鞣治鲈谟邢薜臅r(shí)間(24 h)未能完成分析,混合分析誤報(bào)數(shù)最低,同時(shí)運(yùn)行時(shí)間在可接受的范圍。混合分析的誤報(bào)主要來(lái)自轉(zhuǎn)移函數(shù)的黑名單不健全,污染了一些不該污染的返回值。
表10 3種分析對(duì)比實(shí)驗(yàn)結(jié)果Table 10 Comparison of experimental results
下面將介紹GadgetSearch挖掘到了Hessian僅依賴JDK的反序列化利用鏈,并基于此利用鏈攻擊Dubbo的全過(guò)程。Dubbo 是一個(gè)高性能、輕量級(jí)的開(kāi)源服務(wù)框架。其默認(rèn)支持Hessian作為其數(shù)據(jù)傳輸時(shí)序列化和反序列化的方式。Hessian在反序列化Map類(lèi)型時(shí)執(zhí)行map#put時(shí)會(huì)觸發(fā)Object#hashCode,但是Object#hashCode調(diào)用范圍比較小,未能找到可以利用的調(diào)用鏈。為了擴(kuò)大利用鏈的搜索范圍,尋找除了Object#hashCode以外新的入口函數(shù)。分析GadgetSearch的調(diào)用圖,發(fā)現(xiàn)在執(zhí)行HessianInput#readMap時(shí)會(huì)將反序列化之后的obj和字符串拼接,觸發(fā)obj.toString() 函數(shù),所以本文找到了一個(gè)新的入口函數(shù)Object#toString,如圖8所示。
圖8 Hessian 新入口函數(shù) Figure 8 New entry point of Hessian
將Object#toString添加到入口函數(shù)重新運(yùn)行GadgetSearch,輸出了利用鏈如圖9所示。MimeTypeParameterList#toString中調(diào)用了parameters. get(keys),parameters是類(lèi)型為Hashtable的field,在反序列化時(shí)可控,可以設(shè)置parameters值為UIDefaults的實(shí)例,當(dāng)執(zhí)行parameters.get(keys)時(shí),實(shí)際是執(zhí)行UIDefaults# get,這樣就從MimeTypeParameter List#toString這個(gè)gadget跳到了UIDefaults#get這個(gè)gadget。UIDefaults#get中調(diào)用了UIDefaults# getFromHashtable,在UIDefaults# getFromHashtable中,通過(guò)super.get(key) 獲取了value值,由于UIDefaults繼承了Hashtable類(lèi),并且UIDefaults值是攻擊者可控的,所以取出的value也是攻擊者可控的。如果value是LazyValue類(lèi)型,則調(diào)用value.create Value(this)。因?yàn)関alue攻擊者可控,所以可以設(shè)置value值為UIDefaults $ProxyLazyValue的實(shí)例,調(diào)用value.createValue(this)時(shí),實(shí)際上是調(diào)用UIDefaults$ProxyLazyValue. create-Value(this),調(diào)到了UIDefaults$ProxyLazy Value# createValue gadget上。在UIDefaults$Proxy Lazy-Value# createValue中變量 className、args、methodName 均為field,所以可以從類(lèi)名為className中獲取方法名為methodName的方法,然后通過(guò)MethodUtil.invoke調(diào)用獲取的方法,MethodUtil.invoke就是本文定義的危險(xiǎn)函數(shù)。通過(guò)4個(gè)gadget,完成了從入口函數(shù)Object#toString到危險(xiǎn)函數(shù)MethodUtil.invoke的調(diào)用。但是利用到此還未結(jié)束,還需要確定要調(diào)用哪個(gè)類(lèi)的哪個(gè)方法,MethodUtil.invoke(method,obj,args)第一個(gè)參數(shù)是要調(diào)用的方法,第二個(gè)參數(shù)是調(diào)用的對(duì)象,第三個(gè)參數(shù)是要傳的參數(shù)。在UIDefaults$Proxy LazyValue#createValue中能控制第一個(gè)參數(shù)和第三個(gè)參數(shù),第二個(gè)參數(shù)無(wú)法設(shè)置為對(duì)應(yīng)的對(duì)象。所以這里要調(diào)用class的方法,但調(diào)用class的方法沒(méi)有什么危害。此外還可以調(diào)用靜態(tài)函數(shù),因?yàn)殪o態(tài)函數(shù)的調(diào)用,對(duì)對(duì)象沒(méi)有要求,即使設(shè)置為null也可以。所以需要找一個(gè)有危害的靜態(tài)函數(shù)調(diào)用。從所有JDK的靜態(tài)函數(shù)中,找到了兩個(gè)合適的靜態(tài)函數(shù),com.sun.org.apache.xml. internal.security.utils. JavaUtils#writeBytesToFilename和Java.lang.System# load,JavaUtils# writeBytesToFilename函數(shù)可以對(duì)任意文件寫(xiě)任意內(nèi)容,System#load 可以加載so文件,在加載so文件時(shí)觸發(fā)代碼執(zhí)行。所以可以將兩者組合,利用JavaUtils#writeBytesToFilename在臨時(shí)目錄寫(xiě)入一個(gè)惡意的so文件,然后再用System#load加載該惡意的so即可觸發(fā)命令執(zhí)行。
圖9 Hessian 反序列化利用鏈 Figure 9 Hessian gadget chain
本文提出了一種基于混合分析的反序列化利用鏈挖掘方法,并基于該方法實(shí)現(xiàn)了一款自動(dòng)化挖掘Java反序列化利用鏈工具GadgetSearch,利用該工具發(fā)現(xiàn)了一條第三方庫(kù)中的Jackson未公開(kāi)利用鏈,4條JDK中的XStream未公開(kāi)利用鏈,1條JDK中的Hessian未公開(kāi)利用鏈。同時(shí),在對(duì)比實(shí)驗(yàn)中GadgetSearch的誤報(bào)率和漏報(bào)率遠(yuǎn)低于現(xiàn)有工具GadgetInspector。