◆林 敏 張 超
(清華大學 北京 100084)
隨著業界對軟件安全問題的關注度不斷提升,漏洞挖掘逐漸成為重點研究的內容。模糊測試[1]作為一種通過隨機樣本挖掘軟件漏洞的技術,相比于其他漏洞挖掘技術更加簡單高效。它是一種向應用程序提供非預期的輸入,通過監控執行中的異常,來發現軟件漏洞的方法。目前應用得較為廣泛的灰盒測試工具是AFL[2]及其擴展[3-6]。
然而傳統的模糊測試存在一個公認的缺點:對于具有復雜文件格式輸入的應用程序,隨機的比特變異難以生成合法有效的文件,往往在應用程序初始執行階段就無法通過檢查,難以對應用程序的深層邏輯進行有效的檢測,導致了模糊測試的低效。
本文對EOS執行智能合約所使用的WebAssembly二進制虛擬機進行研究,該目標對象的輸入不僅具有高結構化的特征,同時因其代碼段由各種指令組成,還包含豐富的語義信息。想對虛擬機進行完整的測試,不僅要保留 WebAssembly二進制文件的整體結構信息,同時還要在符合 WebAssembly語法的情況下盡可能地變異出包含各種語義的文件。
但是基于覆蓋率的模糊測試最大的問題是缺乏對輸入文件的結構理解。測試中的變異操作通常在比特的層面進行,例如進行位翻轉、刪除、添加等,但是對于具有復雜文件格式規范的應用程序來說,不合法的文件可能導致程序在初始的合法性檢查階段就退出,導致生成了大量無效的測試用例,難以發現應用程序的深層次邏輯漏洞。針對傳統方案的局限性,研究人員也提出了新的方案,比較主流的有基于字典和污點跟蹤的方法。
AFL的作者采用了自定義字典[15]的方式,讓用戶提供關于輸入文件結構的信息。當輸入文件中存在魔數和塊標識符的情況下,這種方式能發揮比較好的作用。基于AFL的拓展工具VUzzer[7]通過靜態分析和動態污點跟蹤和的方法來定位變異點。但是這兩種方式都不能解決上述的問題,即無法從更高的層面,而非比特層面來改變文件的結構信息。例如,通過字典和污點跟蹤的方案都不能添加或完刪除文件塊。
Driller[8]在AFL的基礎上加入了動態符號執行引擎,交替探索程序執行路徑,引導模糊測試探索到程序更深層次的節點。但是,基于符號執行增強的模糊測試技術仍然會受限于符號執行中的約束求解問題,符號執行的引入可能會弱化模糊測試本身的可擴展性。
針對上述問題,本文提出了一種針對EOS的WebAssembly二進制虛擬機進行模糊測試的方案,與傳統的模糊測試方案不同,本方案通過研究 WebAssembly各模塊對執行的影響,利用WebAssembly的文件格式構造有效文件,對WebAssembly文件進行分解,再將各個模塊之間進行組合和修改,增加了在高級結構而非比特層面的變異。同時,本方案還對WebAssembly的代碼段進行了深入的變異,通過改變初始化數據、打亂的合法指令序列來增加變異的多樣性,讓輸入能探索更多的路徑。同時,本方案還引入了一個基于有效性的能量調度方案,讓模糊測試在有效的種子上花費更多的時間,增加發現程序處理邏輯漏洞的概率。
基于本文的方案,實現了WASMAFL,是一個基于AFL的模糊測試工具,在其基礎上增加了 WebAssembly變異的策略和能量調度策略。在我們的評估中,將兩者進行了比較,評估結果表明,在給定的24小時內,WASMAFL可以比AFL發現更多的崩潰,還顯著提高了路徑覆蓋率。
總之,本文的主要貢獻是對 WebAssembly虛擬機提出了新的模糊測試方案,能夠高效地對其進行灰盒模糊測試,同時該方案也適用于其他腳本語言的虛擬機。
隨著業務邏輯越來越復雜,前端開發的代碼量變得越來越大,然而由于JavaScript動態類型語言的限制,造成了嚴重的性能瓶頸,因此WebAssembly應運而生。WebAssembly[9]是一種體積小、加載快、可移植并且兼容Web的全新二進制格式,可以很方便地將C、C++和Rust等低級語言的代碼以接近原生性能的速度運行在瀏覽器等本地客戶端中。同時,WebAssembly也被新興的區塊鏈技術所看好,被稱為“區塊鏈 2.0”的以太坊[10]計劃使用WebAssembly虛擬機取代目前的 EVM,被稱為“區塊鏈 3.0”的EOS[11]選用了WebAssembly虛擬機作為合約的執行引擎。
EOS作為最具代表性的委托股權證明DPoS平臺和第一個去中心化的操作系統,近年來發展迅速,是全球最活躍的社區之一。EOS采用WebAssembly作為智能合約的執行語言。智能合約[12]是一種以信息化方式傳播、驗證或執行合同的計算機協議,允許在沒有第三方的情況下進行交易,這些交易可追蹤且不可逆轉。由于智能合約可以由任何人發布,因此攻擊者可以通過構造惡意合約來對區塊鏈平臺進行攻擊。同時,由于區塊鏈平臺去中心化的特點,單個節點的漏洞會導致成千上萬的節點遭到攻擊,甚至在傳統軟件漏洞領域被認為相對危害較小的拒絕服務漏洞,在區塊鏈網絡中則可能引發整個網絡癱瘓。2018年國內安全公司360發現了 EOS節點的遠程執行任意代碼,即可以通過遠程攻擊,直接控制和接管 EOS上運行的所有節點。因此針對區塊鏈合約執行的安全研究尤為重要。
針對傳統灰盒測試對 WebAssembly二進制虛擬機進行漏洞挖掘中存在的問題,本文提出了新的針對 WebAssembly二進制虛擬機的模糊測試方案,同時基于該方案我們實現了漏洞挖掘系統WASMAFL。本章將先對該系統的總體設計進行介紹,再闡述每個組件的設計方案和實現細節,以及各模塊之間如何在系統中協調工作,最終實現針對WebAssembly虛擬機的高效模糊測試。
本文針對傳統灰盒模糊測試中隨機變異帶來的低效問題,提出了分層變異算法,通過對段表的組合和指令的比特變異,來實現變異的隨機性。接著方案通過指令修正來保證文二進制文件的合法性,生成更有可能觸發程序中難以到達路徑的測試用例。最后方案在模糊測試的過程中引入了基于合法性的能量調度策略,根據種子的合法性來分配種子的執行時間和變異次數,保證優秀的種子能得到更充分地測試。
WSAMAFL基于AFL進行拓展和修改,其中WASMAFL增加了三個組件,分別是WebAssembly段表組合變異模塊、指令變異模塊和指令合法性修正模塊,同時在對種子進行執行時間分配時,我們引入了基于合法性的能量調度模塊。總體架構如下圖1所示。用于文件合法性檢查的指令修正模塊是獨立的,和模糊測試器解耦,這樣方便更靈活地增加其他規則,以及將算法應用到其他虛擬機對象中。
標準的 WebAssembly模塊[13]是由一系列具有特定功能的段結構組成的,段結構中存放著用于描述模塊功能和屬性的信息,如圖2所示。同時模塊還定義了一系列用于存儲靜態資源的索引空間,索引空間內的資源可以由模塊中定義的各類指令及相關段結構來進行引用。

圖1 WASMAFL架構
WebAssembly二進制遵守嚴格的格式,使用隨機變異方法的盲目模糊測試策略會導致大量無效的測試用例并且降低模糊測試效率。因此,為了使得生產的測試用例更有針對性,更有可能滿足程序對數據結構和邏輯判斷的要求,我們提出了分層變異的策略,通過段表組合和指令變異相結合的方式,來改變虛擬機內存中控制流和數據流的信息,從而對虛擬機進行更充分的測試。分層變異后生成的測試樣例具有高度結構化的特征,不會使得程序在合法性檢查階段就退出測試。同時變異后的種子文件具有更加豐富的語義和隨機性,能夠測試到不符合邏輯的指令流程和數據處理,使得對虛擬機的測試更為充分和全面。
變異過程分為兩個部分,分別是對模塊的各個段表之間進行組合變異,以及對模塊代碼段的函數指令進行變異。模糊測試先進行模塊段表層的組合變異,再進行段表組合變異和代碼段指令變異相疊加的混合變異。
3.1.1 對模塊的各個段表之間進行組合變異
段表間的組合變異本質上是通過WebAssembly的模塊結構、數據格式等先驗知識,來指導并改善測試用例的生成。根據上一章對WebAssembly格式的介紹,我們知道WebAssembly模塊由多個段表組成,其中某些段表為模塊必須的段表,有且僅有一個,另一些段表則為非必須的段表,通常這些段表通常是虛擬機構造內存模型和上下文執行環境的數據來源,通過對段表的組合變異,可以測試虛擬機是否按照標準進行實現,同時可以檢測出段表在不同的上下文環境中,是否存在安全漏洞。這個階段的變異通過對段表進行合理組合,分別為刪除、添加、替換和篡改四類操作。

圖2 WebAssembly二進制段表組成示例
(1)段表刪除
刪除操作以段表為單位,從給定的種子文件中隨機選取一個段,只要該段不是僅有的代碼段則對其進行刪除。例如,隨機選擇模塊的Start段,標準規定在一個WebAssembly模塊中只能設置一個Start段,但是因為其不是僅有的代碼段,則對其刪除,即將 Start段對應的字節部分進行移除,生成新的測試樣例。在段表的刪除操作中需要注意的是,由于要對虛擬機的指令處理邏輯進行深入的測試,因此需要保證 WebAssembly模塊具有實際的語義,所以我們需要保留至少一個代碼段,如果模塊中存在多個代碼段則可以對代碼段進行刪除。

圖3 段表刪除操作
(2)段表插入
同理,段表的插入也是以段為單位。對給定的種子文件s1,從其他種子文件中隨機選擇另一個種子文件s2,選取s2中的隨機段,將其插入到s1的模塊中,插入的位置不能破壞s1原有的段表結構,即只能插入在原有的任意段表之前或者之后。和段表刪除操作不同的是,插入的段表可以是代碼段。

圖4 段表插入操作
(3)段表替換
段表替換是段表層變異中相對復雜的操作。和段表插入的操作類似的,先選擇兩個種子文件s1和s2,接著從種子文件s2中選取任意的段表,替換種子文件 s1中對應的段表,生成新的種子文件。需要注意的是,進行替換的段表必須是相同的段表,例如我們隨機選取了s2中的start段,想對s1中的start段進行替換,但是s1中不存在start段,那么我們則需要重新選擇s2中的其他段表,如果選擇的次數超過三次,則放棄此次的替換操作。和插入操作一樣,替換操作也不能破壞原有種子文件 s1的模塊段表結構。

圖5 段表替換操作
(4)段表篡改
段表篡改是針對模塊的自定義代碼段,根據我們的觀察,虛擬機在對自定義段表的處理容易出現崩潰,因此在段表層的變異操作中,我們特地引入了段表的篡改。對于給定的種子文件s,從中任意選擇一個段表,將其段表的標識變為自定義段表的標識。例如,對于種子文件s,我們將其type段的標識1變為0,從而該段就變成了自定義段。

圖6 段表篡改操作
段表層的變異是在保證模塊基本合法性的前提下,盡可能地豐富虛擬機在執行模塊時的上下文環境。同時,變異過程也不完全遵守 WebAssembly的標準規定,因此能在模糊測試的過程中驗證虛擬機的實現規范。
3.1.2 對模塊代碼段的函數指令進行變異
段表層的重組變異可以改變虛擬機的內存初始化狀態和程序上下文數據,但是虛擬機主要的功能在于對代碼段的指令邏輯進行執行。所以在完成段表層的重組后,我們單獨對模塊的代碼段進行變異。
WebAssembly模塊的代碼段由函數及其指令組成,指令類似于匯編指令,由操作碼和操作數組成。我們觀察到,在傳統模糊測試變異中,如果對指令的操作碼進行比特變異,會產生兩種結果:變異破壞了指令的合法性,即將指令變成了非法指令,當虛擬機執行到該非法指令會直接報錯退出。而如果將指令變異成了其他合法指令,虛擬機也會因為操作數中可能存在的零字節以及編碼解析等問題退出執行,導致能夠連續正常解析的指令數較少。另一方面,從指令操作數變異的層面上來看,只有少數的變異是有意義的。例如,對于常量指令i32而言,后面跟著的常量的變化大部分可能無法改變程序執行的分支,但是如果是常量的臨界值,則可能會導致虛擬機在處理數值時發生溢出。
綜上,為了保證代碼段變異的充分性,我們在代碼段使用比特的粒度進行變異,變異操作包括翻轉、替換、算術加減、隨機比特變換刪除、復制增加等AFL的變異操作。同時,為了減少非法指令造成的退出,我們通過指令修正的方式,保證虛擬機對指令進行完整的解析。

圖7 代碼段比特變異
上文提到對代碼段的比特層面的變異會導致虛擬機在遇到非法指令時退出,因此我們設計了一個優化方案,再使用傳統的比特變異方法對代碼段數據進行變異后,再對指令進行修正。
指令修正的核心思想是將指令的非法操作碼進行NOP指令替換,將指令的非法操作數修改成最大臨界值。指令修正模塊對變異后的文件進行掃描,識別出變異導致的非法指令位置,根據該位置是指令操作碼還是操作數來進行相應的操作。如果識別出是非法操作數,則通過NOP指令進行替換,使得虛擬機能執行后面的邏輯。WebAssembly的操作數通過有符號LEB128、無符號LEB128等進行編碼,如果識別出是操作數,則根據操作碼對應的操作數編碼類型,將操作數改為最大值。

圖8 指令合法性修正
指令修正能讓虛擬機正常解析的指令數變多,執行的邏輯更多樣、更深入,因此,能夠提高模糊測試過程中的代碼覆蓋率。
模糊測試的過程中需要反復選擇種子進行變異,如何從語料庫中選擇有效的種子是模糊測試中的重要課題。通過良好的種子選擇策略,模糊器可以優先考慮更有用的種子,包括覆蓋更多代碼并更有可能觸發漏洞,同時減少重復執行同一路徑的浪費并節省計算資源。我們將為種子文件分配的執行時間稱為種子的能量,能量調度策略決定了在模糊測試過程中為哪些種子分配更多的執行時間和機會。
在 AFL中將能量分配給較小的種子,這樣可以提高執行的速度。AFLfast[11]則將更多的能量分配給執行低頻路徑的種子。本文提出了一種新的能量調度策略,基于文件變異的有效性來分配能量。如果對文件變異后的結果修正越少,那么說明文件變異的效果越好,并且種子修正所需要花費的時間也越低。因此,應該分配更多的能量給這些種子。通常來說,我們會認為文件變異的有效性是一個布爾值,即變異是有有效的或者無效的。但是這里我們使用一個比例來衡量文件變異的有效性。我們將文件變異的有效性定義成修正操作的比例。
對于給定的種子s,基于變異有效性的能量調度prob(s)的分配如下:

其中,t是指令修正的次數,當指令的修正次數大于最大值MAX時,我們認為初始的變異造成的無效文件較多,將對其分配較低的能量,當指令修正的次數小于最大值,則根據修正次數與最大值的差值和比例進行分配。
指令修正次數的最大值是通過實驗進行設置,是一個經驗數值。同時,在模糊測試的初始階段,可以通過選項對其進行設置。我們收集了20個較為流行的合約文件進行測試,發現一次變異需要修正的次數超過50,則會顯著降低測試的速度。因此,本文選擇的最大修正次數為50。
根據本文的方案,我們在AFL的基礎上實現了WASMAFL,將工具與原版AFL_2.52進行對比,分別對EOS的Webassembly虛擬機進行了24個小時的模糊測試。我們對比了新方案和原有方案下發現路徑情況和產生的崩潰數量,從表2中我們的可以發現,在路徑覆蓋率上,有了 35%的提升。在崩潰的產生數量上,WASMAFL比AFL多了6.17%。同時,我們對導致程序崩潰的樣本進行分析,根據樣本產生崩潰的位置和原理進行了分類,發現AFL只找到了1種段錯誤類型的崩潰,而WASMAFL工具則找到了2種段錯誤類型的崩潰。

表1 模糊測試執行結果對比
本文的方案旨在引導程序向更深的路徑進行探索,因此表2對兩個工具的路徑深度和分支覆蓋率進行比較,發現路徑深度提高了兩層,同時分支覆蓋率也相應地提高了34%。由于新的方案在執行過程中引入了文件的結構識別和合法性檢查,因此實驗對執行速度進行了比較,發現執行速度的降低在可接受范圍內。

表2 模糊測試執行效果對比
同時我們還對24小時內總路徑數和路徑深度隨時間變化的曲線進行了繪制,從圖9可以看出WASMAFL能比AFL更快地到達更深的路徑,到達更深路徑后總路徑數的增長WASMAFL快于AFL。

圖9 執行24小時總路徑變化曲線
本文通過對 WebAssembly虛擬機的模糊測試方案進行了設計,提出了分層變異算法,最后和通過工具AFL進行測試,通過24小時的對比驗證,與原AFL工具效果進行對比評估分析, 表明該方案能夠提升模糊測試的覆蓋率和崩潰發現數量。
本文提出了一種針 WebAssembly虛擬機的模糊測試方案。通過研究WebAssembly各模塊對執行的影響,利用WebAssembly的文件格式構造有效文件,對WebAssembly文件進行解析,再將各個模塊之間進行組合和修改,增加了在高級結構而非比特層面的變異。同時,本方案還對WebAssembly的代碼段進行了深入的變異,通過改變初始化數據、打亂的合法指令序列來增加變異的多樣性,讓輸入能探索更多的路徑。同時,本方案還引入了一個基于有效性的能量調度方案,讓模糊測試在有效的種子上花費更多的時間,增加發現程序處理邏輯漏洞的概率。
基于本文的方案,實現了WASMAFL,是一個基于AFL的模糊測試工具,在其基礎上增加了 WebAssembly變異的策略和能量調度策略。在我們的評估中,將兩者進行了比較,評估結果表明,在給定的24小時內,WASMAFL可以比AFL發現更多的崩潰,還顯著地提高了路徑覆蓋率。