黃樺烽 王嘉捷 楊 軼 蘇璞睿 聶楚江 辛 偉
1(中國科學院軟件研究所可信計算與信息保障實驗室 北京 100190) 2(中國科學院大學計算機科學與技術學院 北京 100190) 3(中國信息安全測評中心 北京 100085)
軟件漏洞是指存在于操作系統或者應用程序中,可導致攻擊者在未授權情況下獲取或破壞系統數據的安全缺陷.軟件漏洞的成因主要包括軟件開發人員疏忽及水平受限、編譯器引擎存在缺陷、程序功能邏輯復雜、代碼模塊測試不充分等因素.鑒于軟件的代碼規模、功能組成、多線程并發、數據資源共享等高度復雜的機制,即使經過嚴格的代碼審查與測試也難以消除軟件中的安全漏洞.知名壓縮軟件Winrar被爆出的CVE-2018-20250等一系列漏洞已潛伏長達19年,通過該漏洞可以實現任意代碼執行,實現信息數據竊取、系統破壞、敲詐勒索等惡意攻擊,嚴重威脅到信息系統的安全.
軟件漏洞是網絡安全的主要威脅,國家計算機網絡應急技術處理協調中心(CNCERT)聯合國內多家單位建立了國家信息安全漏洞共享平臺(China National Vulnerability Database, CNVD),建立軟件安全漏洞統一收集驗證、預警發布及應急處置體系.漏洞利用是系統入侵滲透的主要手段,如何提高軟件漏洞的自動化挖掘與利用能力是攻防雙方共同關注的焦點.在有限資源條件下實現軟件漏洞的自動利用是未來的現實需求和技術發展方向,2014—2016年美國DARPA組織的CGC[1](Cyber Grand Challenge)比賽在該方向作了初步的嘗試,并受到業界的廣泛關注.
軟件漏洞的自動挖掘與利用實際已有長久的研究歷史.在軟件漏洞挖掘方面,微軟的Cui等人[2]提出了基于符號執行反饋的并行化Fuzzing方法;Zalewski等人[3]提出了AFL Fuzzing系統,實現了基于遺傳算法的路徑快速觸發,有效提高了漏洞挖掘能力.在軟件漏洞分析方面,加州伯克利的Song等人[4]提出了基于動態污點傳播分析的漏洞分析平臺BitBlaze,之后該團隊又提出了比特粒度的DECAF[5]污點傳播分析系統.漏洞利用方面代表工作包括AEG[6],APEG[7]漏洞利用自動生成系統等.近年來,人工智能方法被引入到漏洞挖掘研究中,代表性工作包括Xu等人[8]提出的Gemini,通過對代碼進行深度學習建模的方式,檢測程序中存在的漏洞函數.相關方法和系統有效提高了漏洞挖掘與利用能力,但仍存在較大的局限性.主要包括:1)缺少統一的模型和框架,漏洞挖掘、漏洞分析、漏洞利用環節的方法難以形成一體化的能力,各環節的結果難以互相支撐;2)資源消耗大,難以支撐實際需求,現有的漏洞挖掘采取并行集群,需要大量計算資源;3)漏洞分析和漏洞利用基于傳統污點傳播、符號執行,時間及空間復雜度高,分析效率低,常因計算資源不足失效;4)漏洞自動利用生成只支持控制流劫持等有限的類型,擴展類型支持工作量大.
CGC是實現漏洞挖掘、分析、利用完全自動化的一次嘗試,參賽團隊通過建立“自動攻擊防御系統”,在無人干預的條件下,自動尋找程序漏洞、生成利用攻擊敵方、以及部署補丁抵御敵方的攻擊,目的在于探索完全無人干預條件下的網絡入侵、網絡防御方面的可行性方案,構建出一套具備自主攻擊及自我防御的軟件.該比賽是軟件漏洞挖掘與自動化利用的里程碑,從系統實現的角度證明了智能化自動攻防的可行性.但是,CGC比賽中采用了裁剪后的操作系統,程序的可執行文件格式等均進行了一定的修改,距離實際應用系統仍有較大的差異,難以應用于實際問題的分析.
漏洞自動挖掘與利用涉及多個環節,需要將各環節有效結合才能發揮作用,本文提出了基于程序運行時信息的Weak-Tainted漏洞描述模型,在該模型下優化污點傳播方法和輸入求解方法,基于2種優化方法指導模糊測試數據生成、漏洞自動分析及漏洞利用自動構造,構建了一套自動化的有限資源條件下的漏洞挖掘與利用框架,實現了輕量級的漏洞挖掘和利用,有效提高漏洞挖掘與利用的效率和準確性,并基于Ubuntu現實系統下的系列實驗,驗證了本文方法的有效性.
本文主要貢獻和創新點有4個方面:
1) 提出了基于程序運行時信息的Weak -Tainted漏洞描述模型,通過脆弱性和污點屬性對漏洞進行定義和描述,該模型支持漏洞挖掘、分析、利用過程中對漏洞進行統一描述,解決當前漏洞研究各環節的結果難以互相支撐的問題,形成了一體化的描述規范;
2) 提出了字節級標簽的高效動態污點傳播分析優化方法,基于多級頁表存儲污點結構屬性信息,通過值校驗消除傳播誤標記,解決了現有污點傳播分析方法時間與空間復雜度高,難以應用于有限資源環境的問題,同時提高了污點傳播準確度;
3) 提出了基于輸出特征反饋的輸入求解方法,基于輸入點和輸出點的關系統計分析預判進行輸入求解和驗證,解決了字符表映射運算的符號表達式難以構建導致符號執行求解失敗的問題,降低了計算資源消耗,同時提高了輸入求解的成功率,支撐漏洞挖掘與漏洞利用生成所需的輸入求解能力;
4) 實現了漏洞自動挖掘與利用生成原型系統,支持漏洞描述模型規則的定義和擴展,針對真實系統中的漏洞程序,驗證了字節級標簽動態污點傳播方法、基于輸出特征反饋的輸入求解方法、程序動態運行時Weak-Tainted漏洞描述模型的有效性.
軟件漏洞自動挖掘目前主要基于模糊測試方法,其基本思想是通過變異算法提供各種非預期輸入,啟發程序執行更多的代碼模塊和路徑,監視目標程序在處理該輸入是否出現異常的動態測試方法.經過多年的發展,模糊測試可分為四大類型,包括:1)測試用例隨機生成,如zzuf[9];2)基于目標靜態數據結構信息生成測試用例,如基于已知文件格式和網絡協議規范,代表性研究工作有Peach,Sulley[10]等;3)基于遺傳迭代的代碼覆蓋率反饋變異,代表工作有honggfuzz[11]和AFL[3]等;4)基于符號執行和求解生成用例,遍歷程序執行路徑,代表工作有KLEE[12],SAGE[13]以及商用版本SpringField等.這些代表的模糊測試工作僅具有單獨的漏洞挖掘能力,形成的數據難以有效支撐后續的漏洞分析與漏洞利用自動生成工作.
軟件漏洞自動利用目前主要基于模糊測試技術和數據流分析方法求解生成使程序陷入可利用狀態的輸入數據,再根據該輸入和狀態自動求解生成漏洞利用代碼.而可利用狀態通常會伴隨著內存訪問越界、格式化字符串受惡意操作、系統調用接口參數受惡意操作等異常情況出現,基于這些異常信息進行利用構造,最終實現控制流劫持等方式的任意代碼執行.漏洞自動利用的代表性工作有2011年Avgerinos等人[6]提出的AEG,該工作使用了指令動態插樁技術和符號執行進行漏洞利用樣本自動生成,驗證了漏洞自動利用生成的可行性.但是,該方案依賴于程序源代碼進行程序錯誤靜態搜索,即可利用狀態的搜索,不適用于非開源的二進制程序.
2012年Cha等人[14]針對二進制程序提出了構建基于索引的內存優化模型,優化符號化內存的加載問題,提高符號執行的效率,形成漏洞自動挖掘與利用代碼自動生成的方案Mayhem.該方案在檢測可利用狀態方面,主要包括緩沖區溢出和格式化字符串2方面,針對指令指針符號化檢測和構造控制流劫持的漏洞利用.但是Mayhem只對30個Linux系統調用和12個Windows庫函數進行建模分析,無法高效處理復雜的程序,無法處理未建模系統調用函數引起的符號執行計算錯誤問題.
在漏洞利用樣本多樣化生成方面,2013年Wang等人[15]提出了一套針對控制流劫持類漏洞的利用樣本多樣性自動生成方法PolyAEG.該方法的基本思想是基于動態污點分析發現所有的程序控制流劫持點,然后基于等效指令替換構建不同的控制流轉移模式,實現漏洞利用樣本的多樣性生成.PolyAEG針對控制流劫持類漏洞實現了一套完整的漏洞利用樣本多樣化生成的自動化構造方案,測試驗證了8個現實漏洞程序,其中針對單個控制流劫持漏洞最多生成了4 724個利用樣本變種.該方案是一種較為有效的控制流劫持類漏洞自動利用生成方法,但對于尚未觸發控制流劫持點的POC樣本難以生成有效的利用,而當前模糊測試生成的POC大多未觸發控制流劫持.

Fig. 1 Control flow hijacking vulnerability logic and exploit principle圖1 控制流劫持漏洞邏輯與利用原理
在堆漏洞利用方面,He等人[16]提出了基于堆溢出崩潰和溢出修復的堆漏洞可利用性評估方法HCS IFTER,該方法首次提出了通過修復被破壞的內存,使得程序能夠繼續執行,并檢測被破壞的內存數據后續使用情況,進而分析判定內存破壞點是否潛在利用條件,雖然該方法能夠檢測出觸發崩潰樣本本身是否潛在可利用性,但還不具備內存數據重新布局和構造的能力,即使檢測出當前條件不可利用也不能得出該漏洞不可利用的結論.針對堆溢出內存數據的重新布局和構造,Wang等人[17]在2018年提出了一種基于現有崩潰路徑,結合導向性模糊測試的技術,觸發堆漏洞程序更多可利用狀態的方法Revery.Wu等人[18]在2018年提出了針對內核堆釋放后使用漏洞的自動利用框架FUZE,該框架利用內核模糊測試技術提供更多的內核崩潰上下文環境作為漏洞利用的依據,并利用符號執行在不同的上下文環境中去嘗試利用目標漏洞.
CGC獲獎團隊之一的加州大學圣塔芭芭拉分校的Shoshitaishvili等人[19-20]提出了面向二進制軟件攻防的基礎分析平臺ANGR.該工作主要是將已有基礎性軟件分析方法進行模塊化實現,形成一套功能完整的分析框架,包括:控制流圖生成、動態分析執行、符號執行、逆向切片等內容.在ANGR基本框架的基礎上,可以實現針對二進制程序的漏洞挖掘、分析與利用等具體應用場景,并進行自動化系統功能構建.ANGR的作者將該系統應用到了CGC比賽中,將多種程序分析方法集成到同一個系統中,并實現了自動化,通過組合不同的分析技術進而構建面向二進制軟件的自動攻防系統.但是ANGR系統在分析現實程序及靜態鏈接的BCTF樣本時卻因為性能和兼容性等因素顯得捉襟見肘,難以直接支撐實際工作.
與以上方法不同的是,本文提出了有限資源條件下基于動態模型的漏洞自動利用方案,在分析了不同類型漏洞利用方法的差異基礎上,提取共性特征建立程序動態運行時的漏洞描述模型,基于漏洞的動態Weak-Tainted模型和優化的污點分析技術檢測和判斷漏洞類型,并提取利用約束條件,最后基于輸入求解技術自動生成利用樣本.
當前軟件漏洞自動挖掘與利用的現實場景大多是在給定的二進制條件下,基于大規模分布式資源對目標軟件進行模糊測試,對產生的程序異常點進行分析,篩選出存在控制流劫持等可利用條件的輸入,對其進行數據流分析和內存布局分析,通過ShellCode布局和攻擊鏈構造實現利用代碼自動生成.
以典型的控制流劫持類漏洞為例,所針對的目標和利用過程如圖1所示.其中,圖1(a)表示一個典型的漏洞程序的基本邏輯,圖1(b)表示針對該程序的利用代碼生成原理.圖1(a)的目標程序包括輸入接口(圖1(a)中的ReadFileRecv函數),處理輸入數據的指令序列(圖1(a)中的階段1),通常情況下有數據的轉移復制,甚至是編碼與解碼的運算(圖1(a)中的階段2),在對輸入數據進行編碼或解碼運算后,程序執行至脆弱點(圖1(a)中的階段3).圖1(b)的漏洞利用代碼生成主要分為控制流劫持檢測、內存布局構造、約束條件求解等步驟:①基于模糊測試得到異常樣本,所謂的異常樣本在傳統漏洞挖掘方法里大多是指造成程序崩潰的輸入;②使用數據流分析檢測異常點是否為可利用的控制流劫持;③分析控制流劫持過程發生時的ShellCode內存布局,設計轉移到ShellCode的攻擊鏈,如圖1(b)利用中間跳板指令jmpesp來觸發??臻gShellCode代碼的執行;④基于ShellCode和攻擊鏈部署條件提取約束,基于符號執行等方式求解生成利用代碼.

Fig. 2 Vulnerability automatic discovery and exploit based on vulnerability description model圖2 基于漏洞描述模型的漏洞自動挖掘與利用方法
以上是當前漏洞自動挖掘與利用的典型過程,可總結為挖掘、分析、利用構造3個部分,而在前期的研究中我們注意到現有研究主要存在3個方面的難點與挑戰:
1) 在漏洞自動挖掘方面,基于大規模計算實現并行化的模糊測試,Peach,Sulley等Fuzzing工具通常會生成大量的冗余樣本,缺乏分析數據的反饋,漏洞挖掘與利用缺乏統一描述模型,生成的數據難以相互支撐,造成漏洞挖掘與漏洞分析利用流程脫節;
2) 在漏洞自動分析方面,以BitBlaze和Decaf為代表的基于污點傳播的分析方法為主,以qemu虛擬機全系統模擬為基礎,其污點傳播算法消耗大量計算資源,在面對復雜的大型目標程序時,其性能難以滿足有限資源條件下的漏洞自動分析需求;
3) 在利用代碼自動生成方面,大多數自動利用基于已有的漏洞知識構造自動利用專家系統,只能針對特定類型的漏洞,針對不同類型的漏洞需重新編寫自動利用系統;另外,邏輯公式求解受到程序自身數據處理過程復雜性與路徑約束條件規模的影響,求解準確性和效率堪憂;同時,當前的符號執行求解在應對base64等存在映射表運算的編解碼情況難以求解,很難有效支撐漏洞利用代碼的自動生成.
針對當前漏洞自動挖掘與利用存在的難點與挑戰,本文提出了有限資源條件下的漏洞自動挖掘與利用方法,總體框架如圖2所示.首先,針對不同類型漏洞在觸發時的關鍵屬性,對漏洞進行歸類和建模,形成可被機器識別的、貫穿漏洞挖掘與利用全流程的程序運行時漏洞統一描述模型,該模型基于程序脆弱性特征和數據污點屬性進行描述和定義,本文稱之為Weak-Tainted漏洞描述模型;然后,基于動態污點傳播分析方法,結合漏洞描述模型給出的描述,在模糊測試過程中提取分支路徑對應輸入,并求解執行分支另一條路徑的輸入約束條件,得到下一輪模糊測試的新種子,用于提高漏洞發現的能力和效率;之后,根據漏洞描述模型和動態污點分析判斷模糊測試獲得的漏洞是否可利用,在可利用條件下自動提取漏洞利用所依賴約束條件;最后由輸入求解引擎進行利用約束條件的求解,生成漏洞利用樣本.
本節從Weak-Tainted漏洞描述模型、基于頁表標簽值校驗的動態污點分析方法、基于輸出特征反饋的輸入求解方法和原型系統設計與實現4個方面對本文提出的有限資源條件下的漏洞自動挖掘與利用方法展開介紹.

Fig. 3 Weak-Tainted vulnerability discription model during program dynamic runtime圖3 Weak-Tainted程序動態運行時漏洞描述模型
不同類型的漏洞,在運行過程中表現出來的特征具有差異性.控制流劫持類漏洞具有明顯的指令特征,表現在控制流轉移指令的目標位置受到輸入數據的污染,或者轉移目標位置的內容受到輸入數據的污染;堆溢出漏洞表現在訪問的內存空間超出了堆內存分配空間的范圍;格式化字符串漏洞可能引發控制流劫持異常,但需要精心構造受輸入控制的格式化參數,在觸發控制流劫持異常觸發之前,其具有自身的特征屬性,具體表現在調用具有格式化操作函數API時,其格式化參數受到輸入數據的污染控制;此外還有一些創建進程、管道調用命令行的API函數,如system,popen等,其關鍵參數受到輸入污染控制也可觸發任意代碼執行的漏洞.
為了對多種不同類型的漏洞進行統一描述,便于機器自動識別和利用代碼生成,同時也為了將漏洞挖掘、分析、利用各環節融合以相互支撐,本文提出了Weak-Tainted程序動態運行時漏洞描述模型,該模型包含脆弱點(weak)和污點屬性2個部分.如圖3所示,該模型定義了2個方面的內容:1)程序脆弱點集合,脆弱點集合包含Weak-INS和Weak-PC兩種類型.Weak-INS是可能引起漏洞的指令,包括觸發控制流劫持的指令call,ret,jmp以及通過執行系統調用執行任意代碼的指令int 0x80,syscall,sysenter等;Weak-PC是指潛在漏洞的指令地址集合,主要通過靜態分析敏感脆弱函數地址,比如memcpy,strcpy,scanf,system等容易產生漏洞的函數,獲得模塊信息和相對偏移地址,最后映射到動態運行時的函數指令地址,該地址通常是函數入口地址,當然也可以根據實際情況需求定義為函數中間某條指令的地址.2)檢測點定義,需要給脆弱點集合的每個脆弱點定義檢測點,檢測其是否被輸入污染(tainted),每種脆弱類型都有其對應的存儲單元敏感點,存儲單元可以是內存或者寄存器,可基于常量地址、指令操作數、寄存器或操作數作為指針索引等進行定義,當脆弱點定義的敏感點受到輸入數據污染,可初步判定為存在漏洞.
一個脆弱點可以定義多個檢測點,這些檢測點可以是滿足其一的關系或者同時滿足的關系,比如call指令的檢測點,包括3種情況:1)call指令op0操作數指向的內存為污點,這一類會導致指向的執行指令可以被輸入改寫;2)call指令的op0操作數為污點,這一類會導致call指令的地址可以被輸入改寫,使程序跳轉去執行預設的攻擊代碼;3)op0的指針被污染,例如call [eax+8]中的eax被污染,這種類型會導致獲取跳轉地址的位置可被改寫,同時引起跳轉地址的位置可被改寫.類似的jmp,ret指令也存在3種類型的脆弱類型.
當前漏洞利用的自動構造依賴于特定的漏洞類型,本文分析收集了常見的控制流劫持、格式化字符串、系統調用后門類型的漏洞利用要素和步驟,基于Weak-Tainted程序動態運行時的漏洞描述模型,實現了相應的漏洞模型庫及模型庫的解析引擎,形成基于Weak-Tainted漏洞描述模型的漏洞利用樣本自動生成框架.
在漏洞描述模型庫的表示方面,以常見的控制流劫持漏洞為例,call指令的劫持與ret指令的劫持在利用構造的具體實現上存在差異,并且call指令的劫持還分為call *Taint,call Taint和call [Taint]三種情況,這些差異導致漏洞利用的構造步驟存在一定差異.本文分析總結了這類漏洞的利用構造步驟,在此基礎上形成漏洞判斷條件和利用要素描述模型庫.系統實現的模型庫對call指令的多種情況分別給出了不同的模型描述,其中call參數為寄存器的情況給出了2種模型,例如calleax,包括eax為污點和eax指向的內存為污點2種狀態;另外call參數為內存的情況給出了3種模型,例如call [eax],包括eax為污點、[eax]為污點和[eax]指向的內存為污點的3種狀態.針對jmp指令,與call指令的描述模型一致,針對ret指令,描述了esp為污點和[esp]為污點的2種狀態.如圖4所示,是針對call參數為內存情況的一種描述.

Fig. 4 Case of vulnerability description model圖4 漏洞描述模型樣例
圖4的樣例給出的Weak_Type:INS是通過特定敏感指令進行判別,Weak_Value:call給出的敏感指令是call,此外還有jmp,ret等指令.而本方案還支持通過指令地址進行判別的方式,表示方式為 Weak_Type:ADDRESS,如針對格式化字符串漏洞或者敏感系統調用API,通過函數入口檢測相應參數是否受到控制,函數的入口地址則是通過靜態分析結合動態庫基地址計算得到,表示方式為Weak_Value:0x80888888.
程序執行到脆弱位置時需要檢測相應條件再判斷是否存在可利用點,不同的情形檢測不同的參數,因此設計了圖4所示的OpCondition域,用于區分檢測指令(如call)的參數是寄存器還是內存,針對不同情況需要檢測不同的位置并使用不同的步驟進行構造,因此也需要分別給出不同的模式配置,針對無需區分的情況OpCondition項為空.
另外配置的污點檢測Tainted_Conditions存儲單元可能是指令參數或者函數參數,這些參數都可以通過寄存器、指令操作數及偏移,多次指針索引進行描述和表示,如圖4中的storagetype域和type_index域.系統設計并實現的storagetype包括reg,memory,point三種類型,用于表示需要通過多少級索引獲取存儲單元地址,type_index包括eax,ecx,esp,op0,op1等通用寄存器和指令操作數,作為存儲單元的索引.圖4的reg和op0組合表示檢測call指令的op0本身是否被污染,如果是memory和op0組合,表示檢測op0指向的內存是否被污染;如果是reg與esp組合,表示檢測esp是否被污染;memory與esp組合,表示檢測[esp]是否被污染;point與esp組合,表示檢測*[esp]是否被污染.

Fig. 5 The page table structure of taint status圖5 污點存儲頁表結構
系統實現了漏洞利用過程中所需步驟的子模塊,包括但不限于:1)內存布局分析子模塊,通過污點傳播技術記錄程序執行過程中存儲單元污點狀態,用于搜索內存中污點數據布局區域和對應的污點源關系,為ShellCode等注入代碼提供存儲空間;2)內存值搜索子模塊,用于搜索內存中存儲了特定數據的空間,返回相應的內存地址,在利用構造過程中提供跳板指令位置、函數指針、或者數據索引指針等特定數據結構位置;3)格式化字符串構造子模塊,針對格式化字符串規則,構造任意地址的讀或寫的能力;4)約束表達式構建與求解子模塊,基于模式庫的約束模型,生成具體的利用構造約束條件,最后基于符號執行和輸出特征反饋的求解方案進行利用樣本生成.圖4所示的Exploit_Steps 則是通過調用內存布局分析子模塊搜索出連續的污點內存區域,通過內存值搜索子模塊搜索特定值的地址,通過約束表達式構建求解子模塊實現其2條約束條件的構建與求解.
本文的系統基于上述的子模塊,在漏洞描述模型庫中添加了針對calljmp指令各5種描述模型規則,ret指令2種描述模型規則的控制流劫持漏洞的自動分析判定與利用生成模型.同時添加了針對各類格式化字符串的漏洞分析判定與利用生成規則,包括基于格式化字符的棧溢出和基于格式化字符串的任意地址寫2種利用構造方式.另外還添加了針對popensystem等敏感函數參數受污點控制的后門漏洞判定與利用構造模式.
本文通過構建Weak-Tainted的程序動態運行時漏洞描述模型,可配置漏洞描述模型的脆弱點和對應的敏感存儲單元,結合動態污點分析技術,可以實現機器對漏洞的認知與識別.同時,本文提出的漏洞模型具有可擴展的能力,對于新型漏洞可基于該漏洞模型描述進行自定義擴展,提高了機器針對新型漏洞的自動識別與利用構造能力,免去了重新編寫漏洞識別與利用代碼生成算法的復雜工作.
針對現有污點傳播方法準確性和效率的缺陷問題,本文提出了基于頁表標簽的動態污點傳播分析方法,通過優化污點狀態記錄方法、污點傳播計算方法,實現低時間與空間復雜度的準確計算.在污點狀態記錄方面,我們解決了內存污點快速查詢與記錄、寄存器狀態記錄等問題;在污點傳播計算方面,本文解決了因未監控內核指令導致的污點誤標記導致的誤報問題.
針對進程污點數據記錄問題,本文借鑒操作系統內存管理的方法提出了基于多層頁表的污點狀態壓縮記錄方法.以32 b系統為例, 32 b系統進程的最大虛擬內存地址空間為4 GB,但是進程實際用到的內存空間遠遠不足4 GB,通過3級頁表索引的方式,如圖5所示,未被操作或未被污染的內存區域在污點狀態頁表內可以用空表項表示,而污點內存則由對應的頁表項指針指向污點標簽記錄結構.查詢和更新指定內存地址的污點狀態的時間復雜度為O(1),記錄程序污點狀態所需內存空間最多與目標程序內存空間呈線性關系,即空間復雜度O(n).
針對寄存器狀態記錄問題,32 b系統的寄存器包括通用寄存器、標志寄存器、浮點寄存器等,考慮到通常情況下,進程的低地址0x0000~0x1000位置不會被使用,將CPU寄存器映射至頁表中的低地址空間,并且為每一個線程維護了一套寄存器的污點狀態信息.
污點標簽結構記錄了存儲單元的污點源數量、污點源標簽組、存儲單元值、對應指令地址和序號等信息.污點源標簽采用偏移量和字節數的壓縮格式表示:{[offset1,size1],[offset2,size2],…,[offsetn,sizen]},標簽之間通過求并集進行污點源的合并,標簽求并集可將重疊的連續字節進行壓縮合并,為了使標簽合并高效進行,從初始化的單標簽開始,合并標簽按照升序進行排列,可以保障之后的多標簽之間的合并只需按歸并排序的策略依次合并,時間復雜度可控制在O(n),其中n為標簽組的數量,且在實現當中限定了標簽組最多支持127組,由于標簽組是壓縮存儲,127組在實際案例測試過程中是足夠用的,如果超出該數量多出的標簽會被丟棄,但能保證消耗的空間復雜度不超過O(n).
在污點傳播計算方面,本文解決了基于指令關系分析的字節粒度污點傳播計算和因未監控內核指令引起的污點過標記誤報問題.字節粒度污點傳播計算為每個字節單獨計算和維護污點狀態信息,通過反匯編解析指令,提取指令操作數之間的關系,為了達到字節級粒度的精度,操作數間的關系并不再是簡單的一對一或者一對多關系,而是復雜的多對多關系,本文用如下表達式模型表示一條指令派生出的操作數關系組,其中每個符號表示一個字節:

根據關系組,更新目標操作字節yi的污點狀態,污點狀態的更新首先是查詢目標操作字節對應的所有源操作字節x1,x2,…,xni的污點狀態,并計算這些污點標簽的并集作為目標操作字節yi的污點標簽,然后更新yi的污點標簽,同時記錄該字節的值以備后續使用.
通過分析發現,用戶態的內存在釋放回收后大多并未清除數據,并且其中有一部分被標記為污點,而當重新分配之后如果由內核模塊對其初始化無法在用戶態檢測到存儲單元被漂白的操作,因此導致了后續的傳播誤報問題.而本文不去監控內核指令是由于內核態指令執行占比大,初步測定是用戶態指令數量的3倍,會導致性能的極度下降.針對上述問題,本文提出的動態污點傳播方法帶有值校驗檢測的能力,污點傳播計算過程中,如果查詢源操作字節的污點狀態是污點,需要校驗此時該字節的值是否與標記該字節為污點時的值是否一致,如果不一致將其漂白,從而解決了因未監控內核指令引起的污點過標記誤報問題.另外,通過大量實驗(包括office word,notepad++,kmplayer等常用軟件)發現用戶態污點數據經過內核,再傳播到用戶態的情況較為少見,在針對應用程序漏洞挖掘與利用構造這一應用場景下可以忽略不計,因此無需去監控內核態的指令.
輸入求解是為了獲得滿足某種條件的輸入,比如執行某一條程序分支,或者使得某個時刻內存或寄存器的值滿足某個約束條件.目前符號執行是理論可行的解決方案,但在實際應用中卻面臨計算關系難以用算術表達式或者邏輯表達式進行表示、符號表達式太多、約束條件太復雜等挑戰.比如Base64編解碼、AES加解密算法,其運算過程都存在基于映射表進行字符替換的過程,從輸入到輸出難以構造對應算術表達式,也就難以進行求解.
針對符號執行的缺陷,本文提出了基于輸出特征反饋的輸入求解方法,用于解決映射表運算等導致符號執行無法求解的問題.首先通過污點分析建立輸入與目標輸出之間存在關系的字節集合,此處的污點傳播支持指針為污點的傳播,因此污點狀態不會因為映射表丟失,并且在傳播過程中為指針標簽增加了指針屬性以便識別指針類型的污點數據.目標輸出字節集表示為T={t1,t2,…,tk},輸入字節集表示為X={x1,x2,…,xn},建立輸入與輸出字節級的污點關系,用如下函數模型表示它們之間的關系,其中函數g是一個未知的黑盒函數.求解的目標是找到一個輸入集合X={x1,x2,…,xn}使得輸出集合T剛好為T={t1,t2,…,tk}.
本文提出的輸出特征反饋的輸入求解基本思想是對輸入字節逐個進行求解.圖6給出了約束關系的樣例,Conditions1中的0x0,4表示待求解目標偏移位置0的連續4 B,相關的輸入字節為偏移0x1d的連續4 B;Conditions3中的0x8,4表示待求解目標偏移位置8的連續4 B,相關的輸入字節為偏移0x1d的連續12 B,以此類推.本例中不難看出,Conditions1的輸入字節是其他Condition輸入字節的子集,這種情況應當優先求解Conditions1.目標字節集在各個Conditions中沒有交集,而輸入字節集中是可能存在交集的.

本文通過算法1實現條件求解順序的排序.首先對約束條件輸入字節進行排序,決定字節的求解順序,如算法1所示,按照關聯輸入字節數少的約束優先原則進行排序,如果InputSet1∈InputSet2優先求解InputSet1對應的約束條件,再得到InputSet1之后對于約束中的部分變量將設置為常量,然后再求解剩余的輸入集合{InputSet2-Inputset1}.
算法1.條件求解順序的排序算法.
輸入:多個待求解的約束集合中的輸入集M={Set1,Set2,Set3,…,Setn};
輸出:約束求解順序.
①OrderSets={}
② WhileM!=OrderSets
③MinSet=one ofM-OrderSets;
④ For each set inM-OrderSets
⑤ Ifset∈MinSet
⑥MinSet=set;
⑦ End If
⑧ End For
⑨OrderSetsinsertMinSet;
⑩ End While
從算法1可知,如果這些約束的輸入條件沒有子集關系,它們的求解順序不會進行調整,將按照原有約束條件的順序進行求解.
針對單個約束,ti==f(x1,x2,…,xni) ,使用算法2進行處理,算法2通過反復修改ti對應的關系輸入字節集{x1,x2,…,xni},動態運行監控對應的ti值變化,采用控制變量法,控制單個輸入變量的變化,基于遺傳迭代和貪心算法思想,保留最接近目標值結果的一組輸入進行下一輪的變量修改.多次反饋迭代調整改變輸入,直到輸出的ti滿足求解條件或者求解失敗.這種方法能夠有效應對大多數編解碼算法.
算法2.單約束條件輸出反饋求解算法.


①BestInput=Init(x1,x2,…,xnk)*初始輸入為約束求解前的一個未滿足的輸入*
②BestTi=f(x1,x2,…,xnk)*所獲得的離目標值最接近的結果*

④ ForxiInBestInput(x1,x2,…,xnk)
⑤xi1=xi+1;
⑥xi2=xi-1;
⑦Ti1=f(x1,x2,…,xi1,…,xnk);
⑧Ti2=f(x1,x2,…,xi2,…,xnk);

⑩BestTi=Ti1;
本文實現了漏洞自動挖掘與利用原型系統AOTA,該系統框架流程如圖7所示,包含污點分析、輸入求解2個基本引擎和一個漏洞描述模型,基于漏洞描述模型輔助實現漏洞挖掘、分析、利用全流程系統.漏洞挖掘主要基于模糊測試基礎方法,結合漏洞描述模型及其污點分析和輸入求解技術,反饋獲得額外的變異樣本輔助漏洞挖掘,提高漏洞挖掘的效率;漏洞分析目的是分析漏洞類型,判定漏洞是否為可利用,主要基于污點分析引擎和漏洞描述模型來完成;漏洞利用自動生成是針對可利用的漏洞及樣本,結合分析獲得的約束條件、關聯的輸入字節,及其對應的漏洞模型和輸入求解引擎實現利用樣本的自動生成.AOTA系統的污點分析引擎基于QEMU的指令插樁實現,基于udis86反匯編結果編寫傳播規則,污點源是通過監控獲取外部輸入的系統調用標記,每個字節采用不同標簽進行區分.輸入求解引擎所需的數據采集基于Python框架,迭代調用QEMU引擎監測求解點的輸出結果,從而進行遺傳迭代反饋預測求解,約束關系式是在漏洞分析過程結合漏洞模型描述提取的,求解引擎除了使用本文提出的優化方法,還結合了符號執行求解引擎Z3.漏洞描述模型以JSON格式數據形成漏洞模型配置文件,模型庫包含了calljmpret等控制流劫持漏洞,格式化字符串漏洞,systempopen等敏感命令行執行后門漏洞;可由系統導入進行配置;模型的解析主要用來根據配置執行對應的檢測操作和求解操作,在QEMU動態運行目標程序過程中進行;另外,模型庫內置了2個不同版本的執行shell的ShellCode.

Fig. 7 Framework of prototype system圖7 原型系統框架流程
參照國際同行選擇實驗測試對象的情況,如Driller[20]以2016年CGC自動攻防比賽的樣本作為測試用例,本文以2018年DEFCON和百度安全聯合舉辦的DEFCON China大會上的BCTF比賽樣本[21]作為測試集,其中共有50個含有漏洞的Linux二進制程序,測試漏洞自動挖掘與利用能力.本文的測試環境均為Intel Xeon?CPU E5-2630 v3 @2.4 GHz × 16, 內存64 GB,硬盤2 T的聯想RD650服務器,操作系統Ubuntu 16.04.5 64 b.
本文的原型系統也直接應用于2018年BCTF比賽,在99 s內完成第1個漏洞的自動挖掘和利用,在2 min48 s內完成了4個樣本的自動利用生成,而人類戰隊完成第1個漏洞利用花了17 min,與國內外知名CTF戰隊同臺競技,獲得了機器人自動攻防單項排名第1的成績.
本文實現的污點傳播模塊包括污點源標記、污點傳播計算、污點異常檢測3個部分.污點源標記通過監測外部輸入實現,當檢測到外部設備、文件或網絡數據讀入內存時,將對應的內存字節標記為污點,并使用對應的輸入字節偏移量為標簽進行標記.污點傳播計算通過代碼插樁提取目標程序執行的指令,針對Linux的目標程序,實驗采用qemu的用戶態模式進行代碼插樁,然后反匯編指令,解析指令語義更新內存和寄存器的污點狀態,記錄對應污點源標簽.污點異常檢測根據配置的漏洞模型規則獲得檢測點和檢測內容,查詢對應內存或寄存器操作數是否為污染狀態,如果是污染狀態取出污點源.
實驗從測試集選取了12個棧溢出和格式化字符串漏洞樣本,使用對應的POC樣本單獨測試AOTA系統的污點傳播模塊,實驗均能基于漏洞模型通過污點分析檢測到漏洞點,并且未發現誤報和漏報.實驗表明:本文改進后的污點傳播方法其準確度能夠滿足漏洞分析與判定的需求.
性能方面,測試的12個樣本中,污點分析消耗的時間最多的是357 ms,最少的是19 ms,平均值為174.25 ms.消耗的內存最多的為25.73 MB,最少的為12.6 MB,平均消耗22.2 MB.另外,實驗統計了1 000組指令數量與污點分析時間消耗關系數據,如圖8所示,橫軸是指令數量,縱軸是消耗的毫秒時間.由于程序開始執行時自身需消耗較多的時間用于IO加載,對污點分析速度的測算影響較大,實驗篩除指令數量少于20 000條的測算數據,對剩下的速度數據求平均值,得到的污點傳播速度為每秒57.34萬條指令.

Fig. 8 Velocity curve of taint analysis圖8 污點分析速度曲線
本文提出的基于輸出特征反饋的輸入求解方法在原型系統AOTA中進行了實現,與ANGR中的符號執行模塊進行了實驗對比,測試輸入數據分別經過atoi轉換、base64編碼、hex編碼處理的求解能力,從求解能力和求解效率的角度進行評估.通過學習ANGR符號執行的用例,發現其主要應用在路徑求解,并且其約束條件是用輸出含有指定字符進行描述.為了適應ANGR的應用場景,實驗構造了符合ANGR應用場景的測試用例,由標準輸入經過編碼轉換,然后將結果與預設值匹配,匹配成功則使用標準輸出打印出Success字段.
實驗分別使用ANGR和AOTA對atoi,base64,hex編碼進行求解,求解成功與否如表1所示.其中對于atoi的轉化均能求解成功;ANGR對base64編碼的求解失敗,但AOTA能夠求解成功,這是因為base64中有映射表轉換的操作,ANGR的符號執行無法支持該轉換的求解,導致求解出來的結果是錯誤的;對于hex編碼的求解ANGR只能求解少量字符,當字符串長度不少于3個時,內存消耗超過64 GB求解失敗,而AOTA的求解不受長度限制.總體而言,AOTA的求解能力要優于ANGR.

Table 1 Results of Input Solving Ability

Fig. 9 Comparison of time and memory consuming to solve atoi圖9 求解atoi時間與內存消耗對比圖
實驗對比分析了AOTA和ANGR分別求解atoi和hex編碼的資源消耗.圖9(a)是ANGR和AOTA求解atoi轉換的時間消耗對比,橫軸是字符個數,縱軸是消耗時間的秒數.ANGR僅在字符數少于3時有求解速度上的優勢,當字符數達到6以上時求解時間維持在15 s左右,而AOTA的求解時間穩定在10 s左右.圖9(b)是ANGR和AOTA求解atoi轉換的內存消耗對比,橫軸是字符個數,縱軸是內存消耗,單位MB.ANGR在求解6字符以內的atoi時內存消耗從130 MB隨字符數增多而增加,之后穩定在145 MB左右,而AOTA一直維持在60 MB左右的內存消耗.
另外,求解出的atoi輸入存在非預期解,這是由于計算過程中存在整數溢出導致,如求解atoi(x)值為87654321,ANGR給出的結果是字符串“8677588913”,而并非字符串“87654321”,此處預設的字符長度并非求解出來的真實長度,求解提前到達了有解情況的最大輸入長度,這也導致了ANGR后續求解時間和內存消耗不再隨著預設的字符數增加而增加,而是保持在穩定的范圍內.而AOTA的求解更多的時間消耗在輸入輸出關系的建立和收集,另外atoi的輸出目標都是一個整型數,恒定為4個字符,關系建立受輸入長度的影響較小,因此求解時間較為穩定.
在求解hex編碼能力方面,ANGR僅在字符數少于3時能夠求解成功,單字符所需的求解時間10 s左右,內存消耗400 MB以上;2字符所需時間將近300 s,內存消耗8 GB以上;當字符數達到3及以上時所需內存超過64 GB求解失敗.而AOTA的求解時間隨著字符數在1~10之間的增加,時間消耗在20~100 s的區間內遞增,所消耗內存維持在60 MB左右,并且均能求解成功.總體而言,AOTA的求解性能優于ANGR.
與ANGR相比,本文的求解方法建立在優化的基于頁表標簽的動態污點分析,通過頁表的多級索引提高了檢索計算的效率,通過值校驗的方式消除了污點數據的誤標記擴散,避免了污點爆炸引起的內存額外消耗,通過污點相關性篩選出需要求解的輸入字節,降低了直接使用符號執行求解的復雜度,一定程度消除了傳統符號執行求解中的路徑爆炸問題.實驗也表明,在針對atoi,base64,hex編碼的反向求解方面,本文的求解能力要優于ANGR,其中對atoi的求解效率提升了33%左右,而hex和base64編碼ANGR無法成功求解.
本文提出的漏洞描述模型與污點分析、輸入求解結合,優化了程序漏洞的自動挖掘,在AOTA原型系統中進行了實現,與AFL-Fuzz進行了實驗對比.實驗以BCTF2018的50個樣本作為樣例測試程序,使用相同的種子文件,分成2組,每組25個測試用例,同時對50個測試用例程序進行了24 h的漏洞挖掘測試.使用4臺配置型號相同的RD650服務器并行測試,配置說明見本節開頭,其中2臺服務器測試AFL的漏洞挖掘能力,另外2臺服務器測試AOTA系統的漏洞挖掘能力.測試每隔10 min統計1次AOTA和AFL挖掘出漏洞的樣本數,24 h的統計結果曲線如圖10所示.

Fig. 10 Comparison of vulnerabilities sequence diagram between AOTA and AFL圖10 挖掘漏洞樣本數時序對比圖
實驗表明:相同種子輸入、時間和測試用例,AOTA能挖掘出41個樣本的漏洞,而AFL只挖掘出了39個樣本的漏洞,并且這39個樣本的漏洞包含在AOTA的41個樣本范圍內,同時對比這39個樣本的挖掘時間發現,AOTA比AFL先挖掘出漏洞的樣本有12個,AFL比AOTA先挖掘出漏洞的也有9個,其中4個是由于樣本有大量的模型約束需要求解,消耗了30~60 min的時間用于約束求解,另外5個在誤差允許的范圍之內,但是AOTA挖掘出這39個樣本的平均時間為65.72 min,而AFL挖掘出這39個樣本的平均時間為121.03 min,總體上AOTA挖掘漏洞的能力與AFL相當,挖掘出漏洞的平均時間縮短了55 min,平均效率提升45.7%.
AOTA的模糊測試優化功能為18個測試樣本生成了新的測試用例,將這些用例作為AOTA新增的種子集合,與AFL在相同硬件配置環境下進行模糊測試路徑覆蓋對比測試,得到的路徑覆蓋數量如圖11所示,其中AOTA的平均覆蓋路徑數量為1 000,而AFL的平均覆蓋路徑數量為992,相比之下,AOTA的覆蓋路徑數量比AFL增加了0.8%.

Fig. 11 Comparison of paths coverage between AOTA and AFL圖11 樣本覆蓋路徑數量對比圖
本文實現的AOTA系統基于Weak-Tainted漏洞模型的漏洞識別和漏洞利用要素提取,并基于輸出特征反饋對輸入進行求解,實現漏洞利用自動生成,并以BCTF2018的50個樣本作為測試用例進行了實驗驗證,實驗統計結果如表2所示.
實驗結果表明:本文提出的自動利用生成方法在AOTA系統中得到了實現和應用,在50個測試用例中成功發現了41個用例的漏洞,比例達到82%,其中26個判定為可利用,比例達到52%,并進行了自動利用生成的嘗試,成功生成利用的有24個用例,比例達到48%.這些成功生成利用的漏洞中,包括棧溢出、格式化字符串和后門系統調用多種類型.

Table 2 Result of Vulnerability Automatic Discovery and Exploit
同時我們研究分析測試了ANGR的自動利用生成插件REX和AEG, 發現ANGR提供的測試用例是動態鏈接libc等動態庫,主模塊代碼量較少,其本身進行了局部優化;對于BCTF這類靜態鏈接libc等鏈接庫,程序代碼量較大(500 KB左右)的樣例,ANGR的插件無法成功進行漏洞自動利用生成,并且針對單個測試用例內存消耗高達30 GB以上.
為了更充分地對比,本文重新選取了10個含原代碼的測試用例,以動態鏈接庫的方式編譯出符合ANGR的AEG插件測試條件的二進制程序,同時用本文的漏洞自動利用生成模塊進行對比測試,實驗結果如表3所示.
表3第1個樣本為ANGR項目中的測試樣本,兩者均能成功自動生成利用,其余9個樣本為收集的BCTF比賽測試樣本,ANGE的AEG插件只生成了一個exploit樣本,但是測試運行時只是產生了段錯誤,并未能成功利用,分析原因是該漏洞是棧溢出,該AEG功能未考慮棧地址隨機性.同時,實驗中AEG每項失敗的利用生成測試時間均超過12 h,內存消耗達到30 GB以上,單臺設備每次只能運行2個任務.而本文的AOTA系統在這些測試用例中,成功生成并利用成功的樣本數有6個,且能夠在單臺設備并發10個任務,在性能和能力方面具備明顯的優勢.

Table 3 Exploit Ability Comparison Between ANGR AEG and AOTA
本文提出的有限資源條件下基于程序動態運行特征Weak-Tainted模型的漏洞自動挖掘與利用方法具備快速發現漏洞和生成可利用樣本的能力,支持漏洞類型的自定義擴展描述,擴充了適用范圍.而PolyAEG與Maythem等相關工作只能對控制流劫持等特定類型的漏洞自動生成利用,本文提出的漏洞模型支持對多種類型的漏洞自動生成利用,通過漏洞描述的配置實現多類型漏洞支持的擴展.
本文優化了污點傳播算法和輸入求解方法,國內對輸入求解的優化研究[22]更側重在漏洞挖掘方面提高路徑覆蓋率.當前的漏洞自動利用生成方案PolyAEG,Maythem,ANGR等均使用了污點傳播和符號約束求解,PolyAEG在硬件虛擬化平臺QEMU基礎上進行指令插樁,構建指令級的數據傳播流圖iTPG和全局污點狀態記錄GTSR,其更側重于漏洞利用樣本的多樣性生成,本文優化后的污點傳播方法在內存消耗和效率上均優于它,同時PolyAEG未考慮因內核指令未監控導致的污點數據過標記問題,會導致控制流劫持的誤報,消耗更多的資源用于無效的利用生成求解.同樣,Maythem也具有同樣的問題,其更側重于符號執行求解的優化,未能消除內核未監控導致的誤報問題.
另外,測試中我們發現部分的漏洞自動利用生成失敗,其原因主要包括3種情況:1)輸入無法滿足特定字符,如通過scanf獲取的輸入字符串不可能有“ ”,“ ”以及空格等字符,對應的ASCII碼值為0x0d和0x0a,而當求解的條件要求輸入的字符為0x0d或0x0a時就會導致求解失敗;2)修改后的輸入改變了程序的原有邏輯,導致程序無法執行到我們設定的求解點,無法獲得用于分析輸入輸出關系的更多數據,比如程序檢測輸入的數據必須全部為數字否則退出,當改變輸入為非數字時就會導致程序執行不到預設的輸出點;3)輸入輸出關系復雜度太高,如產生雪崩效應的加密算法在搜集了大量的輸入輸出關系之后仍然無法求解.
針對求解失敗產生的原因,本節討論可能的解決思路.針對原因1,本文的思路是修改等效的求解條件,在漏洞觸發前題下,其利用輸入的約束并非唯一的,比如棧溢出中所需要的跳板指令jmpesp可以用等效的callesp代替,并且在程序代碼塊中存在該指令的位置可能不止一處,我們在選取跳板指令時盡量避免導致輸入為空格、回車等字符的約束.另外ShellCode可以進行變形,使用等效指令替換,以此規避一些導致無解的約束條件.針對原因2,我們在監測到輸入導致無法執行到輸出點后,只需舍棄該變異的輸入,導致的結果僅僅是消耗更多的計算資源用于收集輸入輸出關系,但仍能繼續求解.針對原因3,目前的研究尚無可行的解決方案,傳統的符號執行方案也可能因為復雜度太高求解失敗,畢竟對于高強度的RSA等公鑰加密、SHA256等HASH算法也并非符號執行能夠解決的問題,但畢竟這類問題很少出現在漏洞利用需要突破的范疇,需要將輸入數據經過復雜加解密運算后作為ShellCode或者跳板指令地址的場景并不常見,但經過編碼轉換的情況則較為常見.
本文提出的Weak-Tainted程序動態運行時漏洞描述模型適用于控制流劫持類漏洞、格式化字符串漏洞、脆弱函數造成的緩沖區溢出漏洞、命令行執行API調用(疑似后門)漏洞等多種漏洞類型的漏洞識別和利用自動生成,但目前對UAF和Double Free僅能給出漏洞判定模型,尚未能給出適當的描述用于此類型漏洞的利用生成方案,這將作為后續的研究點.
本文介紹了一種有限資源條件下基于動態模型的漏洞自動利用方法,基于污點分析和輸入求解優化模糊測試的數據生成,并建立了可貫穿應用于漏洞挖掘、分析、利用全流程的Weak-Tainted程序動態運行時漏洞描述模型,然后結合基于標簽的污點傳播方法用于漏洞判定和利用約束條件提取,最后基于輸出特征反饋的輸入求解方法進行利用樣本自動生成.本文實現了原型系統,通過對2018年BCTF比賽樣本進行了實驗測試,在與AFL具備同等漏洞挖掘能力的前提下,平均效率提高45.7%,樣本分析內存消耗維持在100 MB以內,為并行化提供了有利條件,在測試的50個樣本中有24個能夠自動生成利用代碼,驗證了有限資源條件下Weak-Tainted程序動態運行時漏洞描述模型用于漏洞自動挖掘與利用生成的有效性.