王嘉誠,蔣佳佳,趙佳豪,張玉書,王良民
1.南京航空航天大學計算機科學與技術學院/人工智能學院/軟件學院,南京 211106
2.東南大學網(wǎng)絡空間安全學院,南京 211106
伴隨著區(qū)塊鏈技術[1]的飛速發(fā)展,其應用場景越來越廣泛,在生活中的普及程度也越來越高。目前,區(qū)塊鏈技術已經(jīng)進入了一個新紀元。作為第二代區(qū)塊鏈技術的核心,智能合約[2]在區(qū)塊鏈平臺上發(fā)揮著日益重要的作用,區(qū)塊鏈平臺上的賬戶在智能合約的基礎上管理著價值越來越龐大的數(shù)字資產(chǎn),也正因如此,黑客也開始將目光瞄準這一新興的技術領域。由于智能合約和區(qū)塊鏈技術的發(fā)展時間還較為有限,已經(jīng)暴露的安全問題和潛在的安全隱患都對以太坊區(qū)塊鏈平臺上的資產(chǎn)管理造成了嚴重威脅。
智能合約本質(zhì)上是一種通過區(qū)塊鏈部署并存儲在以太坊平臺上的具有特定功能的程序,因此上鏈數(shù)據(jù)不可篡改也成為智能合約所具備的特點之一,這就導致即使發(fā)現(xiàn)已部署的智能合約中存在影響其正確運行的漏洞,也很難對其進行修改。所以,在智能合約被部署之前就對其進行正確性檢測成為了亟待解決的問題。
近年來,隨著智能合約技術的日漸成熟以及愈加廣泛地應用區(qū)塊鏈平臺之上,越來越多的學者注意到隱藏在智能合約中的漏洞對于用戶以及區(qū)塊鏈平臺安全造成的威脅[3],他們開始探索各種類型的方法對智能合約中存在的安全漏洞進行分析、檢測?,F(xiàn)有的智能合約安全漏洞檢測技術[4]主要有五個類別:基于靜態(tài)分析的漏洞檢測技術、基于測試的漏洞檢測技術、基于機器學習的漏洞檢測技術、組合技術以及其他。
靜態(tài)分析[5-10]屬于較為傳統(tǒng)的漏洞檢測方式,其檢測合約中隱藏的安全漏洞的方法是在不執(zhí)行程序的情況下對程序的靜態(tài)特征進行分析。符號執(zhí)行技術通過抽象方式模擬執(zhí)行程序并涵蓋程序的多個可能路徑。Luu 等人[11]在2016 年提出并構建了一種名為Oyente 的基于符號化執(zhí)行的漏洞檢測工具,用以對智能合約中潛在的安全漏洞進行檢測分析,Oyente工具屬于目前符號分析領域較為領先的漏洞檢測工具,其可以檢測堆棧大小限制漏洞、事務順序依賴漏洞等4種類型的漏洞。形式化方法[12]使用數(shù)學和邏輯方法來描述和驗證智能合約的設計是否滿足其要求。Bartoletti 和Zunino[13]兩人在2018年提出了一種在不依賴可信任的中心的去中心化體系內(nèi),對涉及比特幣交易的合約進行監(jiān)管的形式化驗證語言BitML,交易的參與者可以根據(jù)這種語言構建自己的交易并上鏈,從而執(zhí)行智能合約。Tsankov等人[14]在2018 年提出了一種名為Securify 的以太坊智能合約自動化安全分析工具,其可以根據(jù)漏洞特征分析智能合約是否存在漏洞。Securify分析智能合約源代碼編譯后生成的字節(jié)碼。Securify首先根據(jù)智能合約的字節(jié)碼重構依賴圖,通過分析依賴圖得到智能合約的語義信息。然后Securify根據(jù)給定的特征分析智能合約是否符合或違反這些特征,結合從智能合約中獲得的語義信息,判斷是否存在漏洞。污點分析技術是一種用于跟蹤程序中不可信數(shù)據(jù)或私有數(shù)據(jù)在一定規(guī)則下產(chǎn)生的數(shù)據(jù)流的程序分析技術。Rodler 等人[15]在2019 年提出了一種使用動態(tài)污染跟蹤技術保護智能合約免受重入攻擊的工具,名為Sereum。當Sereum工作時,它的污染引擎通過動態(tài)污染跟蹤為預定義源生成的數(shù)據(jù)分配標簽。之后,攻擊檢測器可以通過觀察標記的數(shù)據(jù)如何影響程序的正常執(zhí)行來識別程序執(zhí)行的異常狀態(tài)。當Sereum確定當前程序中正在發(fā)生重入攻擊時,Sereum 將與EVM事務管理器連接,并終止正在進行的程序。
在基于測試的漏洞檢測技術[16-18]中,模糊測試技術[19-21]通過向目標程序提供大量意外輸入來暴露異常結果,從而發(fā)現(xiàn)程序漏洞。Liu 等人[22]在2018 年提出了Reguard,這是一種基于模糊測試的智能合約漏洞分析工具,其專門針對智能合約中包含的可重入性漏洞進行自動監(jiān)測,這種工具是通過迭代的方式生成隨機的交易,通過執(zhí)行這些交易來對智能合約進行模糊測試,并根據(jù)對交易執(zhí)行過程的監(jiān)控,動態(tài)地檢測合約中的可重入性漏洞。變異測試會對源程序中的某些語句進行修改,并結合測試用例觀察修改后程序的執(zhí)行結果,從而發(fā)現(xiàn)錯誤。Li等人[23]在2019年提出了MuSC。MuSC首先將每個待測合約的源代碼轉(zhuǎn)換為抽象語法樹并在抽象語法樹中生成變異數(shù)據(jù),然后變異數(shù)據(jù)轉(zhuǎn)換回源代碼文件進行編譯和執(zhí)行,從而暴露智能合約的缺陷。MuSC作為首個應用于以太坊智能合約漏洞檢測的突變測試工具,實現(xiàn)了突變測試的基本功能,并能在一定程度上暴露合約中的漏洞。
機器學習[24-26]利用過去的數(shù)據(jù)或經(jīng)驗,以計算機為工具,致力于實時模擬人類的學習。機器學習作為當前的熱點技術,也被越來越頻繁地應用于智能合約漏洞檢測領域。2018年,Tann等人[27]提出了首個應用于智能合約漏洞檢測的機器學習方法。該方法利用長短期記憶連續(xù)學習智能合約安全漏洞,將智能合約字節(jié)碼序列映射為矢量序列,作為長短期記憶模型的輸入,預測智能合約漏洞。
為了追求更好的漏洞檢測效果,彌補單一程序分析技術的局限性,組合技術(將多種類型的程序分析技術結合用于智能合約漏洞檢測)從2019 年開始受到業(yè)界研究人員的關注。2020 年,Ashouri[28]提出了一種將動態(tài)污染跟蹤技術與動態(tài)符號執(zhí)行技術成功結合的智能合約漏洞檢測工具,名為Etherolic。該工具首先對由輸入數(shù)據(jù)組成的事務進行標記,并在執(zhí)行期間使用動態(tài)污點跟蹤技術跟蹤事務的傳播。當其追蹤到受污染的交易后,會檢查其相關的敏感數(shù)據(jù)流,從而識別智能合約中的潛在漏洞。定位漏洞后,Etherolic會對智能合約進行動態(tài)符號執(zhí)行,生成一系列試圖觸發(fā)智能合約漏洞的交易。最后,Etherolic根據(jù)受污染數(shù)據(jù)的跟蹤結果和易受攻擊代碼的觸發(fā)結果生成漏洞檢測報告。
除了上面提到的四類典型的智能合約漏洞檢測技術外,還有一些不太常見的漏洞檢測技術。例如代碼克隆檢測、攻擊向量檢測、在線檢測、入侵檢測、語義感知和氣體感知等。2018 年,Liu 等人[29]提出了一種新的語義感知安全審計技術,稱為S-gram。該審計技術將NGram 語言建模與輕量級靜態(tài)語義標記相結合,通過識別不規(guī)則的語義標記序列和優(yōu)化現(xiàn)有的漏洞分析器來捕獲智能合約的高級語義,并預測潛在的漏洞。
本文以基于測試的智能合約漏洞檢測技術中的模糊測試技術為主要思路,在智能合約被正式部署之前對其進行動態(tài)漏洞檢測,判定其正確性。智能合約的正確性一般包括代碼正確性和運行正確性。作為一段計算機程序,智能合約的運行正確性在其編譯以及部署在區(qū)塊鏈平臺投入應用時會得到驗證,編譯不通過以及無法正常運行的智能合約皆不滿足其運行正確性。本文重點關注的是智能合約的代碼正確性,在某種程度上,代碼正確性會對運行正確性造成不可忽視的影響。與一般的計算機程序相同,影響智能合約代碼正確性的因素通常包括編程語法錯誤、邏輯錯誤等,這一系列問題導致了軟件漏洞的引入,使得智能合約無法實現(xiàn)預期的功能或被惡意用戶所利用,這都將造成實際的損失。本文著重關注針對七種具有區(qū)塊鏈特點的軟件漏洞時智能合約的正確性問題。
為實現(xiàn)模糊測試,本文構建了一個針對智能合約的內(nèi)容以及規(guī)范生成模糊輸入的框架。該框架通過對智能合約的ABⅠ接口以及字節(jié)碼進行分析,根據(jù)智能合約所調(diào)用的函數(shù)的參數(shù)的不同類型生成不同的模糊輸入,并針對不同種類的漏洞構建測試樣例。向待測智能合約導入模糊輸入后,在執(zhí)行測試樣例時通過以太坊虛擬機來監(jiān)控智能合約的執(zhí)行情況,通過對監(jiān)控獲得的若干日志文件進行分析,實現(xiàn)對于智能合約漏洞的檢測。經(jīng)測試,在416個部署在測試網(wǎng)上的智能合約中檢測出19個包含漏洞的不正確智能合約,對檢測結果進行人工分析后發(fā)現(xiàn),檢測的準確率達到了94.7%。
區(qū)塊鏈是一個分布式的共享賬本,也是一個去中心化的數(shù)據(jù)庫,區(qū)塊鏈以分布式存儲的方式將比特幣交易的信息保存在各個區(qū)塊中,而每一個區(qū)塊所包含的數(shù)據(jù)又直接決定了其子區(qū)塊的生成,在生成新區(qū)塊時,需要共識機制來化解分叉問題,同時需要對信息的有效性進行驗證。作為支撐比特幣的底層技術之一,區(qū)塊鏈技術為價值轉(zhuǎn)移提供了新的思路和方法。然而,比特幣的功能較為單一,無法在區(qū)塊鏈網(wǎng)絡上實現(xiàn)較為復雜的應用,其實際應用價值受限。在區(qū)塊鏈技術的發(fā)展過程中,以太坊的出現(xiàn)很好地解決了這個問題,讓區(qū)塊鏈的商業(yè)應用成為可能。
以太坊(Ethereum)是一個去中心化的應用平臺,與比特幣等加密貨幣相同,以太坊同樣具備電子支付、數(shù)字資產(chǎn)轉(zhuǎn)移等功能。除此之外,任何用戶都可以在其上構建去中心化應用,并與其他應用程序進行交互。以太坊上構建的去中心化應用程序便是由智能合約構成的,智能合約管理著以太坊的許多關鍵功能、操作、用戶行為。以太坊支持兩類賬戶:實體用戶所有的賬戶以及智能合約賬戶。以太坊會在每一筆交易產(chǎn)生之后更新自己的狀態(tài),其交易本質(zhì)是在不同賬戶(智能合約賬戶和實體賬戶)之間發(fā)送虛擬貨幣或二進制數(shù)據(jù),當接收方是智能合約賬戶時,將會把二進制數(shù)據(jù)作為智能合約的輸入數(shù)據(jù),執(zhí)行其代碼。
智能合約[30]是第二代區(qū)塊鏈核心技術,Nick Szabo在1995 首次提出了“智能合約(smart contract)”的概念,直到2013年以太坊區(qū)塊鏈平臺出現(xiàn)之后,智能合約的實體才真正誕生。智能合約其本質(zhì)是由代碼組成的程序,也可以理解為是由代碼定義的交易規(guī)范、準則。智能合約的執(zhí)行需要達成某些特定的條件或是發(fā)生特定的行為事件,其本身具有自己的狀態(tài),并且是以區(qū)塊鏈平臺(如以太坊)為載體存在的。結合區(qū)塊鏈不可篡改的特點,智能合約一經(jīng)部署便不容易被惡意攻擊者篡改。當智能合約被部署在區(qū)塊鏈平臺之后,智能合約自身的狀態(tài)會和區(qū)塊鏈的狀態(tài)建立起非常緊密的聯(lián)系,這樣一來,智能合約的執(zhí)行將變得非常高效。
然而,由于智能合約在執(zhí)行時有可能會涉及到多個不同智能合約之間的協(xié)作交互,如果編寫智能合約的工作人員未能清晰地掌握智能合約與區(qū)塊鏈平臺的關系以及智能合約之間的潛在關系,那么開發(fā)出的合約就有可能存在安全漏洞。除此之外,因為智能合約和區(qū)塊鏈均是近幾年新興的技術,編寫智能合約的編程語言以及運行智能合約的區(qū)塊鏈平臺對于大部分開發(fā)者來說接觸時間并不長,加之開發(fā)工具本身也并不夠完善,這就導致開發(fā)人員在開發(fā)的過程中容易遇到困難和問題,編寫出的智能合約極易存在漏洞。這些漏洞都將影響智能合約的正確執(zhí)行,從而對區(qū)塊鏈平臺的安全穩(wěn)定造成威脅。
總的來說,區(qū)塊鏈技術作為一項具有重要意義的新興技術,其快速發(fā)展的過程同樣也是不斷拓展、打破固有思維、消除技術局限性的過程。從比特幣、區(qū)塊鏈到以太坊、智能合約,從區(qū)塊鏈1.0時代到2.0時代,區(qū)塊鏈技術得到了越來越廣泛的應用,實際應用的推廣又進一步推動了技術的發(fā)展。發(fā)展越是迅猛,安全則越是關鍵,作為當前技術核心部分的智能合約安全問題則越應受到重視。本文工作即是在以智能合約為核心的二代區(qū)塊鏈技術飛速發(fā)展的背景下,以模糊測試為主要技術手段,對以太坊智能合約進行漏洞檢測,從而對其正確性做出判斷。
本章將對本文中涉及的7 種類型的智能合約漏洞進行介紹。
不消耗gas 的send 漏洞的產(chǎn)生是基于一種特殊情況:當使用send()函數(shù)(相當于一個特殊的call())發(fā)送以太幣到一個合約時,簽名如果匹配不到任何的函數(shù)時,將會觸發(fā)回退函數(shù)(fallback())。由于send()函數(shù)指定了一個空函數(shù)簽名,所以當fallback()函數(shù)存在時,send()函數(shù)將總是會調(diào)用回退函數(shù)。但和一般的函數(shù)不同的是,執(zhí)行send()所消耗的gas 默認上限是2 300(如果特別指定上限的話,可以大于2 300),因此,如果接收方合約在交易時觸發(fā)的fallback()函數(shù)需要的gas超過其默認上限,以太幣的發(fā)送方將會遇到一個名為“out-of-gas”的異常。如果這種異常沒有被適當?shù)貦z查并做出提醒,那么惡意的發(fā)送者可能會使以太幣的發(fā)送出現(xiàn)錯誤。
異常無序漏洞是一種由于不同合約對于異常處理的穩(wěn)定性不一致所導致的漏洞。異常無序漏洞通常在不同合約彼此調(diào)用時發(fā)生。當一個合約調(diào)用另一個合約的函數(shù)時,調(diào)用行為可能會因為不同類型的異常而失敗,當這種異常發(fā)生時,處理異常的機制是由合約之間彼此調(diào)用的方式確定的。在一個嵌套調(diào)用鏈中,每個調(diào)用都是對智能合約中的函數(shù)的直接調(diào)用,當異常發(fā)生時,所有的交易(包括以太幣的發(fā)送)都會被還原。然而,如果這種異常發(fā)生在嵌套調(diào)用鏈中就會產(chǎn)生問題,因為在嵌套調(diào)用鏈中,總是存在一個調(diào)用address.call()、address.DelegateCall()、address.send()等函數(shù)的調(diào)用行為,交易行為的恢復在調(diào)用函數(shù)處便會停止并返回false,而交易行為對更深層次的調(diào)用產(chǎn)生的影響將不會被恢復,拋出的異常也不會傳播。異常處理方面的這種不一致性將使調(diào)用合約無法獲知執(zhí)行過程中出現(xiàn)的錯誤。
導致可重入性漏洞的原因:有些函數(shù)在設計之初并不允許對其進行重入操作,然而,一些惡意代碼故意用可重入的方式調(diào)用這些函數(shù),例如通過fallback()函數(shù)進行調(diào)用,這與fallback()函數(shù)本身的性質(zhì)有關。一個智能合約如果沒有定義fallback()函數(shù),它在進行以太幣交易的時候會拋出異常,會導致以太幣無法發(fā)送或者退還。于是便給一些不懷好意的攻擊者提供了可乘之機。通過fallback()函數(shù)調(diào)用一些不允許重入的函數(shù)可能會導致以太幣重復支付的問題出現(xiàn),從而丟失以太幣。著名的Dao[31]攻擊便是通過這個方式造成了當時價值六千萬美元的以太幣丟失。
很多智能合約的執(zhí)行邏輯是和當前區(qū)塊的時間戳有關的,在執(zhí)行一些類似于發(fā)送以太幣的關鍵操作時,如果區(qū)塊的時間戳是觸發(fā)該關鍵操作的條件的一部分,又或是智能合約的代碼獲取時間戳作為產(chǎn)生隨機數(shù)的依據(jù)變量,就可能會導致合約中存在時間戳依賴漏洞。在區(qū)塊鏈這樣的分布式系統(tǒng)中,礦工不僅可以決定一個新開采出的區(qū)塊的時間戳,還可以在900 s 的內(nèi)自行設置區(qū)塊的時間戳。如果智能合約在傳輸以太幣時用到了區(qū)塊的時間戳或時間戳相關的數(shù)據(jù),那么可能會存在惡意礦工通過操縱區(qū)塊的時間戳獲取不正當收益。
區(qū)塊號依賴漏洞的作用原理與時間戳依賴漏洞類似,當智能合約使用區(qū)塊號作為觸發(fā)某些重要步驟的條件的組成部分時或作為生成隨機數(shù)的依據(jù)時,就會造成區(qū)塊號依賴漏洞。其本質(zhì)還是因為區(qū)塊的區(qū)塊號可以被礦工所操作,如果智能合約基于區(qū)塊的區(qū)塊號進行交易,攻擊者就可以通過操縱區(qū)塊的區(qū)塊號來利用該漏洞。即使不直接使用區(qū)塊號,將區(qū)塊號作為某些隨機數(shù)生成器的參數(shù)用以生成隨機數(shù),其結果仍然是脆弱的。
DelegateCall()是以太坊智能合約中較為常見的一種用來實現(xiàn)函數(shù)調(diào)用的函數(shù),其功能與call()函數(shù)較為類似,這二者之間的區(qū)別也正是該漏洞產(chǎn)生的原因,call()函數(shù)在進行函數(shù)調(diào)用時,被調(diào)用函數(shù)的代碼是在被調(diào)用方本身合約內(nèi)運行的,但是DelegateCall()調(diào)用的目標函數(shù)的代碼是在DelegateCall()所在的智能合約內(nèi)運行的,這樣的區(qū)別便造成了一定的安全隱患。如果DelegateCall()調(diào)用的目標函數(shù)來自于一個惡意的合約賬戶,該函數(shù)內(nèi)包含一些危險的操作,當該目標函數(shù)在調(diào)用者智能合約中運行的時候,將有可能給調(diào)用者造成損失。
存在一些智能合約,其功能是接收以太幣,并將收到的以太幣轉(zhuǎn)賬給其他地址,起到以太幣中轉(zhuǎn)的作用。但這些合約本身并不包含發(fā)送以太幣的函數(shù),無法自行實現(xiàn)轉(zhuǎn)賬功能,而只能通過DelegateCall()調(diào)用一些其他智能合約中的發(fā)送以太幣的函數(shù)從而實現(xiàn)轉(zhuǎn)賬功能。當這些提供以太傳輸代碼的合約執(zhí)行自毀操作時,調(diào)用了這些合約中的轉(zhuǎn)賬函數(shù)的合約將無法發(fā)送以太幣,智能合約賬戶所管理的所有以太幣都將被凍結。由于Parity Wallet 漏洞而被第二次攻擊的原因便是許多Wallet 合約只能依賴Parity Library 來完成對以太幣的操作,然而,當Parity Library被初始化為智能合約,然后被黑客殺死后。依賴Parity Wallet 進行以太幣操作的所有Wallet合約管理的以太幣都被凍結了。
本章分為測試用例的構建和模糊測試兩個部分,第一部分分別對七種類型的智能合約安全漏洞的測試用例的構建規(guī)則進行了描述(測試用例在漏洞檢測中的意義是:以太坊虛擬機在監(jiān)控智能合約執(zhí)行的過程中會收集一些信息,通過這些信息和測試用例的對照來判斷智能合約中是否包含該測試用例對應類型的漏洞);第二部分針對以太坊智能合約面臨的安全問題,根據(jù)3.1 節(jié)描述的測試用例構建方法,本文提出了一種基于模糊測試的漏洞檢測方案,對模糊測試的主要功能模塊設計思路以及漏洞檢測時的流程進行了講解。
3.1.1 不消耗gas的send漏洞的測試用例
在以太坊虛擬機中,對于send()函數(shù)的定義其實是一種特殊類型的call()函數(shù)。于是,構建出的不消耗gas的send 的測試用例要確保以太坊虛擬機中的調(diào)用確實是send()調(diào)用,并且測試用例需要發(fā)現(xiàn)send()調(diào)用在執(zhí)行期間返回了ErrOutOfGas的錯誤信息。
為了檢查以太坊虛擬機中的調(diào)用是否為send(),需要對調(diào)用的輸入是否為0 以及調(diào)用的gas 限制是否為2 300進行驗證。
3.1.2 異常無序漏洞的測試用例
對于異常無序的測試用例的構建原則是:對于一個嵌套調(diào)用鏈(或DelegateCalls()),其總是會起源于一個根調(diào)用(或DelegateCall()),在這個嵌套調(diào)用鏈(或DelegateCalls())中,如果至少有一個嵌套調(diào)用返回了異常,但根調(diào)用卻并沒有返回異常,這樣的情況說明異常沒有在嵌套調(diào)用鏈中正確地傳播回到根調(diào)用,于是認定該嵌套調(diào)用鏈包含異常無序的漏洞。
3.1.3 可重入性漏洞的測試用例
可重入性的測試用例是基于兩個子用例定義的:第一個子用例是ReCallTest,用于檢測在以調(diào)用Call()為根調(diào)用的調(diào)用鏈中,函數(shù)調(diào)用Call()是否不止一次地出現(xiàn)在這個調(diào)用鏈中;第二個子用例是AgentCallTest,其需要對三個條件進行檢查:條件一:用于傳輸?shù)囊蕴珟诺臄?shù)量(call()調(diào)用值)大于0;條件二:被調(diào)用的函數(shù)有足夠的gas來支付執(zhí)行復雜代碼時的費用;條件三:調(diào)用call()的是本文提出的漏洞檢測工具所提供的代理合約,而不是測試中待測智能合約所指定的賬戶。
基于兩個子用例構建可重入性的測試用例的具體形式如式(1):
在具體情境中,可重入性漏洞的表現(xiàn)為:當一個函數(shù)調(diào)用既是某個調(diào)用鏈的根調(diào)用,又是該調(diào)用鏈的結尾調(diào)用,也就是該調(diào)用經(jīng)過一串調(diào)用之后又回到其自身,并且該調(diào)用儲備了充足的gas,該調(diào)用通過call()函數(shù)向測試框架所提供的代理合約發(fā)送了以太幣。例如代理合約是通過對fallback函數(shù)的重復調(diào)用來體現(xiàn)它的可重入性的,當智能合約的執(zhí)行觸發(fā)了測試用例時,說明其存在遭受重入攻擊的可能性,便會將該智能合約標記為存在可重入性漏洞的脆弱智能合約。
3.1.4 時間戳依賴漏洞的測試用例時間戳依賴的測試用例是由三個子用例組成的。
三個子用例都是布爾型變量。第一個子用例是TimeStampBool,它代表的是當前待測的智能合約在執(zhí)行期間是否調(diào)用了時間戳的操作碼,如果調(diào)用,可以初步認定該智能合約的某些操作中用到了時間戳。第二個子用例是SendBool,它代表智能合約中是否包含發(fā)送以太幣到其他智能合約賬戶的send()調(diào)用。第三個子用例是EtherBool,它代表發(fā)送以太幣的數(shù)量是否大于0。
這三個子用例通過一種特定的關系組合之后獲得針對時間戳依賴漏洞的測試用例,該種關系如式(2):
判斷某一智能合約是否存在時間戳依賴漏洞的方法:待測智能合約使用了區(qū)塊的時間戳,并且在智能合約的執(zhí)行期間進行了以太幣的交易。
3.1.5 區(qū)塊號依賴漏洞的測試用例
區(qū)塊號依賴的測試用例類似于時間戳依賴,與時間戳依賴不同的是,區(qū)塊號依賴的測試用例主要檢查的是智能合約在執(zhí)行期間是否調(diào)用了區(qū)塊號的操作碼,而不是區(qū)塊的時間戳的操作碼。同樣,區(qū)塊號依賴的測試用例也是由三個子用例組成的。第一個子用例是BlockNumberBool,它代表的是當前待測的智能合約在執(zhí)行期間是否調(diào)用了區(qū)塊號的操作碼,其他兩個子用例與時間戳依賴的兩個子用例相同,分別是SendBool 和EtherBool。
區(qū)塊號依賴的測試用例是其三個子用例安裝和時間戳依賴相同的方式組合的,其具體表達為式(3):
對于是否存在區(qū)塊號依賴漏洞的判斷原則即為:當前待測智能合約使用了區(qū)塊的區(qū)塊號,并且在智能合約的執(zhí)行期間進行了以太幣的傳輸。
3.1.6 危險的DelegateCall漏洞的測試用例
危險的DelegateCall 的測試用例會對當前執(zhí)行的智能合約是否包含至少一個DelegateCall()函數(shù)以及DelegateCall()調(diào)用的函數(shù)是否是從初始調(diào)用的智能合約的輸入中獲得的??偟膩碚f就是測試用例會檢查被測試的智能合約是否調(diào)用了一個DelegateCall(),該DelegateCall()的目標函數(shù)是由潛在攻擊者所提供的。
3.1.7 凍結以太漏洞的測試用例
凍結以太的測試用例檢測的是一個智能合約在本身沒有transfer()、send()、call()、suicide()等函數(shù)的相關代碼來將以太幣發(fā)送到其他賬戶地址的情況下,是否可以接收以太幣以及是否在執(zhí)行的過程中使用了DelegateCall()函數(shù)。換一種方式來描述就是,如果一個智能合約在執(zhí)行期間其賬戶余額大于0,但該智能合約無法自行實現(xiàn)發(fā)送以太幣的功能,而是需要通過DelegateCall()調(diào)用其他智能合約中的可以實現(xiàn)以太幣轉(zhuǎn)賬功能的函數(shù),那么凍結以太的測試用例就會將該合約標記為有可能存在凍結以太漏洞的脆弱合約。
3.2.1 模糊測試概述
模糊測試主要包含兩個部分,其一是一個離線的以太坊虛擬機插樁工具,其二是一個在線的模糊測試工具,本文將著重就模糊檢測工具進行介紹。離線的以太坊虛擬機插樁工具通過對以太坊虛擬機進行插樁,使線上的模糊測試工具能夠?qū)χ悄芎霞s的執(zhí)行進行監(jiān)控,分析執(zhí)行日志文件用于漏洞分析;模糊測試工具通過對智能合約的代碼進行分析,生成模糊輸入,并根據(jù)漏洞的特征定義針對該種漏洞的測試用例,通過提取執(zhí)行日志文件中的關鍵信息與測試用例進行比較,從而判斷智能合約中是否包含該種類型的漏洞。該模糊測試工具的工作流程如圖1所示。
圖1 基于模糊測試的漏洞檢測概覽圖Fig.1 Overview of vulnerability detection based on fuzzing
在正式進行本文所涉及的工作之前,先通過網(wǎng)絡爬蟲的方式從Etherscan網(wǎng)站上爬取了一些已經(jīng)部署在以太坊平臺上的智能合約,爬取的內(nèi)容包括智能合約創(chuàng)建的代碼、ABⅠ接口以及這些智能合約的構造函數(shù)參數(shù)。在本文所涉及的工作中,已將爬取的智能合約重新部署在自行搭建的以太坊測試網(wǎng)絡中。一方面是將這些智能合約作為模糊測試的對象,另一方面是將這些智能合約作為使用合約地址作為參數(shù)的智能合約調(diào)用的輸入。
漏洞檢測的過程主要包含以下幾個步驟:
(1)ABⅠ及字節(jié)碼分析。對待測的智能合約的ABⅠ接口以及字節(jié)碼進行分析,需要對ABⅠ函數(shù)的每一個參數(shù)的數(shù)據(jù)類型和ABⅠ函數(shù)中所使用到的所有函數(shù)的函數(shù)簽名進行提取。
(2)映射關系建立。對從以太坊平臺上爬取下來的所有待測或是待引用地址的智能合約進行ABⅠ簽名分析,之后需要建立起智能合約和函數(shù)簽名的一個映射關系,即根據(jù)不同智能合約所包含的不同函數(shù)簽名進行索引。
(3)生成模糊輸入。在這一步模糊測試框架需要根據(jù)第一步和第二步的分析結果,根據(jù)待測智能合約的ABⅠ規(guī)范,生成有效的模糊測試輸入以及并未嚴格按照ABⅠ規(guī)范生成的異常輸入。
(4)啟動模糊測試。以隨機函數(shù)調(diào)用的方式將第三步生成的模糊測試輸入以及異常輸入并發(fā)送給相應的ABⅠ接口,智能合約進行執(zhí)行。
(5)結果輸出及分析。以太坊虛擬機對于模糊測試過程中智能合約的執(zhí)行情況進行監(jiān)控,并生成若干執(zhí)行日志,通過對這些執(zhí)行日志進行分析,實現(xiàn)智能合約的漏洞檢測和正確性分析。
當所有待測智能合約都完成了模糊測試時,整個模糊測試的過程結束。
3.2.2 模糊測試核心步驟
(1)對待測智能合約進行靜態(tài)分析
模糊測試框架對待測的智能合約進行靜態(tài)分析,并提取待測的智能合約的公共函數(shù)的函數(shù)簽名。從細節(jié)出發(fā)進行描述:將每個待測的智能合約的合約文件加上.abi 后綴,導出JSON 格式的ABⅠ,并提取出ABⅠ中聲明的所有函數(shù)簽名,再計算每一個函數(shù)簽名的前四字節(jié)的哈希值(此處哈希的方法是SHA-3)作為其函數(shù)選擇器,保證不同的函數(shù)簽名對應不同的函數(shù)選擇器。最后構建出了一個以函數(shù)選擇器為鍵,以包含所有該函數(shù)選擇器的智能合約的地址向量為值的鍵值對映射。
接下來將具體介紹模糊測試過程中對智能合約的靜態(tài)分析。工具通過分析JSON格式的智能合約ABⅠ接口,提取智能合約中每個函數(shù)的參數(shù)的參數(shù)類型以及關于函數(shù)的描述,大多數(shù)數(shù)據(jù)類型模糊的輸入域是可以確定的,但地址數(shù)據(jù)類型與其他數(shù)據(jù)類型不同。在模糊測試框架中,地址數(shù)據(jù)類型的參數(shù)不能隨機產(chǎn)生,其具體指代的是外部賬戶和智能合約賬戶的地址。當智能合約的地址作為實參被提供給智能合約中包含的函數(shù)的時候,函數(shù)接收到地址數(shù)據(jù)類型的參數(shù)輸入后便會調(diào)用call()函數(shù)和收到的地址參數(shù)對應的智能合約建立聯(lián)系,進行交互。所以,模糊測試框架必須要使用智能合約的地址來作為帶有地址數(shù)據(jù)類型的參數(shù)的ABⅠ函數(shù)的輸入。
算法1智能合約靜態(tài)分析算法
在對待測智能合約進行模糊測試時,需要對智能合約的ABⅠ函數(shù)簽名進行提取,并確定函數(shù)簽名與智能合約之間的映射關系。當具體給出一個智能合約的ABⅠ函數(shù)時,需要確定所有待測智能合約中,哪些可以與這個ABⅠ函數(shù)進行交互,也就是說哪些智能合約中還包含了該ABⅠ函數(shù)。該確定過程需要調(diào)用一個非常重要的函數(shù)call,call函數(shù)的參數(shù)的前四字節(jié)對應了模糊測試框架為每一個ABⅠ函數(shù)計算的函數(shù)選擇器(函數(shù)簽名前四字節(jié)的SHA-3哈希)。
算法1 將待檢測的二進制形式的智能合約作為輸入,輸出智能合約的每個ABⅠ函數(shù)到智能合約代碼中使用的函數(shù)選擇器集合的映射。首先,算法使用了以太坊虛擬機自帶的反匯編工具將智能合約的二進制代碼反匯編為匯編代碼。接下來,算法會對已經(jīng)轉(zhuǎn)為匯編代碼形式的智能合約中的公共ABⅠ函數(shù)進行提取,生成一個函數(shù)集合,該集合中存放的就是所有提取出的公共ABⅠ函數(shù)。之后算法會通過循環(huán)結構對上一步生成的函數(shù)集合中的每一個ABⅠ函數(shù)進行遍歷,在遍歷的過程中將會獲得該函數(shù)用到的函數(shù)選擇器,并生成一個函數(shù)選擇器集合。該循環(huán)結構在對每個ABⅠ函數(shù)進行遍歷的時候,首先會獲取其函數(shù)體,并通過函數(shù)體來獲取代碼段部分,并在每一次循環(huán)時為每一個ABⅠ函數(shù)創(chuàng)建一個函數(shù)選擇器集合。在該循環(huán)結構中還嵌套了兩層循環(huán),二層循環(huán)是對代碼段中的每一段進行遍歷,三層循環(huán)是對每一段中的每一行進行遍歷,通過每一段的第一行匯編代碼內(nèi)容來判斷該段內(nèi)容是否包含要提取的函數(shù)選擇器,如果是,就將函數(shù)選擇器分離出來并添加到函數(shù)選擇器集合中。在對每一段進行遍歷之后,函數(shù)選擇器集合中可能有不止一個函數(shù)選擇器,也可能一個都沒有,于是先對函數(shù)選擇器是否為空進行判斷,如果不為空,就將該集合與本輪遍歷的ABⅠ函數(shù)建立映射關系。當所有ABⅠ函數(shù)遍歷結束后,每一個ABⅠ函數(shù)都將建立和它對應的函數(shù)選擇器集合的映射關系。該算法需要對每一個待測的智能合約執(zhí)行。
根據(jù)該部分第一自然段可知,在本模塊功能中除了建立了ABⅠ函數(shù)和函數(shù)選擇器的映射關系以外,還建立了函數(shù)選擇器和支持該函數(shù)選擇器對應的函數(shù)的智能合約的地址向量的鍵值對關系。根據(jù)這兩對關系,不難再建立起ABⅠ函數(shù)和ABⅠ函數(shù)包含的函數(shù)選擇器對應的函數(shù)所支持的智能合約的關系。于是就根據(jù)ABⅠ函數(shù)找到所有相應的智能合約,并將它們存放在一個集合之中,再根據(jù)ABⅠ函數(shù)和智能合約建立起的關系,通過智能合約找到其包含的所有ABⅠ函數(shù),對這些ABⅠ函數(shù)進行模糊處理,針對其不同的類型的參數(shù)生成不同的模糊輸入。在前文提到了關于地址類型輸入的問題,如果隨機生成地址數(shù)據(jù)類型的輸入,無意義的地址會導致無法在智能合約之間建立聯(lián)系,所以地址數(shù)據(jù)類型的參數(shù)應該選用智能合約對應的地址。
(2)產(chǎn)生模糊輸入
在整個模糊測試框架中,生成模糊輸入是非常關鍵的一個步驟,也是工具實現(xiàn)以模糊測試為基本思路來檢測智能合約中的安全漏洞的技術核心,是“模糊”最直接也是最真實的體現(xiàn)。
首先需要明確的是對什么內(nèi)容進行模糊處理,生成模糊輸入。模糊測試框架是對待測智能合約中所包含的函數(shù)的參數(shù)進行模糊處理,生成模糊化的參數(shù)作為函數(shù)的輸入,并且不同類型的參數(shù)會對應不同的模糊處理方式。本文主要采用的模糊處理方法是先獲取該函數(shù)的所有參數(shù),將其存放在一個集合當中,然后對這個集合中的每一個參數(shù)進行遍歷,首先對它的類型進行判斷,然后再根據(jù)參數(shù)類型確定不同的模糊處理方式,按照選定的方式為每一個參數(shù)生成一個隨機的候選值集合,以備為函數(shù)提供多次不同的模糊輸入,進行漏洞檢測。該候選值集合所包含的模糊化參數(shù)個數(shù)也是根據(jù)參數(shù)的不同類型在不同的區(qū)間范圍內(nèi)隨機生成的。
本文采用的參數(shù)類型劃分方法是按照參數(shù)長度來劃分不同類型的參數(shù),將函數(shù)的所有參數(shù)劃分為定長參數(shù)和不定長參數(shù)兩個類型。對于定長參數(shù),只需要按照參數(shù)的長度,調(diào)用相應生成隨機數(shù)的函數(shù),產(chǎn)生模糊輸入集合即可;對于不定長的參數(shù),例如字符串等類型的參數(shù),首先需要隨機生成一個數(shù)據(jù)作為參數(shù)的長度,然后按照隨機生成的長度再隨機生成其參數(shù)的具體數(shù)據(jù)內(nèi)容。這樣的方式可以提高輸入的模糊程度,最大程度上覆蓋有可能的所有輸入。
對于每個函數(shù)參數(shù)的候選值集合,其候選值并非全部由隨機函數(shù)按照一定的條件生成,最終的候選值集合由兩個部分所組成,一部分是模糊輸入,另一部分是在對函數(shù)進行靜態(tài)程序分析的時候確定的該參數(shù)常用的一些數(shù)據(jù)。
在對于智能合約進行模糊測試時,需進行多次測試,次數(shù)不固定。在對待測智能合約集合中的每個智能合約進行測試之前會先生成一個確定范圍內(nèi)的隨機數(shù)作為其模糊測試的次數(shù),以此來達到同時保證測試效果以及測試效率的目的。接著會對該合約循環(huán)執(zhí)行隨機次數(shù)的模糊測試,在每次模糊測試時,還會在該智能合約包含的所有函數(shù)中隨機選擇一個函數(shù)進行模糊測試,從其參數(shù)的候選值集合中隨機選擇作為函數(shù)的模糊輸入。本文提及的七種智能合約安全漏洞中的大多數(shù)漏洞都可以按照常規(guī)的針對函數(shù)的參數(shù)生成模糊輸入,然后將模糊輸入作為參數(shù)傳遞給函數(shù),執(zhí)行智能合約這樣的方式進行漏洞檢測。但這樣的方式并不適合可重入性漏洞,或者可以說并不適合所有涉及到不止一個智能合約之間的相互聯(lián)系的漏洞類型。于是,為了解決這一問題,需要針對可重入性漏洞,根據(jù)其漏洞的原理,結合某些智能合約本身的結構特點,設計一個可能觸發(fā)可重入性漏洞的攻擊場景,以此來標記合約中的可重入性漏洞。
(3)收集測試樣本
上一部分介紹的模糊輸入為整個測試框架提供了輸入,輸入之后進行模糊測試,模糊測試的本質(zhì)即在輸入了模糊化參數(shù)之后,執(zhí)行待測智能合約。在智能合約的執(zhí)行過程中,需要提取一些關鍵信息來構建出本文第三章提到的針對不同安全漏洞類型的測試用例,對這些測試用例進行分析,以此來判斷正在執(zhí)行的智能合約中是否包含某種安全漏洞。
通過觀察智能合約的函數(shù)調(diào)用情況可以發(fā)現(xiàn),智能合約中非常多的規(guī)則的定義、交易行為的規(guī)定都和call()函數(shù)有關,有的會直接使用call()函數(shù),同時send()和DelegateCall()函數(shù)也占據(jù)了相當?shù)暮瘮?shù)調(diào)用比例。原因其實也很簡單,這幾種函數(shù)都可以實現(xiàn)不同賬戶之間的交易轉(zhuǎn)賬,而對于智能合約來說,其本身就包含了很多關于交易、轉(zhuǎn)賬的行為定義以及相關規(guī)則。前文中提到過send()函數(shù)其實是一種特殊的call()函數(shù),二者實現(xiàn)了相同的功能,區(qū)別是send()函數(shù)對gas 的最大支付值有限制(限制為2 300),而call()函數(shù)對gas 沒有限制;call()函數(shù)和DelegateCall()函數(shù)的功能也十分類似,二者的主要功能是通過調(diào)用其他合約的代碼,或是函數(shù)來實現(xiàn)轉(zhuǎn)賬、交易等行為。區(qū)別之處在于當他們在調(diào)用其他合約的函數(shù)時,程序的運行位置是不同的,當一個合約中的call()函數(shù)在調(diào)用其他合約的函數(shù)時,是在call()函數(shù)調(diào)用的函數(shù)所在的合約中運行的,當DelegateCall()函數(shù)調(diào)用其他函數(shù)時,代碼是在DelegateCall()函數(shù)本身所在的合約中運行的。由此可知,智能合約中call()函數(shù)相關的數(shù)據(jù)對于分析智能合約執(zhí)行過程中的交易行為有無異常非常重要。于是便對智能合約中call()函數(shù)的一些指標進行了提取,列舉在此:發(fā)起調(diào)用的智能合約賬戶地址、當前智能合約內(nèi)的函數(shù)、被調(diào)用的函數(shù)所在的智能合約賬戶地址、被調(diào)用的函數(shù)名、被調(diào)用的函數(shù)的輸入?yún)?shù)、傳輸?shù)囊蕴珟诺臄?shù)量、調(diào)用行為所需花費的gas、當前調(diào)用中用到的操作碼。
操作碼是這些指標中非常重要的一項,當在構建關于時間戳依賴漏洞和區(qū)塊號依賴漏洞的測試用例時,將會直接對于智能合約是否包含區(qū)塊時間戳和區(qū)塊號的操作碼進行檢查;除此之外,在前文提到的判斷智能合約中的ABⅠ函數(shù)的每段代碼是否包含函數(shù)選擇器時也用到了操作碼,其判斷方法是每個代碼段的第一行是否是操作碼PUSH4,PUSH4 的含義是將前四字節(jié)的數(shù)據(jù)放入堆棧中(以太坊虛擬機是堆棧虛擬機的一種),與之對應的是函數(shù)選擇器是由函數(shù)簽名的前四字節(jié)經(jīng)過SHA-3 哈希后得到的。操作碼不僅被直接用在某些測試用例中的,作為支撐起以太坊虛擬機執(zhí)行指令的重要部分,部分操作碼在執(zhí)行的過程中還會對智能合約的狀態(tài)產(chǎn)生影響。所以,對操作碼相關信息的分析對于安全漏洞檢測有著顯著的意義。
(4)日志分析與漏洞檢測
日志分析與漏洞檢測模塊通過調(diào)用go-ethereum庫(https://geth.ethereum.org)的以太坊虛擬機插樁工具對智能合約的執(zhí)行過程進行監(jiān)控,在監(jiān)控的過程中插樁工具會將能夠反饋智能合約執(zhí)行情況的信息以執(zhí)行日志文件的形式輸出。在對相關日志文件進行分析時,該模塊會提取日志文件中反映交易相關行為(轉(zhuǎn)賬、賬戶地址等)以及調(diào)用信息(Call()、Callnode()、DelegateCall()等)的內(nèi)容,并將提取的信息記錄在棧中。
在日志分析與漏洞檢測模塊中,棧是核心數(shù)據(jù)結構,發(fā)揮著非常重要的作用。該模塊會將交易的發(fā)起者、交易的接收者、轉(zhuǎn)賬的以太幣數(shù)量、耗費的gas、賬戶余額、以太幣存儲狀態(tài)等反映智能合約狀態(tài)以及區(qū)塊鏈狀態(tài)的信息存入棧中。當棧的長度達到一定數(shù)值(本文工作設置的閾值為1)時,分析模塊便會利用存儲在棧中的信息,調(diào)用對應的功能函數(shù),結合針對特定類型漏洞構建的測試用例進行檢查和判定。對棧中的信息分析完成后便清空棧,之后繼續(xù)將新提取的信息記錄在棧中進行分析,重復以上過程直到當前所有候選待測智能合約執(zhí)行完畢且全部交易執(zhí)行完畢。
在分別針對每種類型漏洞進行檢測時,會對該類型漏洞的所有候選智能合約執(zhí)行上述分析過程,逐一檢測判定這些智能合約中是否包含相應類型的漏洞,并將經(jīng)檢測可能存在對應漏洞的智能合約名稱匯總在一個名單列表文件中,該文件即為漏洞檢測的結果,該文件中包含的智能合約即為不滿足正確性的智能合約,包含任一類型或若干類型漏洞的智能合約均不滿足其正確性。候選智能合約集合中未出現(xiàn)在檢測結果名單的智能合約即為針對該種漏洞類型滿足正確性的智能合約。
本章對于使用本文提出的模糊測試框架對智能合約進行漏洞檢測的實驗過程進行了具體的闡述。在搭建好實驗平臺、配置好實驗環(huán)境之后,將該模糊測試框架部署在以太坊虛擬機中,將416個待檢測的智能合約部署在測試網(wǎng)上。針對7種類型的漏洞,對合約進行漏洞檢測,檢測出19個存在漏洞的不正確智能合約,經(jīng)人工分析驗證,其中18個檢測結果正確。
在進行智能合約漏洞檢測的實驗部分時,選用的實驗環(huán)境是在本機上安裝的Linux虛擬機,虛擬機系統(tǒng)選用的CentOS-7-x86_64,分配內(nèi)存為4 GB,部署4核處理器,硬盤分區(qū)40 GB。本機配備Ⅰntel?CoreTMi5-6200 CPU,已安裝內(nèi)存(RAM)8 GB。
搭建實驗環(huán)境的過程主要分為安裝CentOS7 虛擬機以及在虛擬機中安裝docker 應用容器引擎兩個主要步驟,安裝虛擬機時需要注意的是一定要分配足夠的內(nèi)存,原因是需要在虛擬機中搭建一個以太坊虛擬機平臺和一個測試網(wǎng),并且要在以太坊虛擬機中部署上百個智能合約,如果分配的內(nèi)存不夠會導致實驗無法正常進行,工具載入到某一步耗盡內(nèi)存之后便會拋出異常。對于docker 的安裝,采用了自動執(zhí)行的腳本命令,選用的是阿里云的鏡像。
由于在整個實驗中關注的只是智能合約包含安全漏洞的情況,于是在整個測試網(wǎng)中只設置一個對等節(jié)點。Docker應用容器引擎成功安裝后,在其中部署以太坊虛擬機平臺,并且在docker中安裝用于測試的geth客戶端并創(chuàng)建一條只包含一個節(jié)點的私有鏈。由于模糊測試框架的主體部分是基于go 語言編寫的,該語言與Java 有很多的相似之處,并且關于測試部分是以一個JavaScript項目架構的,于是選擇了web3.js(以太坊虛擬機兼容的JavaScript 應用程序接口)與docker 中的geth客戶端建立聯(lián)系。因為創(chuàng)建私有鏈的原因并不是為了實現(xiàn)真實的以太幣交易,而是對部署在測試網(wǎng)的四百余個智能合約進行漏洞檢測,所以私有鏈中對于新區(qū)塊的開采的虛擬過程設計較為簡單,以降低實驗復雜度并提高實驗效率,從而獲得較為理想的實驗結果。
實驗部分涉及的待檢測智能合約通過Python 爬蟲程序從Ethersca網(wǎng)站(https://etherscan.io/)爬取。Etherscan是瀏覽以太坊區(qū)塊鏈平臺上所有公共數(shù)據(jù)的安全工具,可以通過Etherscan 瀏覽的數(shù)據(jù)包括交易轉(zhuǎn)賬數(shù)據(jù)、錢包地址、智能合約等。Etherscan 網(wǎng)站支持查看智能合約、驗證智能合約、與智能合約進行必要的交互等操作。智能合約能夠被公開查看是Etherscan網(wǎng)站的優(yōu)勢之一,也滿足了本文工作的相應需求。
由于Etherscan 網(wǎng)站并沒有較為完善的反爬蟲機制,網(wǎng)站上的智能合約信息也相對固定,于是在爬蟲程序的編寫部分只要采用較為基礎的靜態(tài)爬取方法即可實現(xiàn)。在爬取過程中,獲取的信息主要包括智能合約的源代碼(存入.sol文件中)、智能合約的ABⅠ(存入.abi文件中)、智能合約的二進制代碼(存入.bin文件中)、智能合約的構造函數(shù)參數(shù)(存入.txt文件中)等。
然而,并非所有獲取的合約都將部署在測試網(wǎng)上進行漏洞檢測。首先,獲取的智能合約全部是帶有源代碼的合約,本文所采用的漏洞檢測方法是以提取并分析處理智能合約代碼中的指定信息為基礎的,雖然模糊測試框架對于智能合約的分析主要是針對于字節(jié)碼的(智能合約以字節(jié)碼的形式被部署在以太坊平臺上),但在手工對漏洞檢測的結果進行審計驗證時,需要對智能合約的源代碼,即高級編程語言進行分析,所以,獲取智能合約的源代碼對于實驗的正常進行有著非常重要的作用。其次,如果獲取的智能合約中涉及到其他賬戶或智能合約地址的調(diào)用,那么該地址需是有效地址,這樣才能保證部署在測試網(wǎng)上的智能合約可以和其他合約建立正常的交互關系。
實驗的具體過程嚴格遵循了測試框架進行模糊測試、漏洞檢測的步驟,實驗的具體細節(jié)描述如下:
本文采用的方法是以不同類型的漏洞為檢測基準,先選定即將投入檢測的漏洞類型,然后針對該種漏洞,對部署在測試網(wǎng)上的所有智能合約一一進行檢測(提取相關內(nèi)容并針對該種類型漏洞的測試用例進行分析),檢測的內(nèi)容即該合約中是否存在選定類型的漏洞。最后針對該種類型的漏洞生成一個檢測結果文件,即被測的智能合約中有哪些合約包含該類型漏洞,檢測結果中將這些合約的名稱列出。該種方法相比于前一種方法最明顯的改善是去除了很多冗余的檢測。最后達到的檢測效果便是每一種類型的漏洞對應一個合約名稱列表,該列表中包含的便是對所有待測合約進行檢測后得到的存在該種類型漏洞的智能合約。這樣的方式將會明顯提高漏洞檢測的效率,獲得的檢測結果也更加直觀。
對于待測智能合約的靜態(tài)分析是整個漏洞檢測過程的開始,同時也是支撐整個檢測過程的最基本的步驟。模糊測試框架主要是對待測智能合約的ABⅠ接口以及字節(jié)碼進行分析,在分析的過程中建立智能合約和ABⅠ函數(shù)、ABⅠ函數(shù)和其調(diào)用的函數(shù)兩對映射關系(分析過程中會為被調(diào)用的函數(shù)計算其獨一無二的函數(shù)選擇器)。通過這兩對映射,本文為每一個待測智能合約建立一個函數(shù)調(diào)用集合,針對函數(shù)調(diào)用集合中的每一個函數(shù),測試框架又會為其建立一個由模糊輸入組成的候選值集合,為后續(xù)的檢測過程做好準備。
在完成對于待測智能合約的靜態(tài)程序分析之后,便需要根據(jù)分析獲得的結果生成模糊輸入。這一步是整個漏洞檢測過程中的核心步驟。模糊測試其實并不是對智能合約本身進行模糊化處理。因為智能合約是由一些定義交易規(guī)則規(guī)范的代碼組成的程序,所以并不能對程序的代碼或是程序的執(zhí)行步驟進行模糊,這將會改變程序的功能或者直接導致程序崩潰,使得交易在執(zhí)行時出現(xiàn)一系列混亂和錯誤。測試框架真實的模糊對象是智能合約代碼中所包含的函數(shù)調(diào)用的參數(shù)。在對于智能合約的靜態(tài)分析步驟,可以獲得某一智能合約包含的所有函數(shù)調(diào)用集合,針對該集合中的每一個函數(shù)的參數(shù)(也可能不包含參數(shù)),模糊測試框架會根據(jù)參數(shù)的不同類型,按照不同的方案生成若干模糊候選值,在執(zhí)行智能合約進行模糊測試時,將這些候選值作為參數(shù)傳入函數(shù)。
智能合約的執(zhí)行過程本質(zhì)上就是不同賬戶之間根據(jù)智能合約所提供的規(guī)則(對智能合約所包含的ABⅠ函數(shù)的調(diào)用)進行交易,這些交易有可能涉及以太幣的轉(zhuǎn)移,也有可能是單純的信息傳遞。在常規(guī)的漏洞檢測流程之外,為了保證智能合約的正常執(zhí)行,還需要構建交易的參與者,即外部賬戶。除去普通的外部賬戶之外,針對一些特殊的漏洞類型,例如可重入性漏洞(其特殊之處在于該種漏洞會涉及到不同合約之間的相互調(diào)用以及函數(shù)的嵌套調(diào)用),無法通過普通的交易行為來檢測其是否存在于合約之中,于是便需要根據(jù)其具體的攻擊場景來構建代理賬戶,代理賬戶按照會造成可重入性攻擊的方式和其他用戶進行交易,與此同時標記出存在可重入漏洞的合約。
完成靜態(tài)分析,生成相關的模糊輸入后,便開始針對某一特定類型的漏洞,對已部署的每個智能合約進行漏洞檢測。首先通過隨機數(shù)生成器在某一區(qū)間內(nèi)生成一個整數(shù)作為對該智能合約進行漏洞檢測的次數(shù),該數(shù)字的選取區(qū)間需要適中,盡可能達到檢測效率以及檢測覆蓋率的平衡。在每一次漏洞檢測時,會隨機選擇該智能合約對應的函數(shù)調(diào)用集合中的某個函數(shù)進行模糊輸入。模糊輸入的內(nèi)容便是在該函數(shù)的參數(shù)對應的已生成好的若干候選值中隨機選取得到的。在一次漏洞檢測的過程中涉及了多次隨機選擇,強調(diào)隨機性的原因是為了最大限度的保證漏洞檢測的覆蓋率以及準確性,并通過隨機化的方式來模擬智能合約在不同的函數(shù)調(diào)用順序下的執(zhí)行情況。輸入完成之后,即監(jiān)控智能合約的執(zhí)行情況以及交易的情況。
測試框架通過以太坊虛擬機的HTTP 服務器監(jiān)控智能合約執(zhí)行的過程,收集交易過程中產(chǎn)生的一些關鍵信息,例如交易的發(fā)起者和接收者的地址、智能合約在執(zhí)行過程中的一些特定操作碼、交易涉及的以太幣金額、gas的耗費以及私有鏈在交易過程中的狀態(tài),賬戶余額等,將這些信息放入棧中,根據(jù)收集到的信息對已經(jīng)根據(jù)漏洞的特點構建好的測試用例進行分析、判斷,從而獲得最后的檢測結果,即某一智能合約中是否包含某一類型的漏洞。
在經(jīng)過大約40小時的實驗過后,針對7種類型智能合約安全漏洞,完成了對部署在測試網(wǎng)上的416個智能合約的漏洞檢測。
本文還與業(yè)內(nèi)的其他智能合約漏洞檢測工具進行了比較。本文使用開源的符號執(zhí)行領域的經(jīng)典工具OYENTE[11]對數(shù)據(jù)集中的全部416個智能合約進行漏洞檢測,并使用同樣開源的模糊測試工具ReGuard[17]對已分配給可重入性漏洞的13個智能合約實體進行漏洞檢測,對它們的檢測結果進行相應的對比分析。
首先,考慮到對不同類型漏洞進行檢測所需時間并不相同,所以根據(jù)檢測每種類型漏洞的時間耗費,分別為7 種典型的漏洞大致分配了一定數(shù)量的智能合約供其檢測??傆媽?16 個智能合約進行了漏洞檢測。針對7 種類型的漏洞分別進行漏洞檢測的智能合約的數(shù)量列舉如如表1所示。
表1 漏洞類型與智能合約數(shù)量對照Table 1 Vulnerability type versus number of smart contracts
下面,將對漏洞檢測實驗的結果進行展示,在表2中列舉了針對每種類型的漏洞檢測出的不正確智能合約的數(shù)量、不正確智能合約在所有被測智能合約中的數(shù)量占比、檢測出的不正確智能合約經(jīng)人工分析后是否確實包含漏洞,檢測正確率是多少。
表2 漏洞檢測結果Table 2 Vulnerability detection results
在17 個智能合約中,工具檢測出2 個包含不消耗gas的send漏洞的合約(EthronTokenPonzi和BuyerFund),在所有被檢測的智能合約中占比約11.76%,根據(jù)該種漏洞的特點可以確定,當合約在執(zhí)行過程中拋出了outof-gas異常,那么該合約包含不消耗gas的send漏洞,在實驗的過程中,監(jiān)控這兩個合約的執(zhí)行時,都拋出了該異常,可以認定并沒有出現(xiàn)錯報的情況,檢測結果是正確的。
在38個智能合約中,本文提出的方案檢測出1個智能合約包含異常無序漏洞。在對這個智能合約的代碼進行分析之后發(fā)現(xiàn),它們的代碼中均包含嵌套調(diào)用鏈,再結合以太坊虛擬機對于合約執(zhí)行的監(jiān)控信息,嵌套調(diào)用鏈中拋出異常的位置不是根調(diào)用,以此可以判斷,對于包含異常無序漏洞的合約的檢測結果是正確的。
在13個智能合約中,檢測出了1個包含可重入性漏洞的智能合約,在對于該智能合約進行人工審計驗證后可以判斷,其確實包含可重入性漏洞,檢測結果真實可信。
對于時間戳依賴漏洞和區(qū)塊號依賴漏洞,實驗中部署了相對較多的智能合約進行檢測。在152 個智能合約中檢測出4個包含時間戳依賴漏洞,在88個智能合約中檢測出3 個包含區(qū)塊號依賴漏洞。在經(jīng)過對于檢測結果的人工檢查之后發(fā)現(xiàn),對于區(qū)塊號依賴漏洞的檢測出現(xiàn)了誤報的情況(檢測出包含區(qū)塊號依賴漏洞的智能合約中,有一個智能合約并未包含相關的漏洞)。結合針對這兩種類型的漏洞構建測試用例的方法以及錯報的智能合約的實際情況,可以總結出錯報的原因是:工具根據(jù)測試用例中的判定規(guī)則進行判斷(規(guī)則即為合約代碼中是否包含時間戳/區(qū)塊號的操作碼以及是否涉及以太幣的傳輸,如果通過滿足條件,即判定包含漏洞),出現(xiàn)錯報情況的智能合約確實滿足測試用例的判定條件,但測試用例忽略了一種特殊情況,即智能合約中既包含區(qū)塊號的操作碼,又涉及了以太幣的交易轉(zhuǎn)賬,但區(qū)塊號在智能合約中的使用并不是作為以太幣傳輸?shù)汝P鍵操作的一部分,二者并無關聯(lián)。這種情況屬于少數(shù)現(xiàn)象但確實存在,而且在這兩種相似的漏洞類型中均可能出現(xiàn),所以對于時間戳依賴的檢測也可能出現(xiàn)錯報。這屬于對于漏洞的測試用例定義不完善所導致的錯報,若要想解決這個問題,則需要在測試用例中加入關于時間戳/區(qū)塊號是否與以太幣轉(zhuǎn)移行為有關的條件的判斷,并且需要在監(jiān)控合約執(zhí)行的過程中額外提取一些信息,特別是對交易發(fā)起方的交易行為的監(jiān)控信息。
對于危險的DelegateCall 漏洞,工具在28 個智能合約中檢測出1個包含該漏洞的合約,為了確認該檢測結果是否真實,對這個被檢測出的包含漏洞的智能合約進行了人工審查,發(fā)現(xiàn)這個合約中包含DelegateCall()函數(shù),并且該DelegateCall()調(diào)用的函數(shù)確實是從初始調(diào)用的智能合約的輸入中獲得的,由此發(fā)現(xiàn)完全符合該漏洞的測試用例條件,得到檢測結果正確的結論。
對于凍結以太漏洞,80個智能合約中經(jīng)過檢測發(fā)現(xiàn)了7個包含該漏洞的合約。經(jīng)過對于這7個合約的人工分析后發(fā)現(xiàn),它們確實自身沒有包含傳輸以太幣的功能,必須借助于對其他合約的函數(shù)的調(diào)用才能夠?qū)崿F(xiàn)以太幣轉(zhuǎn)賬的功能,所以可以證明檢測結果真實無誤。
對416個智能合約的漏洞檢測結果顯示,在報告的19 個包含漏洞的不正確合約中,18 個合約檢測結果是正確的,1個存在錯誤的判斷,整體上,本文提出的智能合約漏洞檢測技術具有較高的檢測正確率,達94.7%。
為了更加直觀地展示本文提出的方法在檢測以太坊智能合約漏洞的性能以及目前仍然存在的問題,本文進行了與智能合約漏洞檢測領域部分相關工作的對比實驗。本文提出的方法與Oyente、ReGuard兩種漏洞檢測工具進行對比,針對若干種相同類型的漏洞并針對相同數(shù)據(jù)集中的智能合約進行漏洞檢測實驗,并對實驗結果的差異性進行對比分析。
Oyente 工具作為符號執(zhí)行技術領域較為先進且經(jīng)典的漏洞檢測工具,可以對堆棧大小限制漏洞、事務順序依賴漏洞、時間戳依賴漏洞以及可重入性漏洞四種智能合約漏洞進行檢測;ReGuard是一種專門針對可重入性漏洞進行檢測的模糊測試工具。表3 對本文提出的方法、Oyente以及ReGuard三者能夠檢測的漏洞類型進行了統(tǒng)計。根據(jù)統(tǒng)計結果,在三種檢測方法涉及的9種漏洞類型中,本文提出的方法可檢測其中的7種,Oyente工具可以檢測其中的4 種,ReGuard 工具可以檢測其中的1 種。實質(zhì)上,在以太坊EⅠP150 硬分叉獲得的新鏈上,堆棧大小限制漏洞作為一種不具備區(qū)塊鏈特性的程序漏洞已經(jīng)得到了解決。將其排除后,對于涉及到的8種漏洞,本文提出的方法可以檢測其中的7 種,Oyente工具可以檢測其中的3 種,Reguard 工具可以檢測其中的1 種。三者中明顯本文提出的方法能夠檢測的漏洞種類最為豐富,并且Oyente工具能夠檢測的3種漏洞中只有事務順序依賴漏洞不能被本文提出的方法檢測。
表3 參與對照工具可檢測漏洞類型Table 3 Types of vulnerabilities detected by tools participating in comparison
本文提出的方法和Oyente 工具二者均可檢測的漏洞類型為可重入性漏洞和時間戳依賴漏洞,可重入性漏洞同時也可被ReGuard 工具檢測,于是在對比實驗中,使用Oyente 工具和ReGuard 工具對數(shù)據(jù)集中劃分給可重入性漏洞的13 個候選智能合約進行檢測,并使用Oyente 工具對劃分給時間戳依賴漏洞的152 個候選智能合約進行檢測,得到的實驗結果如表4所示。
表4 對比實驗結果Table 4 Results of comparison experiment
在參與對比的三者對可重入漏洞的檢測結果中,本文提出的方法檢測出1個包含可重入漏洞的智能合約,經(jīng)人工驗證檢測結果正確;Oyente工具檢測出5個包含可重入漏洞的智能合約,經(jīng)人工驗證,其中3個為誤報;ReGuard 工具檢測出2 個包含可重入漏洞的智能合約,經(jīng)人工驗證檢測結果均正確。
由此不難發(fā)現(xiàn),對比其他兩種工具的檢測結果,本文提出的方法遺漏了一個存在可重入漏洞的智能合約。對該合約進行人工分析后發(fā)現(xiàn),該合約在傳輸以太幣之前會進行較為復雜的條件判斷,在大多數(shù)情況下,所有條件判斷完成后,智能合約的執(zhí)行走向不涉及根調(diào)用函數(shù)經(jīng)過一連串調(diào)用又回到自身的情況,無法觸發(fā)本文制定的可重入漏洞的測試用例,因此導致漏報。后續(xù)還應對文中提出的漏洞檢測測試用例進行進一步完善,覆蓋盡可能全面的漏洞觸發(fā)路徑的同時確保檢測的準確性。
Oyente工具采用了符號執(zhí)行技術,在面對復雜的程序路徑時具備優(yōu)勢,其在13個候選合約中檢測出5個可能存在重入威脅的合約,但其中3個屬于誤報。誤報的原因包括未能充分考慮gas不足時無法執(zhí)行重入調(diào)用的情況、未能充分考慮即使存在重入調(diào)用,但被調(diào)用函數(shù)不涉及以太幣的轉(zhuǎn)移或涉及以太幣轉(zhuǎn)移的函數(shù)無法被調(diào)用的情況等。雖然Oyente 工具對于可重入漏洞的檢測更加全面完善,但其對于可重入漏洞的判定標準劃分不夠精確,檢測準確性明顯低于本文提出的方法。
ReGuard工具同樣采用了模糊測試技術,其在13個候選合約中檢測出2 個脆弱智能合約,檢測結果均正確,同時具備了漏洞檢測覆蓋能力以及檢測準確性。ReGuard工具在產(chǎn)生模糊輸入方面與本文不同的是,其對合約管理的交易行為進行模糊處理,生成隨機的交易,在合約執(zhí)行這些交易的過程中對不安全因素進行監(jiān)控。交易行為必然涉及虛擬數(shù)字貨幣的轉(zhuǎn)移,該種模糊處理方式可以對智能合約執(zhí)行過程中的關鍵行為進行針對性關注,排除無效信息的干擾。該思路對本文工作的后續(xù)延伸及改善具有借鑒意義。相較于本文提出的方法,ReGuard工具對于可重入性漏洞具備較強的檢測能力,但其能夠檢測的漏洞類型較為有限。
在本文提出的方法和Oyente 工具對時間戳依賴漏洞的檢測結果中,本文提出的方法檢測出4個包含時間戳依賴漏洞的智能合約,經(jīng)人工驗證檢測結果正確;Oyente 工具檢測出7 個包含時間戳依賴漏洞的智能合約,經(jīng)人工驗證,其中4個為誤報。
與檢測可重入漏洞時表現(xiàn)出的情況類似,Oyente工具面對同樣的候選合約數(shù)據(jù)集,相較于本文提出的方法檢測出了更多的潛在脆弱合約,但同時也存在一定的誤報情況,導致誤報的原因與本文提出的方法在對區(qū)塊號依賴漏洞進行檢測時出現(xiàn)的一次誤報情況的原因類似,即:在檢測的過程中發(fā)現(xiàn)智能合約中存在時間戳相關的操作,并且進行了以太幣的轉(zhuǎn)移,便認為該合約存在時間戳依賴漏洞,但時間戳并未直接參與以太幣轉(zhuǎn)移,一些通過操作時間戳進行攻擊的惡意行為實質(zhì)上并不會對以太幣的轉(zhuǎn)移造成威脅。與此同時,相較于本文提出方法的檢測結果,Oyente工具遺漏了一個存在時間戳依賴漏洞的合約,經(jīng)人工分析發(fā)現(xiàn),該合約在進行以太幣轉(zhuǎn)移操作之前,調(diào)用當前時間戳進行涉及密碼學加解密的條件判斷操作,符號執(zhí)行技術難以對該種涉及復雜數(shù)學函數(shù)的代碼結構進行分析。
對比實驗總結:相較于Oyente 工具,本文提出的方法在針對可重入性漏洞和時間戳依賴漏洞的檢測上具備更高的準確性,并且能夠檢測的漏洞種類更多,在對于可重入性漏洞的檢測能力上Oyente工具占優(yōu);相較于ReGuard工具,本文提出的方法雖然在針對可重入漏洞的檢測能力上略顯遜色,但仍具備較高的檢測準確性,并在可檢測漏洞種類方面明顯占優(yōu)。本文后續(xù)工作還應對于選擇更有效的模糊種子以及制定更完善的模糊輸入生成方案進行進一步探索。
本文提出了基于模糊測試的智能合約正確性檢測方案,構建了一個較為完整的基于模糊測試的漏洞檢測框架,并針對當前典型的7 種具備區(qū)塊鏈特點的漏洞,對若干智能合約進行了正確性檢測。從實驗結果來看,該工具生成的模糊輸入有效解決了傳統(tǒng)模糊測試方法存在的輸入數(shù)據(jù)量過大影響檢測效率的問題,與此同時在檢測過程中的隨機化過程又能從整體上保證檢測的覆蓋率。根據(jù)漏洞構建出的測試用例也能被監(jiān)控合約執(zhí)行時收集到的信息有效觸發(fā),對于智能合約是否因包含漏洞而影響正確性做出較為準確的判斷。
針對這7 種類型的漏洞,對416 個合約進行了漏洞檢測,報告了19個包含漏洞的不滿足正確性的合約,經(jīng)過對于代碼的人工分析后發(fā)現(xiàn),這19 個智能合約中18個存在漏洞,1個誤報,具有較高的檢測正確率。
針對本文進行的工作,其主要的幾個技術指標:檢測準確率、檢測代碼覆蓋率、可檢測的漏洞種類等都還有較大的上升空間。對于檢測準確率,需繼續(xù)對已知漏洞進行全面的分析,構建更加完善的測試用例,與此同時在監(jiān)控過程中收集更加全面的信息,以此來盡量避免誤報情況的出現(xiàn);對于檢測代碼覆蓋率,可通過統(tǒng)計漏洞檢測過程中進行模糊處理的函數(shù)占所有代碼的百分比獲得覆蓋率,并通過定義更加合理的隨機化處理過程提高檢測覆蓋率??蓹z測的漏洞種類越多,檢測工具的功能將會越發(fā)強大和完善,可以通過分析更多種已知的漏洞,構建更多種漏洞的測試用例,以此來擴展工具的檢測范圍。目前本文提出的模糊測試框架只能對以太坊平臺上的智能合約進行漏洞檢測,未來希望可以實現(xiàn)跨平臺的智能合約正確性檢測。