黃 寧,黃曙光,潘祖烈,常 超
(國防科技大學(xué) 電子對抗學(xué)院, 安徽 合肥 230000)
隨著信息技術(shù)的發(fā)展,軟件漏洞的發(fā)掘與利用成了一個熱點問題。針對不同類型的漏洞利用技術(shù),各種保護機制也層出不窮。但是,多年的漏洞利用實踐證明,由于各方面條件的限制,依然存在許多可繞過這些保護機制,成功實施漏洞利用的技術(shù)手段。
以二進制程序漏洞利用為例,由于計算機無法區(qū)分內(nèi)存空間中二進制碼的代碼或數(shù)據(jù)屬性,可能導(dǎo)致進程執(zhí)行外部數(shù)據(jù),從而造成控制流劫持攻擊[1]。針對這一問題,Linux和Windows等主流操作系統(tǒng)相繼引入了數(shù)據(jù)執(zhí)行保護(Data Execution Prevention, DEP)機制[2]。該機制的基本原理是,通過標記程序內(nèi)存頁為可執(zhí)行/不可執(zhí)行,實現(xiàn)內(nèi)存空間代碼區(qū)和數(shù)據(jù)區(qū)的區(qū)分。在DEP環(huán)境下,位于數(shù)據(jù)區(qū)的惡意代碼將無法被執(zhí)行,從而阻止控制流劫持攻擊[3]。
由于DEP機制不會攔截可執(zhí)行頁面中的代碼指令,solar designer提出了ret to libc(ret2libc)方法。該方法通過劫持程序控制流,使程序跳轉(zhuǎn)至已有的系統(tǒng)函數(shù)。Schacham等[4-5]在ret2libc思想的基礎(chǔ)上,提出了返回導(dǎo)向式編程(Return Oriented Programming, ROP)技術(shù)。相比ret2libc,ROP使用更小的匯編指令片段(gadget),提高了該類方法的泛用性。Lu等[6]基于Rix的方法,提出了可壓縮、可打印的ROP構(gòu)造方法,提高了ROP載荷的靈活性。
近年來出現(xiàn)多種針對控制流劫持類漏洞自動化分析[7]與測試用例自動生成技術(shù)[8-10]。Schwartz等提出了面向二進制程序漏洞的ROP自動構(gòu)造框架Q[11-12]。其工作流程:首先,向Q框架可執(zhí)行文件,搜索其中具備特定功能的gadget集合;對面向gadget的高級語言進行語義分析,構(gòu)建中間指令序列;分析中間指令序列,為每條中間指令分配合適的gadget集合,形成ROP鏈。Q框架的局限性[13]在于,該方案生成的測試用例僅從功能實現(xiàn)的角度出發(fā),未考慮ROP布局對程序內(nèi)存可控性條件的要求,降低了ROP鏈的實用性。
為解決上述問題,本文提出了基于符號執(zhí)行的ROP碎片化自動布局方法。該方法將ROP鏈以模塊為單位,切割成長度不一的碎片;使用導(dǎo)向式符號執(zhí)行技術(shù),引導(dǎo)源程序運行至控制流劫持點的同時,檢查程序中可控內(nèi)存區(qū)域是否滿足ROP模塊布局要求;以ROP模塊切片與可控內(nèi)存區(qū)域布局為根據(jù),構(gòu)建碎片化ROP鏈的數(shù)據(jù)約束;通過求解數(shù)據(jù)約束,自動生成滿足程序可控內(nèi)存分布條件的碎片化ROP鏈。該方法解決了ROP鏈對內(nèi)存可控性要求高的問題,提高了ROP鏈的實用性。
ROP技術(shù)基于ret2libc技術(shù)發(fā)展而來。該技術(shù)主要針對DEP機制未限制代碼頁中已有代碼的執(zhí)行權(quán)限這一缺陷,實現(xiàn)DEP機制繞過[14]。其主要原理是,通過搜索程序內(nèi)存代碼頁,構(gòu)建以ret等跳轉(zhuǎn)指令結(jié)尾的匯編指令片段gadget集合。從gadget集合中篩選出符合條件的部分,組合成一段可實現(xiàn)特定功能的ROP代碼鏈。圖1表示了一個ROP鏈的代碼執(zhí)行順序及其相應(yīng)的??臻g數(shù)據(jù)分布。
圖1 ROP鏈在棧內(nèi)存中布局結(jié)構(gòu)示意圖Fig.1 ROP chain and the structure of stack
Q框架以自定義高級語言ROPL表示ROP鏈的目標功能程序。ROPL高級語言到匯編指令序列的轉(zhuǎn)換過程為:ROPL高級語言—ROPL中間表示序列—中間指令序列—gadget匯編指令序列(ROP鏈)。
目標程序模塊指的是一個可執(zhí)行相對獨立功能的ROPL代碼序列的集合,其結(jié)構(gòu)類似于C語言中的函數(shù)。
ROPL語言框架下,多模塊目標程序定義為:目標程序包含至少兩個以上模塊,且調(diào)用了至少一個以上非main模塊。多模塊ROP中,除main模塊外的ROP模塊的切換過程由以下三個子過程組成:目標模塊開始調(diào)用過程,目標模塊參數(shù)初始化過程,目標模塊返回調(diào)用過程。
已有的ROP自動生成技術(shù)主要解決了gadget搜索與分類,高級語言語義分析,以及面向中間指令的gadget分配與排列三個方面的問題,實現(xiàn)了ROP鏈自動構(gòu)造。但從實際運用效果看,Q框架仍然無法滿足多數(shù)場景下源程序的內(nèi)存可控性狀態(tài)對ROP鏈布局的限制。為解決這一問題,本文在Q框架的基礎(chǔ)上,提出了基于碎片化布局的ROP自動構(gòu)造方法。該方法構(gòu)造碎片化布局的ROP鏈過程如圖2所示。
圖2 ROP碎片化自動布局過程示意圖Fig.2 Overview of ROP fragmented layout and automatic generation
在Q框架生成ROP載荷與目標程序符號表的基礎(chǔ)上,本文將結(jié)合對源程序可控內(nèi)存區(qū)域的檢查結(jié)果,構(gòu)建碎片化ROP約束,求解約束,生成碎片化ROP鏈。
為了避免符號執(zhí)行對每條程序路徑遍歷導(dǎo)致的路徑爆炸問題,本文在符號執(zhí)行工具S2E的基礎(chǔ)上,采用了經(jīng)過路徑選擇算法優(yōu)化的導(dǎo)向式符號執(zhí)行技術(shù)[15-16]。以crash文件作為源程序的輸入文件,可引導(dǎo)源程序沿著確定的程序路徑動態(tài)運行,直至觸發(fā)控制流劫持狀態(tài)。圖3是通過導(dǎo)向式符號執(zhí)行觸發(fā)源程序控制流劫持狀態(tài)的過程。
圖3 導(dǎo)向式符號執(zhí)行路徑選擇過程示意圖Fig.3 Path selection of source program with path-oriented symbolic execution
本文在導(dǎo)向式符號執(zhí)行過程中,收集程序堆棧狀態(tài)與可控內(nèi)存區(qū)域狀態(tài)。結(jié)合ROP載荷,分析上述狀態(tài)是否滿足ROP鏈的布局條件,并構(gòu)建相應(yīng)的ROP數(shù)據(jù)約束。通過約束求解,可實現(xiàn)碎片化布局的ROP測試例自動生成。
如圖1所示,ROP鏈是由一個特定順序的gadget序列組成的。由于每個gadget均以ret指令結(jié)束,并以此控制程序跳轉(zhuǎn)至下一個gadget所在地址,其地址及gadget的相關(guān)操作數(shù)需存放于程序的堆棧中。當源程序處于控制流劫持狀態(tài)時(即指令寄存器中的數(shù)值為符號值),堆棧是否有足夠的可控空間用于ROP布局,決定了ROP是否適用于源程序。為此,本文首先對源程序內(nèi)存狀態(tài)進行分析,確定ROP布局條件。
針對本文方法的內(nèi)存分析過程涉及的關(guān)鍵數(shù)據(jù)檢查與狀態(tài)變化,本文有如下定義:
symbolicBlock
stackPtr:表示首次控制流劫持狀態(tài)下的當前棧頂指針。
stack_symbolicLength:若當前棧頂位置處于符號化區(qū)域中,該數(shù)值表示以stackPtr為起始地址的一段連續(xù)的符號化內(nèi)存長度。
mLenid表示ROP中名稱為id的模塊占用內(nèi)存長度。每個模塊的長度信息均記錄于中間表示符號表中。
在一般的溢出漏洞中(比如棧溢出漏洞),覆蓋當前棧頂位置的污點數(shù)據(jù)的起始地址通常位于上一個函數(shù)棧幀中。但對于執(zhí)行g(shù)adget序列來說,需要關(guān)注的只是源程序的控制流劫持時刻的棧頂數(shù)據(jù)屬性。因此,本文將在源程序控制流劫持時刻,從棧頂位置開始進行符號化檢查。若源程序棧頂位置不為符號化數(shù)據(jù),表示源程序無法跳轉(zhuǎn)至第二個gadget,不滿足ROP開始執(zhí)行的初始條件;若棧頂數(shù)據(jù)為符號化數(shù)據(jù),計算以棧頂位置開始的符號化數(shù)據(jù)長度。圖4顯示的是源程序控制流劫持時刻,滿足ROP布局條件的棧結(jié)構(gòu)示意圖。
圖4 源程序控制流劫持時刻的棧內(nèi)存結(jié)構(gòu)示意圖Fig.4 Structure of stack at the time of control-flow hijacked
為滿足當前棧幀可控空間不足情況下ROP的布局要求,可控內(nèi)存搜索算法將搜索并記錄進程用戶態(tài)內(nèi)存空間中其余的符號化區(qū)域信息。其過程如算法1所示。
算法1 可控內(nèi)存區(qū)域搜索
本文從可控內(nèi)存區(qū)域集合memSet中尋找滿足ROP模塊布局條件的元素。候選的符號化區(qū)域長度symbolicSize需要至少滿足容納某一ROP模塊,且該區(qū)域包含的范圍與源程序控制流劫持狀態(tài)下的棧頂指針不應(yīng)相互沖突。對集合memSet中的所有symbolicBlock元素進行第一輪過濾,選出若干符合ROP模塊長度條件的內(nèi)存區(qū)域,將該元素加入對應(yīng)ROP模塊的候選區(qū)域集合lenBlocksid中,該模塊的候選區(qū)域集合需滿足如式(1)所示條件。lenBlocksid表示經(jīng)過第一輪過濾后,模塊名為id的ROP對應(yīng)的所有候選區(qū)域集合。
lenBlocksid:
?block∈symbolicBlock|(symbolicSize>mLenid)∧[(stackPtr
(1)
在候選區(qū)域長度滿足ROP模塊長度要求的基礎(chǔ)上,通過比較候選區(qū)域數(shù)據(jù)可控性約束與ROP模塊數(shù)據(jù)約束的兼容性,對每個ROP模塊的候選區(qū)域集合進行第二輪過濾。第二輪過濾完成后,ROP模塊id對應(yīng)的候選區(qū)域集合conBlocksid應(yīng)滿足式(2)所示條件。
conBlocksid:
?block∈lenBlockid|(canArea?block)∧ (canArea.Size≥ropModuleid.Size)∧Eq(area,moduleid)=true
(2)
式中,canArea表示候選區(qū)域block中任意連續(xù)的可控內(nèi)存區(qū)域;canArea.Size表示canArea的長度;ropModuleid.Size表示ROP模塊id的長度,moduleid表示該模塊的數(shù)據(jù)約束;函數(shù)Eq用于實現(xiàn)約束條件A與約束條件B的兼容性比較。
result=Eq(A,B)
當約束比較函數(shù)Eq(A,B)的返回值result為true時,表示約束條件A與B可兼容;為false時,表示A與B不可兼容。
當且僅當模塊ropModule的數(shù)據(jù)約束module與canArea區(qū)域可控性約束area的兼容性判斷結(jié)果為true時,該ROP模塊是可執(zhí)行的。數(shù)據(jù)約束module與area相兼容的檢查條件包括以下兩項:module中所有連續(xù)字節(jié)的約束與area所有連續(xù)字節(jié)的可控約束相兼容;module中剩余的字節(jié)數(shù)應(yīng)不大于area中剩余的字節(jié)數(shù),即canArea應(yīng)有足夠的可控空間容納該ROP模塊。
針對area與module的兼容性比較過程以字節(jié)為單位。對ropModuleid中的每個字節(jié)與canArea區(qū)域中每個字節(jié)的約束條件逐一比較,構(gòu)造待求解的模塊數(shù)據(jù)約束mConstraint。該過程如算法2所示。
算法2 ROP模塊數(shù)據(jù)約束構(gòu)造
算法2中,若isAvailable返回值為true,表示canArea區(qū)域滿足ROP模塊id的布局條件,并將canArea區(qū)域標記為不可控區(qū)域后,重新構(gòu)建可控內(nèi)存區(qū)域集合memSet。圖5表示完成碎片化自動布局后,多模塊ROP的執(zhí)行過程。針對剩余ROP模塊,重復(fù)執(zhí)行第一輪與第二輪過濾,直至所有ROP模塊完成布局區(qū)域分配。該過程如算法3所示。
圖5 碎片化多模塊ROP執(zhí)行過程示意圖Fig.5 Execution process of fragmented multi-module ROP
算法3 ROP碎片化約束構(gòu)造
以代碼1中的ROPL高級語言作為目標程序。通過對目標程序進行語義分析,其對應(yīng)的高級語言中間表示符號表如代碼2所示。
代碼1
代碼2
針對代碼1所示目標程序生成的ROP鏈長度為224(0xE0)B。該ROP鏈分為3個模塊:模塊main的長度為76(0x4C)B;模塊f1的長度為76(0x4C)B;模塊foo的長度為72(0x48)B。
為驗證通過本文方法實現(xiàn)的ROP鏈在實際程序中的碎片化布局效果,選取了7個包含控制流劫持漏洞的源程序進行實驗驗證。本文將可能覆蓋程序內(nèi)存關(guān)鍵位置的污點數(shù)據(jù)標記為符號值,并通過構(gòu)建ROP數(shù)據(jù)約束及約束求解,判斷相應(yīng)的內(nèi)存位置是否滿足ROP布局條件。表1為各實驗程序的實驗環(huán)境。
表1 漏洞程序?qū)嶒灜h(huán)境
通過漏洞觸發(fā)代碼觸發(fā)源程序首次控制流劫持狀態(tài)。本文對首次控制流劫持時刻的程序內(nèi)存狀態(tài)進行分析,內(nèi)存各區(qū)域可控污點數(shù)據(jù)情況如表2所示。
表2 控制流劫持狀態(tài)時刻源程序可控內(nèi)存區(qū)域分布情況Tab.2 Layout of controllable tainted data in memory at the first time of control flow hijacked
注:“*”表示棧內(nèi)存中的可控數(shù)據(jù)長度從當前棧頂位置開始計算表示。
對比表1與表2中的數(shù)據(jù)可發(fā)現(xiàn),部分漏洞程序的引入污點數(shù)據(jù)長度與首次控制流劫持狀態(tài)時刻的內(nèi)存各部分可控污點數(shù)據(jù)長度并不完全一致。造成這一現(xiàn)象的原因主要有以下幾種:
1)引入污點數(shù)據(jù)通過復(fù)制等操作傳播至內(nèi)存其他區(qū)域,造成部分污點數(shù)據(jù)的約束條件有重合部分。屬于該類情況的漏洞程序包括CVE-2017-11882等。
2)污點數(shù)據(jù)覆蓋其他內(nèi)存區(qū)域的關(guān)鍵數(shù)據(jù)。屬于該類情況的漏洞程序包括CVE-2014-0322等。
此類漏洞利用方式為,通過數(shù)組越界讀寫,實現(xiàn)程序任意內(nèi)存地址讀寫,進而導(dǎo)致函數(shù)地址覆蓋與程序控制流劫持。由于函數(shù)地址在內(nèi)存代碼段,因此不在本文對ROP布局內(nèi)存分析的范圍內(nèi)。
3)污點數(shù)據(jù)符號屬性丟失或污點數(shù)據(jù)不可控。屬于該類情況的漏洞程序包括CVE-2010-3333等。
CVE-2010-3333是棧溢出漏洞,但在棧內(nèi)存中,可控污點數(shù)據(jù)僅在棧頂位置向下16 B的范圍內(nèi)。對于不可控的污點數(shù)據(jù),由于不具有利用價值,因此,本文未做統(tǒng)計與分析。
在對源程序內(nèi)存可控污點數(shù)據(jù)布局狀態(tài)分析的基礎(chǔ)上,針對代碼1目標程序構(gòu)造的ROP鏈經(jīng)過碎片化布局處理后,各模塊ROP在內(nèi)存空間中的分布如表3所示。
表3 各模塊ROP在源程序內(nèi)存中的布局情況
MS06-055的棧內(nèi)存僅滿足main模塊的布局條件。本文通過堆噴射漏洞觸發(fā)代碼,實現(xiàn)源程序內(nèi)存中的堆塊大量布局,并將寫入堆塊的數(shù)據(jù)標記為污點數(shù)據(jù)。通過內(nèi)存分析,確定進程的堆內(nèi)存滿足f1與foo模塊的布局條件。
CVE-2010-3333的棧內(nèi)存不滿足任一模塊布局條件。本文對該漏洞實驗做了手工調(diào)整,在其棧內(nèi)存中布置了簡化的堆棧偽造指令,使其堆棧指針指向堆內(nèi)存區(qū)域。通過分析,該進程堆中的可控數(shù)據(jù)區(qū)域滿足代碼2所有模塊的布局條件。
CVE-2012-0158漏洞的棧內(nèi)存布局情況滿足代碼2所有模塊的布局條件,因此未將任何一個模塊布置于其他內(nèi)存區(qū)域中。
CVE-2014-0322與CVE-2015-5119漏洞的棧內(nèi)存不符合代碼1中模塊布局條件。為了驗證碎片化布局方法,本文首先通過偽造??臻g的方法,在堆內(nèi)存中開辟一段偽造的棧內(nèi)存。與上述兩個漏洞布局進行對比,f1與foo模塊均布置于堆內(nèi)存中。實驗結(jié)果證明,CVE-2014-0322與CVE-2015-5119的實驗程序內(nèi)存滿足表3所示的布局條件。
根據(jù)表2,CVE-2017-11882棧內(nèi)存的可控空間大于代碼1 ROP所需的內(nèi)存空間長度。但通過進一步分析發(fā)現(xiàn),該漏洞程序的棧內(nèi)存空間過于碎片化,其中最大的一塊棧內(nèi)存可控區(qū)域長度僅為120 B,不足以容納代碼1中的任意兩個模塊。因此,本文僅將main模塊布局于棧內(nèi)存中,f1與foo模塊布局于堆內(nèi)存中。CVE-2017-11882的ROP碎片化布局實驗結(jié)果如表3所示。
CVE-2018-8174漏洞程序的棧內(nèi)存可控空間僅滿足進程跳轉(zhuǎn)功能,不滿足代碼1 ROP模塊布局功能,因此,該漏洞程序的ROP模塊全部布局于堆內(nèi)存中。
本文挑選了CVE-2014-0322漏洞實驗樣本,對其ROP碎片化布局過程進行具體分析與闡述。
當符號執(zhí)行工具S2E檢測CVE-2014-0322樣本程序進入控制流劫持狀態(tài)時,首先檢查源程序狀態(tài)結(jié)果為:
棧頂指針寄存器ESP:0x0307B7D4;
符號化寄存器:EAX;EIP;
符號化內(nèi)存區(qū)域:0x0307B7D4~0x0307B7D7;0x10000000~0x28180000;0x28180000~0x28280000。
如上文所述,控制流劫持狀態(tài)下的樣本程序棧內(nèi)存不符合任何一個ROP模塊的布局要求。為此,需要向樣本程序中加入偽造棧空間的步驟。該步驟通過下述gadget完成。
XCHG EAX, ESP;
RETN;
該gadget的內(nèi)存地址為0x5ED6E850。
偽造的??臻g棧頂指針為:0x1A1B3010。根據(jù)對比,發(fā)現(xiàn)該區(qū)域位于符號化區(qū)域中。
針對main 模塊進行兩輪可控內(nèi)存區(qū)域過濾后,該模塊的最終布局范圍為:0x1A1B3010~0x1A1B305C。
針對f1模塊進行兩輪可控內(nèi)存區(qū)域過濾后,該模塊的最終布局范圍為:0x1A1B4010~0x1A1B405C。
針對foo模塊進行兩輪可控內(nèi)存區(qū)域過濾后,該模塊的最終布局范圍為:0x1A1B5010~0x1A1B5058。
為驗證本文方法的時間開銷,本文記錄了該方法的時間開銷為t1。時間開銷t1的定義為:從源程序開始符號執(zhí)行,到生成碎片化布局的ROP測試用例所消耗時間。
作為對比,本文還記錄了符號執(zhí)行工具S2E對源程序進行分析的時間開銷t2。時間開銷t2的定義為:從源程序開始符號執(zhí)行,到生成控制流劫持路徑測試用例的消耗時間。對各實驗樣本進行10次實驗,各樣本的平均時間消耗如圖6所示。
圖6 碎片化ROP布局方法與S2E系統(tǒng)時間開銷對比Fig.6 Comparison of analysis time by ROP fragmented layout method and S2E
由于本文提出的碎片化ROP布局方法是在源程序控制流劫持狀態(tài)下,通過內(nèi)存分布狀態(tài)分析實現(xiàn)的,因此,圖6中各實驗樣本的時間開銷之差,即t1-t2,基本可被視為可控內(nèi)存區(qū)域分析過程的時間消耗。
圖6所示實驗樣本中,t1與t2的差值較大。原因是crash文件觸發(fā)源程序控制流劫持狀態(tài)過程中,使用了堆噴射技術(shù),使源程序內(nèi)存空間存在大量可控區(qū)域,導(dǎo)致內(nèi)存搜索算法運行時間增加。
針對目前ROP自動化構(gòu)造過程中存在的空間效率低與內(nèi)存布局要求高等問題,提出了基于符號執(zhí)行的多模塊ROP碎片化自動布局方法。該方法以ROP模塊為單位,在動態(tài)分析源程序可控污點數(shù)據(jù)分布情況的基礎(chǔ)上,實現(xiàn)各模塊ROP的碎片化分布,降低了ROP布局對源程序內(nèi)存布局條件的要求。
此外,本文提出的基于碎片化布局的多模塊ROP自動生成方法仍然存在局限性。首先,ROP模塊調(diào)用過程未考慮地址隨機化對目標模塊地址尋找的影響。其次,符號執(zhí)行過程中造成的污點數(shù)據(jù)符號屬性丟失等問題會影響該方法對源程序內(nèi)存狀態(tài)分析的結(jié)果。如何減少上述問題對ROP自動生成過程的影響,是下一步工作的研究重點。