文/武成崗 李建軍
?
控制流完整性的發(fā)展歷程
文/武成崗李建軍
武成崗中國科學(xué)院計算技術(shù)研究所正高級工程師、博士生導(dǎo)師,主要研究領(lǐng)域為動態(tài)編譯、軟件安全、程序分析等。
李建軍中國科學(xué)院計算技術(shù)研究所副研究員,主要研究領(lǐng)域為動態(tài)程序分析、軟件安全保障等。
控制流劫持是一種危害性極大的攻擊方式,攻擊者能夠通過它來獲取目標(biāo)機器的控制權(quán),甚至進行提權(quán)操作,對目標(biāo)機器進行全面控制。當(dāng)攻擊者掌握了被攻擊程序的內(nèi)存錯誤漏洞后,一般會考慮發(fā)起控制流劫持攻擊。早期的攻擊通常采用代碼注入的方式,通過上載一段代碼,將控制轉(zhuǎn)向這段代碼執(zhí)行。為了阻止這類攻擊,后來的計算機系統(tǒng)中都基本上都部署了DEP(Data Execution Prevention)機制,通過限定內(nèi)存頁不能同時具備寫權(quán)限和執(zhí)行權(quán)限,來阻止攻擊者所上載的代碼的執(zhí)行。為了突破DEP的防御,攻擊者又探索出了代碼重用攻擊方式,他們利用被攻擊程序中的代碼片段,進行拼接以形成攻擊邏輯。代碼重用攻擊包括Return-tolibc、ROP(Return Oriented Programming)、JOP(Jump Oriented Programming)等。研究表明,當(dāng)被攻擊程序的代碼量達到一定規(guī)模后,一般能夠從被攻擊程序中找到圖靈完備的代碼片段。
為了抵御控制流劫持攻擊,加州大學(xué)和微軟公司于2005年提出了控制流完整性(Control Flow Integrity, CFI)的防御機制。其核心思想是限制程序運行中的控制轉(zhuǎn)移,使之始終處于原有的控制流圖所限定的范圍內(nèi)。具體做法是通過分析程序的控制流圖,獲取間接轉(zhuǎn)移指令(包括間接跳轉(zhuǎn)、間接調(diào)用和函數(shù)返回指令)目標(biāo)的白名單,并在運行過程中,核對間接轉(zhuǎn)移指令的目標(biāo)是否在白名單中。控制流劫持攻擊往往會違背原有的控制流圖,CFI使得這種攻擊行為難以實現(xiàn),從而保障軟件系統(tǒng)的安全。
CFI從實現(xiàn)角度上,被分為細粒度和粗粒度兩種。細粒度CFI嚴格控制每一個間接轉(zhuǎn)移指令的轉(zhuǎn)移目標(biāo),這種精細的檢查,在現(xiàn)有的系統(tǒng)環(huán)境中,通常會引入很大的開銷。而粗粒度CFI則是將一組類似或相近類型的目標(biāo)歸到一起進行檢查,以降低開銷,但這種方法會導(dǎo)致安全性的下降。
CFI已經(jīng)被提出十多年的今天,依然有許多研究者在探索新的CFI技術(shù),使其在可接受的開銷下能獲得高安全性,圖1給出了CFI的發(fā)展歷程,本文將介紹在該歷程中的重要的技術(shù)創(chuàng)新和相關(guān)研究成果。
為了有效防御控制流劫持攻擊,2005 年Martín Abadi和Mihai Budiu,等人提出了控制流完整性(Control-Flow Integrity,CFI)的思路[1]。程序在其執(zhí)行過程中,應(yīng)當(dāng)遵循預(yù)先定義好的控制流圖(Control Flow Graph, CFG),以確保程序控制流不被劫持或非法篡改,背離程序編制時所設(shè)計的控制流轉(zhuǎn)移關(guān)系。CFI在運行時檢測程序的控制轉(zhuǎn)移是否在控制流圖中,以識別是否遭遇了攻擊。具體作法是在控制流轉(zhuǎn)移指令前插入檢驗代碼,來判斷目標(biāo)地址的合法性。這種做法能夠?qū)刂屏鹘俪止羝鸬椒烙饔茫且泊嬖谝恍﹩栴}。
嚴格意義上的CFI,需要做到對每條間接控制轉(zhuǎn)移都做檢查,并確保每條轉(zhuǎn)移指令只能轉(zhuǎn)移到它自身的目標(biāo)集合。如果要達到更好的防御效果,則要做到上下文敏感的檢查。然而,這種檢查機制雖然能夠最大程度地提升系統(tǒng)的安全性,但其開銷過大,難以得到實際部署。
Chao Zhang 等人指出,CFI未被廣泛應(yīng)用的原因是插樁引入的開銷過大,需要額外的信息(比如間接控制轉(zhuǎn)移可能的目標(biāo)集合)的支持,且不能進行增量式的部署。為此,他們提出了CCFIR[2](Compact Control Flow Integrity and Randomization)。其策略是對間接調(diào)用指令和函數(shù)返回指令的目標(biāo)進行區(qū)分,阻止未經(jīng)驗證的返回指令跳轉(zhuǎn)到敏感函數(shù)的行為。這些限制囊括了CFI保護的主要思想,且不需要別名分析。出于效率和兼容性的考慮,他們通過一個專用的Springboard section來實現(xiàn)間接分支轉(zhuǎn)移,并限定代碼對齊,對跳轉(zhuǎn)目標(biāo)進行限制。經(jīng)SPECint2000進行測試,其平均/最高性能開銷分別為3.6%和8.6%(平均/最高)。為了進一步提升安全性,Springboard section在程序啟動時隨機變換允許的跳轉(zhuǎn)目標(biāo),進一步增加了控制流劫持的難度。CCFIR是一個純粹的二進制轉(zhuǎn)換程序,不依賴源碼或調(diào)試信息,所依賴的僅是重定位表中的信息。受保護的代碼可以被獨立地驗證,也可以進行增量式的部署。
圖1 CFI主要技術(shù)發(fā)展歷程
Mingwei Zhang 等人提出了一種針對COTS(Commercial-Off-The-Shelf)程序的CFI機制——binCFI[3],目標(biāo)是使CFI直接應(yīng)用于已有的商用應(yīng)用上。他們將間接轉(zhuǎn)移指令的操作數(shù)分為代碼指針、異常處理程序入口、導(dǎo)出符號地址和返回地址等類型,通過精細的靜態(tài)分析,對不同類型的間接控制轉(zhuǎn)移,收集它們的合法目標(biāo)集合,如返回指令的合法目標(biāo)集合就是所有函數(shù)調(diào)用指令的下一條指令的地址,導(dǎo)出符號地址集合就是ELF文件中.dynamic段中存儲的地址。利用這種方法,binCFI可以得到與已有CFI方案相當(dāng)?shù)陌踩?。通過新增代碼段的方式,不改變原有的代碼,達到完全透明的目的。實驗表明,論文中提出的方案可以應(yīng)用于大規(guī)模的二進制程序中,擁有不錯的安全性。
相對于嚴格意義上的CFI,CCFIR[2]和binCFI[3]都屬于粗粒度CFI機制。所謂粗粒度CFI,就是放寬檢查的條件,減少比較目標(biāo)集合。原來的CFI是一個間接轉(zhuǎn)移對應(yīng)一個目標(biāo)集合。放寬后,間接轉(zhuǎn)移目標(biāo)進行合并,比如把所有的間接調(diào)用指令和間接跳轉(zhuǎn)指令的目標(biāo)集合合成一個目標(biāo),所有的函數(shù)返回指令的目標(biāo)集合合成一個。這種方式能夠有效地降低CFI引入的開銷,但也存在安全性問題。
Enes G?ktas在Overcoming CFI[4]中,利用兩種特殊的gadget——entry point(EP)gadget和call site(CS)gadget,來繞開粗粒度CFI機制的防御。這兩種gadget滿足檢查的條件,但是其實是并不符合真實的控制流。Enes G?ktas對這兩種gadget的有效性進行了論述,并利用其構(gòu)造ROP鏈、調(diào)用庫函數(shù),形成實際的攻擊。
為了對抗Overcoming CFI對粗粒度CFI的攻擊,Ali Jose Mashtizadeh 等人提出了一種通過對代碼指針加密,來增強CFI的方法,稱為CCFI[5]。粗粒度CFI的問題本質(zhì)就在于歸類使得攻擊者有了可乘之機。CCFI就是要對代碼指針做更細致地劃分,還不能回歸到CFI原本定義的那種細粒度的分類。他們將代碼指針細分成四類,分別是函數(shù)指針類、return指針類(函數(shù)返回地址)、方法指針類、虛表指針類(后兩種都是針對C++語言特有的類)。每次在保存一個代碼指針時,將其運行時信息加密保存。每類指針對應(yīng)一些具有標(biāo)識作用的運行信息。在這些指針解引用(間接轉(zhuǎn)移)時,解密信息并比對。另外為了加快加解密的速度,CCFI使用了處理器中獨特的指令“AES-IN”對AES加解密算法加速,同時為了保證密鑰的安全,又將密鑰保存在處理器中獨特的寄存器中。他們修改了開源編譯器LLVM,定制了一個編譯器,并用其編譯了一些程序,在實際的攻擊環(huán)境中檢測其有效性。實驗證明,他們的方法在安全性和性能方面取得了很好的折衷。
Lucas Davi 等人在Stitching the Gadgets[6]中詳細分析了不同的CFI解決方案包括kBouncer、ROPecker、binCFI、ROPGuard 和Microsoft EMET4.1,并指出它們的安全性不容樂觀。他們首先重新思考了不同的粗粒度的CFI的假設(shè),對kBouncer、ROPecker、binCFI、ROPGuard和Microsoft EMET4.1進行了詳盡的安全性分析,特別地,整合了這些現(xiàn)有粗粒度CFI,形成了一個安全策略更為嚴格的CFI系統(tǒng)。在這個更為嚴格的條件下,完成了圖靈完整性的驗證。為了更好地展示攻擊能力,他們在Adobe Reader和mPlayer上進行了實驗,證明了粗粒度的CFI無法達到預(yù)期的安全效果。論文[7]也探索對CFI進行攻擊的手段。多數(shù)防御人員認為,通過影子棧(Shadow Stack)來檢測函數(shù)返回目標(biāo),再加上DEP和ASLR的保護,棧應(yīng)該會變得非常安全,從而將重心轉(zhuǎn)向堆數(shù)據(jù)的安全保護,然而,作者發(fā)現(xiàn),棧還是會受到攻擊的。他們提出了三種攻擊方法:一是利用堆上的漏洞來破壞棧上的calleesaved寄存器保存區(qū)域,使得calleesaved寄存器被劫持;二是利用用戶空間和內(nèi)核之間進行上下文切換的問題,來劫持sysenter指令,使控制跳轉(zhuǎn)到攻擊者想跳轉(zhuǎn)的位置;三是通過泄露主棧的地址來泄露出shadow stack的地址,進而進行攻擊。
以往CFI方案的問題是只實施了控制流不敏感策略。上下文信息的缺少不可避免地降低了CFI的防御能力,從而給了攻擊者利用空間。上下文敏感的CFI(Context sensitive CFI)是有望解決這一問題的方法,它依賴于上下文敏感的靜態(tài)分析,將CFI不變量和CFG中的控制流路徑聯(lián)系到一起,運行時在執(zhí)行路徑上強制執(zhí)行這些不變量。CCFI早在CFI提出之時已被認可,但因其在現(xiàn)實應(yīng)用中不實際而被迅速廢棄。Victor van der Veen 等人提出的Practical Context-Sensitive CFI[8],展示了一個可用于現(xiàn)實應(yīng)用程序的高效、可靠、實用的上下文敏感CFI方案:PathArmor。PathArmor依賴于商用硬件的支持,高效可靠地監(jiān)控通往敏感函數(shù)(可用于實施控制流轉(zhuǎn)移攻擊)的執(zhí)行路徑;使用仔細優(yōu)化的二進制插樁設(shè)計,在被監(jiān)控路徑上強制執(zhí)行上下文敏感CFI的不變量。PathArmor的路徑不變量是按需在CFG上,通過一定范圍內(nèi)的上下文敏感靜態(tài)分析得到的,而且路徑驗證步驟使用了caching來提高驗證效率。路徑驗證本身也是非常高效的,因為所有的CFI檢查都在敏感函數(shù)點處集中完成。
John Criswell將CFI做到操作系統(tǒng)內(nèi)核中,使之免受經(jīng)典的控制流劫持、return2user和代碼段修改攻擊,該系統(tǒng)稱為KCoFI[9]。他們在基于標(biāo)簽的控制流間接轉(zhuǎn)移保護的基礎(chǔ)上,加入一個運行時監(jiān)控的軟件層,負責(zé)保護一些關(guān)鍵的操作系統(tǒng)數(shù)據(jù)結(jié)構(gòu)和監(jiān)控操作系統(tǒng)進行的所有底層狀態(tài)操作。并且還通過形式化方法,證明了一個子集的正確性。證明涵蓋了頁表管理、異常處理、上下文切換和信號分派等操作。實驗表明,KCoFI阻止了攻擊者將控制流向FreeBSD Kernel中的任何gadget的轉(zhuǎn)移。與未修改的內(nèi)核相比,KCoFI在運行server測試集時具有較小的開銷,但運行文件系統(tǒng)操作密集型的測試集時,開銷較大。
為了能夠在性能和防御方面取得更好的效果,一些研究著手于利用現(xiàn)有的硬件機制,來降低CFI的開銷。
Vasilis Pappas提出利用硬件性能計數(shù)器,在運行時觀察執(zhí)行流的思路,該方法被稱為kBouncer[10]。他們利用LBR(Last Branch Register)來捕獲最近的16次跳轉(zhuǎn)信息。具體做法是在敏感系統(tǒng)調(diào)用處,對捕獲的16次跳轉(zhuǎn)進行安全性判斷,即return指令需要跳轉(zhuǎn)到調(diào)用點的后繼位置,indirect-call指令的目標(biāo)是函數(shù)入口,其余跳轉(zhuǎn)指令目標(biāo)基本塊長度不能全部少于20條指令。為了避免攻擊者利用庫函數(shù)調(diào)用來完成攻擊,文章在所有的庫函數(shù)調(diào)用點,進行上述合法性檢查。為了驗證kBouncer的防御效果,作者對IE瀏覽器、Adobe Flash Player和Adobe Reader進行了實驗(利用已知安全漏洞,組織ROP payload攻擊這三種應(yīng)用),實驗結(jié)果表明該方法能夠有效緩解ROP攻擊。同時,該方法的性能開銷低于4%。
Yueqiang Chen等人設(shè)計了一種與kBouncer類似的方法,稱作ROPecker[11],也是利用LBR捕獲程序控制流的方式進行ROP攻擊監(jiān)測。但不同之處在于判斷是否遭受ROP攻擊的邏輯和觸發(fā)監(jiān)測的時機。
1.判斷邏輯:在運行時檢測過去(利用LBR)和未來的執(zhí)行流(模擬執(zhí)行)中是否存在長gadget鏈(5個比較短的gadget),若存在,則認為這是一次ROP攻擊。Gadget信息是通過靜態(tài)分析二進制程序和共享庫得到的。
2.運行時監(jiān)測是事件驅(qū)動的,具體時機是敏感系統(tǒng)調(diào)用和執(zhí)行流跳出滑動窗口觸發(fā)異常。ROPecker設(shè)計了一個滑動窗口,因為代碼本身具有時間和空間的局部性,但是gadget鏈卻是散列的,利用這一特性,系統(tǒng)保證該窗口內(nèi)的gadget數(shù)目不足以構(gòu)成一次ROP攻擊,窗口內(nèi)的代碼設(shè)置可執(zhí)行權(quán)限,窗口外的代碼不可執(zhí)行,當(dāng)執(zhí)行流跳出滑動窗口時,便會觸發(fā)異常,進行運行時檢測。該方法利用代碼本身具有的時間和空間局部性,針對gadget鏈是散列的前提,提出了滑動窗口機制,使用事件驅(qū)動的檢測方法,具有較高的準確性和高效性。為了驗證該方法的安全性,ROPecker選取了有棧溢出的真實世界應(yīng)用(Linux Hex-editer)進行攻防演練。實驗結(jié)果證明,ROPecker能夠有效的阻止ROP攻擊。同時,SPEC CPU2006 benchmark顯示了該方法的開銷非常低(2%)。
Yubin Xia等人設(shè)計的CFIMon[12],也是采用性能計數(shù)器來捕獲程序執(zhí)行流,并進行合法性判斷。但他們采用的是BTB(Branch Trace Buffer),來捕獲受保護程序運行過程中所有跳轉(zhuǎn)指令的信息。BTB 與LBR不同之處在于,BTB可以把程序整個執(zhí)行過程中所有的跳轉(zhuǎn)指令的歷史信息都記錄下來,LBR只能記錄16條。但是BTB需要CPU向指定的一個緩沖區(qū)內(nèi)寫入跳轉(zhuǎn)信息,當(dāng)緩沖區(qū)滿時,CPU會觸發(fā)異常交給操作系統(tǒng)處理(將緩沖區(qū)內(nèi)容寫入文件中),LBR是循環(huán)的寄存器。使用BTB的程序性能明顯比LBR性能低。CFIMon檢查BTB的時機在兩個階段:一是當(dāng)緩沖區(qū)滿時,操作系統(tǒng)將所有歷史信息寫入另一個進程,由另一個進程進行合法性判斷;二是當(dāng)受保護進程執(zhí)行敏感系統(tǒng)調(diào)用時,另一個進程也進行歷史信息的合法性判斷。合法性判斷主要檢查間接控制轉(zhuǎn)移的跳轉(zhuǎn)目標(biāo)是合法目標(biāo)集合內(nèi)。如果所有間接控制轉(zhuǎn)移的歷史跳轉(zhuǎn)目標(biāo)在合法目標(biāo)集合中,認為當(dāng)前受保護進程沒有收到攻擊;如果有至少一個間接控制轉(zhuǎn)移的歷史跳轉(zhuǎn)目標(biāo)在合法目標(biāo)集合中,那么認為受保護進程受到攻擊。合法目標(biāo)的集合是在線下通過靜態(tài)分析獲得的,并且存儲在檢查進程中。
大多數(shù)的攻擊都依賴于某種形式的控制劫持來重定向程序執(zhí)行,CFI(Control Flow Integrity)是一種運行時強制執(zhí)行的技術(shù),用以抵御代碼注入、代碼重用攻擊,也不易受信息泄露攻擊。CFI在運行時強制執(zhí)行預(yù)期的控制流轉(zhuǎn)移,不允許執(zhí)行應(yīng)用程序控制流圖(Control Flow Graph,CFG)中未出現(xiàn)的轉(zhuǎn)移。精確的CFI會引入大量開銷,這一點激勵了更為實際的、粗粒度的CFI變體的發(fā)展;粗粒度CFI通過放寬限制條件來降低開銷,卻也給攻擊者提供足夠的利用空間。另外,CFI的作用也僅僅是用于阻止攻擊者對控制流的劫持,有研究表明(如Control-Flow Bending,CFB[13]),攻擊者可以僅僅通過控制函數(shù)調(diào)用的參數(shù),完成攻擊者的目的,如執(zhí)行任意代碼、執(zhí)行受限制的代碼和信息泄露。
從2005年CFI技術(shù)被提出,CFI技術(shù)已經(jīng)歷經(jīng)了10多年的發(fā)展。近兩年涌現(xiàn)出的大量CFI相關(guān)的研究工作,極大推進了CFI技術(shù)的發(fā)展。但是,CFI技術(shù)未來仍有很大的發(fā)展空間,仍然需要研究人員繼續(xù)深入研究,實現(xiàn)一種高可靠、低開銷的CFI技術(shù)。
(作者單位為中國科學(xué)院計算技術(shù)研究所)
參考文獻
[1]. Martín Abadi,Mihai Budiu, úlfar Erlingsson, and Jay Ligatti. 2005. Control-flow integrity.InProceedings of the 12th ACM conference onComputer and communications security(CCS ’05). ACM, New York, NY, USA,340-353.
[2]. Chao Zhang, TaoWei, Zhaofeng Chen, Lei Duan, Laszlo Szekeres, Stephen McCamant, Dawn Song, and Wei Zou. 2013. Practical Control Flow Integrity and Randomization for Binary Executables. In Proceedings of the 2013 IEEE Symposium on Security andPrivacy(SP ’13).IEEE Computer Society, Washington,DC, USA, 559-573.DOI=http://dx.doi.org/10.1109/SP.2013.44
[3]. Mingwei Zhang and R. Sekar. 2013. Control flow integrity for COTS binaries. In Proceedingsof the 22nd USENIX conference on Security(SEC’13). USENIX Association,Berkeley, CA, USA, 337-352.
[4]. Enes G?ktas,Elias Athanasopoulos, Herbert Bos, and Georgios Portokalidis. 2014. Out of Control: Overcoming Control-Flow Integrity. In Proceedingsof the 2014 IEEE Symposium on Security and Privacy(SP ’14). IEEE Computer Society,Washington,DC, USA, 575-589.
[5]. Ali JoseMashtizadeh, Andrea Bittau, Dan Boneh, and David Mazières. 2015. CCFI:Cryptographically Enforced Control Flow Integrity. In Proceedingsof the 22nd ACM SIGSAC Conference on Computer and Communications Security(CCS ’15). ACM, New York, NY, USA,941-951.
[6]. Lucas Davi,Ahmad-Reza Sadeghi, Daniel Lehmann, and Fabian Monrose. 2014. Stitching thegadgets: on the ineffectiveness of coarse-grained control-flow integrityprotection. In Proceedings of the 23rd USENIX conference on Security Symposium(SEC’14). USENIX Association, Berkeley, CA, USA, 401-416.
[7]. Mauro Conti, Stephen Crane, Lucas Davi,Michael Franz, Per Larsen, Marco Negro, Christopher Liebchen, Mohaned Qunaibit,and Ahmad-Reza Sadeghi. 2015. Losing Control: On the Effectiveness of Control-FlowIntegrity under Stack Attacks. In Proceedings of the 22nd ACM SIGSAC Conferenceon Computer and Communications Security(CCS ’15). ACM, New York, NY,USA,952-963.
[8]. Victor van der Veen, Dennis Andriesse, EnesG?ktas, Ben Gras,Lionel Sambuc, Asia Slowinska, Herbert Bos, and CristianoGiuffrida. 2015. Practical Context-Sensitive CFI. In Proceedings of the 22ndACM SIGSAC Conference on Computer and Communications Security(CCS ’15). ACM,New York, NY, USA, 927-940.
[9]. John Criswell, NathanDautenhahn, and Vikram Adve. 2014. KCoFI: Complete Control-Flow Integrity forCommodity Operating System Kernels. In Proceedings of the 2014 IEEE Symposium on Security andPrivacy(SP ’14).IEEE Computer Society, Washington,DC, USA, 292-307.
[10]. Vasilis Pappas, MichalisPolychronakis, and Angelos D. Keromytis. 2013. Transparent ROP exploitmitigation using indirect branch tracing. In Proceedings of the 22nd USENIXconference on Security(SEC’13). USENIX Association, Berkeley, CA, USA, 447-462.
[11]. Yueqiang Cheng, Zongwei Zhou,Miao Yu, Xuhua Ding and Robert H. Deng. 2014. ROPecker: A Generic and PracticalApproach for Defending Against ROP Attacks. In Proceedingsof the 2014 Network and Distributed System Security Symposium(NDSS‘14).
[12]. Yubin Xia, Yutao Liu, HaiboChen, and Binyu Zang. 2012. CFIMon: Detecting violation of control flowintegrity using performance counters. In Proceedings of the 2012 42nd Annual IEEE/IFIPInternational Conference on Dependable Systems and Networks(DSN)(DSN ’12). IEEE Computer Society,Washington,DC, USA, 1-12.
[13]. Carlini N, Barresi A, Payer M, Wagner D andGross TR. 2015. Control-Flow Bending: On the Effectiveness of Control-FlowIntegrity. In Proceedings of the 24th USENIX Security Symposium(USENIX Security 15). USENIX Association, Washington,D.C., USA