沈傳年
(國家計算機網(wǎng)絡(luò)應(yīng)急技術(shù)處理協(xié)調(diào)中心上海分中心 上海 201315)
智能合約由Szabo[1]于1994年首次提出,由于當時缺乏可信執(zhí)行環(huán)境等技術(shù)發(fā)展的限制,智能合約一直沒有被很好地應(yīng)用和發(fā)展,直到以以太坊[2]為代表的區(qū)塊鏈[3]2.0時代通過引入可編程性[4]的智能合約,將區(qū)塊鏈的應(yīng)用范圍從單一數(shù)字貨幣領(lǐng)域擴展至涉及合約功能的其他泛金融領(lǐng)域.在以太坊中,智能合約由參與交易的且互不信任的各方用戶共同協(xié)商制定,交易各方的權(quán)利及義務(wù)在合約中會被明確規(guī)定且對其進行編程,合約條款的響應(yīng)規(guī)則和觸發(fā)條件通過代碼實現(xiàn),交易各方在簽署私鑰后將合約隨同交易一并提交,并經(jīng)P2P網(wǎng)絡(luò)在全鏈進行傳播,區(qū)塊鏈全網(wǎng)節(jié)點收到合約并驗證通過后將合約進行存儲,智能合約會定期檢查每個合約的狀態(tài),并將滿足條件的事件進行排隊等待共識驗證.當大多數(shù)節(jié)點的驗證達成共識后,智能合約將自動執(zhí)行并將執(zhí)行結(jié)果進行保存.
隨著智能合約在區(qū)塊鏈中的應(yīng)用不斷發(fā)展,其面臨的安全問題也正日益突出:一是智能合約通常由于掌管著區(qū)塊鏈平臺上巨額的數(shù)字資產(chǎn),極易成為攻擊者的重要攻擊目標;二是智能合約的開發(fā)人員由于無法預(yù)見合約可能面臨的特殊環(huán)境狀態(tài),導(dǎo)致編寫的合約存在潛在的安全漏洞[5];三是使用Solidity語言編寫的智能合約更難以測試其安全性;四是區(qū)塊鏈的不可篡改、不可逆等特性,使得智能合約一旦遭到攻擊,無法中斷或終止合約的執(zhí)行.近年來,一系列智能合約應(yīng)用領(lǐng)域的安全漏洞事件頻發(fā),區(qū)塊鏈發(fā)展史上智能合約安全漏洞事件如表1所示,可以看出安全問題已成為制約智能合約和區(qū)塊鏈發(fā)展的重要因素,因此,加強對智能合約安全漏洞的研究已成為業(yè)界當前關(guān)注的重點.
表1 智能合約安全漏洞事件
與傳統(tǒng)合約相比,智能合約一方面具有合約內(nèi)容公開透明、不可偽造,合約執(zhí)行經(jīng)濟、高效等優(yōu)點,另一方面,由于智能合約編程語言本身的特點以及其運行過程無法更改等特性,導(dǎo)致智能合約面臨的安全問題日趨嚴重,因智能合約安全漏洞所引發(fā)的資產(chǎn)損失已躍居區(qū)塊鏈所有安全問題損失之首.本文通過分析過往智能合約安全漏洞事件,詳細介紹以下11種較為常見的智能合約安全漏洞問題,相關(guān)分析如表2所示.
表2 智能合約安全漏洞問題分析
整數(shù)溢出漏洞主要是由編寫智能合約的Solidity語言的整數(shù)類型所導(dǎo)致,同其他編程語言一樣,Solidity語言對不同類型的整數(shù)定義了不同長度的存儲空間,從uint8類型到uint256類型,其整數(shù)型變量均存在一定的表示范圍,如果所存儲的數(shù)值超出了整數(shù)變量類型可以表示的最大值或最小值,將會出現(xiàn)溢出現(xiàn)象.整數(shù)溢出通常包括上溢和下溢2種情況,上溢是指數(shù)值的增加超出可以存儲的最大值,下溢是指數(shù)值的減小超出可以存儲的最小值,無論是上溢還是下溢都會使其相應(yīng)的判斷機制出現(xiàn)異常,從而導(dǎo)致智能合約可能出現(xiàn)安全威脅.
拒絕服務(wù)漏洞是指攻擊者通過破壞智能合約中代碼的執(zhí)行邏輯,使得調(diào)用合約所需要的以太幣和gas等資源被不斷消耗,從而使得智能合約在短時間內(nèi)無法被用戶使用,甚至某些情況下永久性地無法被用戶使用,這就意味著智能合約中的以太幣將會永遠無法被用戶提取出來.針對拒絕服務(wù)漏洞的攻擊方式主要有3種[7]:一是通過(unexpected)Revert發(fā)動的攻擊,當智能合約的狀態(tài)由外部函數(shù)執(zhí)行結(jié)果來決定,且該執(zhí)行一直失敗時,如果未對該執(zhí)行失敗的情況進行處置,將可能面臨拒絕服務(wù)漏洞攻擊;二是通過gas限制發(fā)動攻擊,以太坊中區(qū)塊能消耗的gas都設(shè)定了上限,如果攻擊者惡意操控gas消耗至上限,將會導(dǎo)致合約交易失敗;三是通過owner賬戶發(fā)動攻擊,很多合約都擁有能夠開啟和暫停交易權(quán)限的owner賬戶,如果owner賬戶被攻擊者所操控,將可能導(dǎo)致合約交易被永久凍結(jié).
以太坊智能合約在執(zhí)行過程中通常都需要調(diào)用外部合約,因此能否返回被調(diào)用合約的異常狀態(tài)直接影響智能合約的正常執(zhí)行.如果智能合約執(zhí)行過程中遇到gas耗盡、調(diào)用棧溢出等情況,被調(diào)用合約正常情況下會終止合約的執(zhí)行并且返回false,由于Solidity語言對send(),delegatecall(),call()等低級別函數(shù)調(diào)用異常時,只能返回false,并不會拋出異常[8].因此,如果對函數(shù)的返回值未進行檢查,即便被調(diào)用合約執(zhí)行異常,交易也將繼續(xù)執(zhí)行,最終影響智能合約的順利執(zhí)行.
智能合約的權(quán)限控制漏洞主要是由于智能合約編寫者未能明確合約中函數(shù)的可訪問性,或者未能對函數(shù)的訪問權(quán)限進行仔細檢查,從而導(dǎo)致攻擊者能夠訪問原本不該被訪問的函數(shù)或變量.Solidity語言對函數(shù)和狀態(tài)變量的可見性設(shè)置了4種說明符,分別為external,internal,public,private,如果合約在編寫時沒有聲明其可訪問性,則函數(shù)的訪問權(quán)限默認為public,即函數(shù)既可以被合約內(nèi)部調(diào)用,也可以被外部合約調(diào)用,因此,函數(shù)也極易被攻擊者惡意調(diào)用.此外,合約中的owner賬戶通常具有超級權(quán)限功能,如果其超級權(quán)限被攻擊者竊取,將會嚴重破壞智能合約的執(zhí)行邏輯.
智能合約一般通過函數(shù)調(diào)用或者向外部用戶賬戶發(fā)送以太幣執(zhí)行對外部合約的調(diào)用,如果這些外部調(diào)用操作不當,則可能被攻擊者所利用,通過回調(diào)合約自身或者回退函數(shù)達到攻擊者所希望的目的.例如,智能合約在跨合約交易轉(zhuǎn)賬時,通常在轉(zhuǎn)賬后會更新接收方的賬戶余額,若接收方在接收轉(zhuǎn)賬的同時不斷回調(diào)轉(zhuǎn)賬函數(shù),可能會導(dǎo)致接收方的賬戶余額在保持不變的同時,不斷獲得轉(zhuǎn)賬發(fā)起方的資產(chǎn),直到轉(zhuǎn)賬發(fā)起方的賬戶余額不足,這就是重入攻擊漏洞[9].其代表案例就是2016年6月發(fā)生的The DAO攻擊,該攻擊直接使得以太坊出現(xiàn)了硬分叉[10].
代碼注入漏洞主要是由以太坊智能合約中的委托調(diào)用機制導(dǎo)致,委托調(diào)用機制所使用的delegatecall()能夠在自己合約的上下文環(huán)境中從其他地址動態(tài)加載代碼,以實現(xiàn)對其他合約中代碼的復(fù)用,如果智能合約被攻擊者注入一段可以修改合約中重要狀態(tài)變量或者竊取賬戶資產(chǎn)功能的惡意代碼并執(zhí)行,將會引發(fā)一系列安全問題.2017年7月,多重簽名錢包服務(wù)商Parity出現(xiàn)重大安全漏洞,攻擊者通過delegatecall()調(diào)用公開函數(shù)initWallet(),然后通過重新初始化錢包覆蓋之前錢包的所有者,從而使自己成為錢包的所有者.
短地址攻擊漏洞[11]通常出現(xiàn)在ERC-20(代幣標準協(xié)定)的代幣合約中.當智能合約在調(diào)用transfer()進行轉(zhuǎn)賬時,若攻擊者故意選擇以0結(jié)尾的地址,并在傳入地址參數(shù)時省略結(jié)尾的0,以太坊虛擬機(Ethereum virtual machine, EVM)在解析應(yīng)用二進制接口(application binary interface, ABI)字符時會發(fā)現(xiàn)傳入的地址長度未達到所要求的20B,EVM為了補齊地址長度,會借用表示轉(zhuǎn)賬金額的數(shù)據(jù)段的0補齊地址段的0,此時,數(shù)據(jù)段的長度也會相應(yīng)變短,EVM同樣會在數(shù)據(jù)段末尾對缺失的字節(jié)位用0補齊,從而導(dǎo)致代幣金額暴增,攻擊者通過該漏洞實施攻擊能夠極易竊取巨額賬戶資產(chǎn).
智能合約在對外部合約每進行1次調(diào)用時,EVM的調(diào)用棧深度都將增加1次,而在EVM中,調(diào)用棧有確定的最大深度即1024.若攻擊者惡意重復(fù)調(diào)用合約1023次之后,再通過發(fā)起交易觸發(fā)調(diào)用被攻擊目標的智能合約,使其因超出調(diào)用棧最大深度而溢出,從而導(dǎo)致合約執(zhí)行異常,假如被攻擊目標的智能合約無法進行異常處理,攻擊者將會通過調(diào)用棧溢出漏洞達成攻擊目的.
智能合約通常使用區(qū)塊的時間戳作為執(zhí)行合約,諸如發(fā)送以太幣等關(guān)鍵操作的觸發(fā)條件,不同的時間戳所產(chǎn)生的智能合約執(zhí)行結(jié)果一般也不相同.區(qū)塊鏈節(jié)點在處理新區(qū)塊時,通常會為新區(qū)塊設(shè)置1個時間戳,在以太坊中,新區(qū)塊的時間戳只有當大于上一區(qū)塊的時間戳且其變化范圍小于900s才是合規(guī)的,攻擊者可以通過惡意設(shè)置時間戳使得智能合約的執(zhí)行結(jié)果對自己有利,這就是時間戳依賴漏洞[12].
根據(jù)區(qū)塊鏈的共識機制[13],在以太坊中必須全網(wǎng)所有節(jié)點對交易驗證一致才能達成共識,因此以太坊智能合約中并不能直接生成隨機數(shù),合約開發(fā)人員一般通過block.number,block.timestamp,block.gaslimit,block.blockhash等區(qū)塊變量作為生成隨機數(shù)的隨機種子.由于區(qū)塊鏈數(shù)據(jù)的公開透明性,生成隨機數(shù)所依賴的區(qū)塊變量很容易被礦工獲取,礦工通過操控這些區(qū)塊變量能夠使得所生成的隨機數(shù)變得可預(yù)測,這在一定程度上增加了智能合約的安全風險.
在區(qū)塊鏈中,1個區(qū)塊通常包含多個交易,區(qū)塊中交易的打包順序與最終執(zhí)行順序可能并不相關(guān),如果區(qū)塊中的2個不同交易調(diào)用了同一個智能合約,交易用戶在執(zhí)行各自的合約調(diào)用時并不清楚合約當前處于什么狀態(tài),交易用戶想要調(diào)用的合約狀態(tài)與合約執(zhí)行時的實際狀態(tài)之間可能存在差別,而合約的最終執(zhí)行狀態(tài)取決于礦工所選擇的交易順序,這就是交易順序依賴.如果攻擊者通過懸賞合約或者減少合約回報等方式惡意擾亂交易順序,將會改變當前合約的執(zhí)行狀態(tài),造成嚴重的安全漏洞[14].
由于區(qū)塊鏈具有不可篡改性,智能合約一旦被部署上鏈,即使發(fā)現(xiàn)有代碼漏洞其執(zhí)行操作也無法更改,因此需要在智能合約正式部署之前對其可能存在的安全漏洞進行檢測.傳統(tǒng)的漏洞檢測方法主要依靠檢測人員的知識經(jīng)驗和主觀判斷,其不僅效率低、漏檢率高,而且適用范圍也有限,因此本文將重點介紹以下4種針對智能合約的漏洞檢測方法,包括形式化驗證、符號執(zhí)行、模糊測試、污點分析.
形式化驗證[15]方法通過基于語義、判斷、推理等邏輯語言對智能合約中的文檔內(nèi)容和程序代碼進行形式化建模,再通過嚴謹?shù)臄?shù)學推理邏輯和證明檢測智能合約函數(shù)功能的正確性及合約執(zhí)行的安全性[16].ZEUS[17]作為一款基于形式化驗證方法的漏洞檢測工具,能夠?qū)olidity語言翻譯為LLVM(low level virtual machine)中間語言,再通過形式化驗證其安全性,其支持包括重入攻擊漏洞、整數(shù)溢出漏洞在內(nèi)的多種合約漏洞的檢測;Securify[18]工具首先將智能合約的程序代碼編譯為字節(jié)碼,從字節(jié)碼中推理出精準的語義事實,并用Datalog語法對語義事實進行描述,然后通過將語義事實與服從和違反2種模式進行匹配來檢測智能合約的安全性;VaaS[19]是首個基于形式化驗證且同時支持多平臺的漏洞檢測工具,其能夠精確定位出風險的代碼位置,有效檢測出智能合約的常見安全漏洞.
符號執(zhí)行的主要思想是將程序的輸入變量值表示為符號值,然后逐步分析程序在不同符號值輸入情況下的執(zhí)行路徑,以發(fā)現(xiàn)相應(yīng)的漏洞問題.符號執(zhí)行包括靜態(tài)分析和動態(tài)分析,靜態(tài)分析不需要執(zhí)行程序.Oyente[20]是最早用于以太坊智能合約漏洞檢測的工具之一,其以EVM字節(jié)碼和以太坊全局狀態(tài)作為輸入,探索合約的不同執(zhí)行路徑,由于只支持針對單個交易觸發(fā)的漏洞檢測,因此其檢測功能并不完善;Manticore[21]是一款基于動態(tài)符號執(zhí)行的漏洞檢測工具,不僅支持多合約分析,還支持對其他二進制程序的分析;Mythril[22]作為一款基于符號執(zhí)行的靜態(tài)分析工具,通過對EVM字節(jié)碼的分析支持包括重入攻擊漏洞、整數(shù)溢出漏洞在內(nèi)的多種常見漏洞的檢測;Maian[23]則是一款針對長序列合約調(diào)用的漏洞檢測工具,其只支持少數(shù)類型的合約漏洞檢測.
模糊測試通過在目標應(yīng)用程序中構(gòu)造大量正常和非正常的非預(yù)期測試用例,并監(jiān)視目標應(yīng)用程序在執(zhí)行過程中的異常情況來發(fā)現(xiàn)漏洞問題,其中測試用例通過基于生成和基于變異2種方式進行構(gòu)造.ContractFuzzer[24]是首個基于模糊測試對智能合約進行漏洞檢測的工具,其使用智能合約ABI生成的隨機測試用例調(diào)用合約,然后通過監(jiān)聽EVM獲得合約的執(zhí)行日志,并分析日志檢測當前執(zhí)行的合約是否存在安全漏洞;Regurad[25]使用不斷迭代生成的隨機測試用例對合約進行模糊測試,并通過跟蹤合約執(zhí)行過程識別重入攻擊漏洞.
污點分析通常把智能合約中不受信任的節(jié)點或者由程序外部輸入的數(shù)據(jù)標記為污點,然后在程序運行過程中通過數(shù)據(jù)流或者控制流分析的方法持續(xù)監(jiān)控與污點數(shù)據(jù)相關(guān)的信息傳播路徑,進而對其進行漏洞檢測,污點分析一般不會單獨用于漏洞檢測,通常需要與其他技術(shù)結(jié)合使用.EasyFlow[26]是一款基于動態(tài)污點分析檢測智能合約中是否存在溢出漏洞的工具,其不僅能夠?qū)⒅悄芎霞s劃分為安全合約和存在溢出漏洞的不安全合約,還能夠自動生成交易以觸發(fā)潛在的溢出漏洞.
為了更加直觀地展現(xiàn)上述4種智能合約漏洞檢測方法及其所對應(yīng)的10種檢測工具的差異,本文將從每種檢測方法對應(yīng)的檢測工具所能夠檢測的漏洞類型、存在的不足之處等方面對上述4種漏洞檢測方法及所對應(yīng)的10種檢測工具進行簡要比較,如表3所示:
表3 智能合約漏洞檢測方法及工具概覽
當前,智能合約安全漏洞檢測技術(shù)已取得突破性的進展和較好的成果,其中上述介紹的形式化驗證、符號執(zhí)行、模糊測試、污點分析等仍為漏洞檢測的主要方法.因此,如何進一步改進和優(yōu)化漏洞檢測方法的不足將是未來研究的重要方向,未來漏洞檢測方法需要深入研究的方向包括以下4個方面:
1) 針對形式化驗證,未來研究方向應(yīng)根據(jù)不同的漏洞目標制定相對應(yīng)的驗證規(guī)范,以滿足大多數(shù)合約及多種漏洞類型的檢測需求,并不斷擴展形式化驗證的應(yīng)用范疇,逐步實現(xiàn)從檢測常見安全漏洞到復(fù)雜安全漏洞的進階.
2) 針對符號執(zhí)行,需要重點解決路徑爆炸等問題,一方面,可以通過諸如將深度優(yōu)先遍歷和寬度優(yōu)先遍歷算法相結(jié)合[27],提高符號執(zhí)行的路徑覆蓋率;另一方面,可以通過尋找智能合約中諸如call(),delegatecall()等容易產(chǎn)生漏洞的高危指令,并將與其相關(guān)的路徑定義為重點路徑,實際檢測中可以僅關(guān)注重點路徑,以有效縮減路徑空間,提高執(zhí)行效率.
3) 針對模糊測試,需要重點考慮對現(xiàn)有測試用例的生成算法進行改進優(yōu)化,同時需要進一步研究模糊測試與其他檢測方法的結(jié)合使用.例如模糊測試與符號執(zhí)行相結(jié)合的混合測試方法[28],可以在測試前判定適合模糊測試的路徑,從而制導(dǎo)模糊測試到達適合自己的代碼區(qū)域.
4) 智能合約的漏洞檢測當前尚未形成標準的測試案例集,現(xiàn)有漏洞檢測工具所使用的案例集大多相互獨立,嚴重影響漏洞檢測工具的評價效果,為了有效驗證漏洞檢測工具需要建立更加完善的智能合約漏洞庫.同時,綜合考慮漏洞檢測工具的檢測效率、檢測準確率、檢測漏洞類型及數(shù)量等指標因素,需要建立一套統(tǒng)一的漏洞檢測工具評價標準.
智能合約的引入極大地拓展了區(qū)塊鏈的應(yīng)用場景,但隨著智能合約在區(qū)塊鏈中應(yīng)用的不斷發(fā)展,其面臨的安全問題也正受到廣泛關(guān)注.本文梳理了整數(shù)溢出漏洞、重入攻擊漏洞等11種智能合約安全漏洞問題,并提出了相應(yīng)的防范策略,討論了形式化驗證、符號執(zhí)行、模糊測試、污點分析4種漏洞檢測方法,并引入了各漏洞檢測方法對應(yīng)的檢測工具,通過對各漏洞檢測方法和工具的分析,給出了現(xiàn)階段漏洞檢測工作中存在的不足,在此基礎(chǔ)上對未來的研究方向進行了展望.