陳錦富,王震鑫,蔡賽華,馮喬偉,陳宇豪,許容天,Patrick Kwaku Kudjo
(1.江蘇大學(xué)計(jì)算機(jī)科學(xué)與通信工程學(xué)院,江蘇 鎮(zhèn)江 212013;2.江蘇省工業(yè)網(wǎng)絡(luò)安全技術(shù)重點(diǎn)實(shí)驗(yàn)室,江蘇 鎮(zhèn)江 212013;3.威斯康星國際大學(xué)學(xué)院商業(yè)計(jì)算系,阿克拉 RE 00233)
隨著區(qū)塊鏈的發(fā)展,智能合約的使用得到了極大的普及[1]。由于不需要第三方就可以直接被調(diào)用并且自動(dòng)執(zhí)行交易[2-4],智能合約的影響力已經(jīng)擴(kuò)散到很多方面。同時(shí),針對智能合約的攻擊造成經(jīng)濟(jì)損失的風(fēng)險(xiǎn)也在逐漸提升,與傳統(tǒng)的分布式應(yīng)用程序平臺不同的是,區(qū)塊鏈的智能合約平臺在開放的網(wǎng)絡(luò)中運(yùn)行,任何用戶都可以參與其中。這就導(dǎo)致了任意存在惡意的節(jié)點(diǎn)都可能利用一些安全漏洞非法地從智能合約中獲利[5]。
目前,以太坊智能合約[6]安全事故發(fā)生的頻率逐年增加,每一次安全問題帶來的破壞力都是巨大的,這不僅對市場造成了毀滅性的打擊,還使用戶對智能合約的安全性產(chǎn)生懷疑。TheDao 事件[7-8]是以太坊歷史上最著名的安全事件,它導(dǎo)致了以太坊公鏈的硬分叉。
雖然已經(jīng)發(fā)現(xiàn)了智能合約安全漏洞的多種類型[9],但是這些漏洞通常在編寫智能合約代碼時(shí)易被忽視或者在合約分析時(shí)沒有完全被檢測出來,因此仍然有大量的智能合約存在安全漏洞[10-11]。到目前為止,研究人員已經(jīng)提出了多款針對智能合約的測試工具[12-13],如SmartDec 開發(fā)的分析工具SmartCheck[14],SRI 系統(tǒng)實(shí)驗(yàn)室開發(fā)的Securify 分析工具[15],Badruddoja等[16]開發(fā)的Oyente 靜態(tài)分析工具,F(xiàn)eist 等[17]提出的智能合約分析工具Slither。
一般來說,區(qū)塊鏈模型由數(shù)據(jù)層、網(wǎng)絡(luò)層、共識層、合約層、應(yīng)用層組成,共識層中封裝了網(wǎng)絡(luò)節(jié)點(diǎn)之間的共識算法以及激勵(lì)機(jī)制,在合約部署的調(diào)用過程中,數(shù)據(jù)存儲在節(jié)點(diǎn)中的順序與優(yōu)先級均服從共識機(jī)制的算法,因此調(diào)用智能合約需要額外的支出以保證各節(jié)點(diǎn)之間的協(xié)同和共識。以以太坊為例,調(diào)用智能合約涉及計(jì)算資源gas 的支付,它與以太幣之間存在較復(fù)雜的換算標(biāo)準(zhǔn),因此在動(dòng)態(tài)測試中獲取測試預(yù)言時(shí)將會(huì)面臨更大的困難[18-19],這將導(dǎo)致獲取預(yù)期結(jié)果消耗的資源代價(jià)過大或無法獲取預(yù)期結(jié)果。
綜上,本文提出了基于蛻變測試(MT,metamorphic testing)的方法用于檢測智能合約的安全漏洞。通過分析多種類型的智能合約安全漏洞,設(shè)計(jì)相應(yīng)的蛻變關(guān)系,將生成的測試用例分為源測試用例和后續(xù)測試用例,并分別在部署的智能合約中執(zhí)行,進(jìn)而驗(yàn)證兩組測試用例執(zhí)行的結(jié)果是否符合蛻變關(guān)系。
本文的主要貢獻(xiàn)如下。
1) 提出了一種基于蛻變測試的區(qū)塊鏈智能合約漏洞檢測方法,通過收集智能合約實(shí)例驗(yàn)證測試結(jié)果是否符合蛻變關(guān)系,從而判斷智能合約是否存在相應(yīng)的安全漏洞。
2) 從區(qū)塊鏈智能合約漏洞觸發(fā)原理的角度出發(fā),提出了面向區(qū)塊鏈智能合約安全漏洞的蛻變關(guān)系設(shè)計(jì)方法,并根據(jù)此方法設(shè)計(jì)了6 種適于智能合約安全漏洞測試的蛻變關(guān)系。
由于區(qū)塊鏈具有不可篡改的特性,為了在智能合約被部署前盡可能地檢測智能合約是否存在安全風(fēng)險(xiǎn),國內(nèi)外研究人員對智能合約分析工具進(jìn)行了相關(guān)的研究。
由 SmartDec 開發(fā)的智能合約分析工具SmartCheck[18]通過將Solidity 源代碼直接轉(zhuǎn)換為基于XML 的中間表示,然后根據(jù)XPath 模式檢查中間表示以識別潛在的安全漏洞,這種方法缺乏準(zhǔn)確性,因?yàn)槟承╁e(cuò)誤不能表示為XPath 模式。例如,重入攻擊漏洞很難表示為XPath 模式,因此不會(huì)被檢測到。此外,由于Smartcheck 使用XPath 模式來檢測某些bug 的特定語法,因此即使bug 片段語法發(fā)生細(xì)微變化,也不會(huì)與XPath 模式匹配。由SRI系統(tǒng)實(shí)驗(yàn)室開發(fā)的Securify[19]通過解析和反編譯EVM 字節(jié)碼,然后將生成的代碼轉(zhuǎn)換為語義事實(shí),最后將事實(shí)與預(yù)定義的模式列表相匹配以檢測安全漏洞,但是Securify 無法檢測到重入、未經(jīng)檢查的發(fā)送、未處理的異常,還存在TOD 誤報(bào)的問題。根據(jù)研究發(fā)現(xiàn),Securify 報(bào)告的誤報(bào)是因?yàn)檫^度的近似執(zhí)行[20]。此外,Grieco 等[21]提出使用模糊測試對智能合約安全漏洞進(jìn)行檢測的方法Echidna,該方法使用靜態(tài)分析框架Slither編譯合約并對其進(jìn)行預(yù)處理,處理并識別智能合約中的重要常量和函數(shù),然后進(jìn)行模糊測試。該方法需要檢測隨機(jī)交易中的違規(guī)屬性,這使對違規(guī)屬性的定義顯得非常重要。在軟件測試中,預(yù)言問題會(huì)經(jīng)常伴隨著測試軟件類型的多樣化出現(xiàn),有時(shí)難以對隨機(jī)的測試用例得出的結(jié)果進(jìn)行判斷。因此,本文將使用蛻變測試解決在區(qū)塊鏈智能合約領(lǐng)域中出現(xiàn)的預(yù)言問題。
蛻變測試與其他智能合約檢測方法的優(yōu)勢對比如表1 所示。
表1 蛻變測試與其他智能合約檢測方法的優(yōu)勢對比
本文所提方法主要基于區(qū)塊鏈智能合約漏洞分析和蛻變測試,下面針對相關(guān)概念和預(yù)備工作進(jìn)行介紹。
針對區(qū)塊鏈軟件的測試普遍面臨預(yù)言問題。通常,在執(zhí)行測試用例x之后,需要一種稱為測試預(yù)言的系統(tǒng)機(jī)制來檢查執(zhí)行結(jié)果。如果執(zhí)行結(jié)果與預(yù)期結(jié)果不一致,說明x是導(dǎo)致失敗的測試用例;否則,說明x不是導(dǎo)致失敗的測試用例。然而,在許多現(xiàn)實(shí)測試中,測試預(yù)言可能不存在,或者它可能存在但資源限制使其無法使用。當(dāng)預(yù)言問題發(fā)生時(shí),許多可靠測試集問題策略的適用性和有效性都會(huì)變得有限。
目前,蛻變測試被廣泛應(yīng)用于多個(gè)場景,具有代表性的是將蛻變測試應(yīng)用于機(jī)器學(xué)習(xí)分類器[22]。在Weka3.5.7版本上進(jìn)行了測試,以k-最近鄰(KNN,k-nearest neighbor)和樸素貝葉斯分類器(NBC,naive Bayesian classifier)的實(shí)現(xiàn)為例。通過改變算法的實(shí)現(xiàn)來注入漏洞,然后使用交叉驗(yàn)證和蛻變測試方法來檢測漏洞。實(shí)驗(yàn)證明,一些錯(cuò)誤很難通過交叉驗(yàn)證檢測到,但可以通過蛻變測試檢測到。
蛻變測試的核心元素是一組蛻變關(guān)系(MR,metamorphic relation)[23],它是與多個(gè)輸入及其預(yù)期輸出相關(guān)的目標(biāo)函數(shù)或算法的必要屬性。在實(shí)現(xiàn)蛻變測試時(shí),首先生成一些程序輸入(稱為源輸入)作為原始測試用例,在此基礎(chǔ)上使用蛻變關(guān)系生成新的輸入作為后續(xù)測試用例。蛻變測試會(huì)根據(jù)相應(yīng)的蛻變關(guān)系驗(yàn)證源測試用例和后續(xù)測試用例的輸出。
基于上述的指導(dǎo)思想,在區(qū)塊鏈智能合約中構(gòu)建蛻變關(guān)系需要滿足以下兩點(diǎn)特性。
1) 針對區(qū)塊鏈智能合約的蛻變測試中,蛻變關(guān)系是它的核心元素。
2) 蛻變關(guān)系的生成,需要確定多個(gè)輸入及其預(yù)期輸出的目標(biāo)函數(shù)或算法。
針對這兩點(diǎn)特性,需要對待測的智能合約進(jìn)行處理和分析,通過處理確定目標(biāo)算法,作為構(gòu)建蛻變關(guān)系的依據(jù)。
使用蛻變測試方法,通過設(shè)計(jì)蛻變關(guān)系的多樣性可以檢測多種類型的漏洞。在此之前,需要對相應(yīng)類型的漏洞原理進(jìn)行分析。
在區(qū)塊鏈中,整型溢出漏洞是最常見同樣也是非常嚴(yán)重的安全漏洞,它導(dǎo)致的經(jīng)濟(jì)損失在區(qū)塊鏈領(lǐng)域中占比很高。
在solidity 語言中,整型變量支持的整數(shù)類型以8 bit 為步長遞增,定義的范圍為28~2256,在定義時(shí)沒有特別說明的整型變量是256 bit。如圖1 所示,一個(gè)uint8 類型變量可以存儲的數(shù)據(jù)范圍為[0,28-1]。這意味著在智能合約的使用中,通過算數(shù)操作使變量存儲的內(nèi)容超過整型定義的上限或下限(變量x1在算數(shù)操作后溢出到x2的位置),那么將會(huì)導(dǎo)致合約執(zhí)行的結(jié)果發(fā)生錯(cuò)誤,對使用合約的用戶都將造成巨大損失。
圖1 智能合約中溢出漏洞的觸發(fā)原理
存在此類漏洞的智能合約通常出現(xiàn)在交易轉(zhuǎn)賬、代幣發(fā)行等智能合約中。根據(jù)國家區(qū)塊鏈漏洞庫中近年來收錄的有關(guān)整型溢出的漏洞案例可知,多數(shù)受到攻擊的智能合約為通過代幣發(fā)行的整型溢出漏洞造成項(xiàng)目增發(fā)無限量代幣,它們通常與存儲代幣的參數(shù)未進(jìn)行溢出漏洞的檢查相關(guān)。
重入攻擊是區(qū)塊鏈系統(tǒng)早期最著名同時(shí)也是危害性最大的攻擊方式之一[24]。近幾年來,重入攻擊對區(qū)塊鏈造成了巨大的經(jīng)濟(jì)損失。在以太坊的智能合約中,可以聲明一個(gè)不帶任何參數(shù)和返回值的匿名函數(shù),稱為fallback 函數(shù),當(dāng)對這個(gè)合約發(fā)送消息時(shí),如果沒有找到相匹配的函數(shù)就會(huì)調(diào)用fallback 函數(shù)。如圖2 所示,攻擊者調(diào)用攻擊合約,在被攻擊合約更新攻擊者的余額之前調(diào)用fallback函數(shù),fallback 函數(shù)再次調(diào)用取款方法,然后繼續(xù)循環(huán)上述流程,直至攻擊合約中的資源被消耗完畢或攻擊合約中的余額被完全盜取。
根據(jù)solidity 的定義,向合約發(fā)送send、transfer、call 消息時(shí)都會(huì)調(diào)用fallback 函數(shù),不同的是send和transfer 有2 300 gas 的限制,傳遞給fallback 函數(shù)的gas 只用于日志的記錄。因此,如果增加其他操作則可能會(huì)超過gas 的限制。而call 則會(huì)把剩余的gas 都傳遞給fallback 函數(shù),使其可能會(huì)擁有足夠的gas 進(jìn)行循環(huán)調(diào)用和使用其他方法進(jìn)行攻擊。
本節(jié)將在傳統(tǒng)軟件的蛻變測試基礎(chǔ)上,結(jié)合區(qū)塊鏈智能合約的特點(diǎn),與傳統(tǒng)蛻變測試的流程融合后設(shè)計(jì)出適合智能合約的蛻變測試框架。
區(qū)塊鏈系統(tǒng)中,智能合約有“公開透明”“不可篡改”的特點(diǎn)。隨著區(qū)塊鏈的發(fā)展,智能合約的類型更加多元,可實(shí)現(xiàn)的功能也更加豐富,因此帶來的安全缺陷也更加多樣。這意味著在針對智能合約的整個(gè)蛻變測試中,需要對智能合約進(jìn)行完整的分析或預(yù)處理,包括智能合約每個(gè)方法實(shí)現(xiàn)的功能。根據(jù)智能合約的具體功能分析合約可能存在的潛在漏洞,這些依據(jù)對后續(xù)確定目標(biāo)變量、設(shè)計(jì)蛻變關(guān)系都是至關(guān)重要的。因此,智能合約應(yīng)在部署上鏈之前進(jìn)行完備的安全漏洞測試。為了應(yīng)對在區(qū)塊鏈測試中更加頻發(fā)的測試預(yù)言獲取難題,本文提出了基于蛻變測試的智能合約安全漏洞檢測方法。
由于蛻變測試中蛻變關(guān)系的特殊機(jī)制,蛻變測試擁有兩組相對應(yīng)的測試用例輸入,即源測試用例和后續(xù)測試用例,通過判斷它們的輸出是否符合蛻變關(guān)系從而獲得測試判定。在智能合約測試中可以繞過測試預(yù)言的獲取階段,有效地應(yīng)對了測試預(yù)言獲取的難題,從漏洞觸發(fā)角度設(shè)計(jì)蛻變關(guān)系并用于相應(yīng)智能合約的蛻變測試,保證了測試的準(zhǔn)確率。
為了將蛻變測試方法在區(qū)塊鏈智能合約中有效實(shí)現(xiàn),本文提出了一種區(qū)塊鏈智能合約蛻變測試方法。圖3 給出該方法的實(shí)施流程,整個(gè)方法分為4個(gè)模塊:智能合約分析模塊,通過分析和預(yù)處理確定待測合約的相關(guān)變量和輸入域;蛻變關(guān)系設(shè)計(jì)模塊,根據(jù)智能合約存在的多類型漏洞觸發(fā)邏輯構(gòu)建蛻變關(guān)系;測試用例生成模塊以及蛻變關(guān)系驗(yàn)證模塊。
圖3 區(qū)塊鏈智能合約蛻變測試方法
為了更全面地對區(qū)塊鏈智能合約的蛻變測試的流程進(jìn)行解釋,在算法1 中以偽代碼的形式對圖3中展示的流程進(jìn)行進(jìn)一步展示。其中,k[i]為輸入域內(nèi)隨機(jī)生成的源測試用例,MR 為設(shè)計(jì)的蛻變關(guān)系,k’[i]為后續(xù)測試用例。判斷輸出的結(jié)果是否符合預(yù)先設(shè)計(jì)的蛻變關(guān)系,若不符合,則說明智能合約存在相應(yīng)的安全漏洞。
算法1區(qū)塊鏈智能合約蛻變測試算法
根據(jù)3.1 節(jié)中描述的特點(diǎn)可知,與傳統(tǒng)軟件的蛻變測試相比,針對區(qū)塊鏈智能合約的蛻變測試對智能合約的分析或預(yù)處理也在整體的測試流程中占有重要的作用。因此,將蛻變測試方法融入?yún)^(qū)塊鏈智能合約中。智能合約分析、蛻變關(guān)系設(shè)計(jì)以及蛻變關(guān)系驗(yàn)證3 個(gè)方面組成了整個(gè)測試流程的框架,如圖4 所示。
根據(jù)圖4 可以看出,除了智能合約分析,蛻變關(guān)系設(shè)計(jì)和蛻變關(guān)系驗(yàn)證也是整個(gè)蛻變測試的核心。因此,第4 節(jié)將詳細(xì)介紹蛻變關(guān)系的設(shè)計(jì)方法和設(shè)計(jì)過程。
本節(jié)將詳細(xì)展示針對第3 節(jié)中介紹的區(qū)塊鏈智能合約出現(xiàn)的漏洞的蛻變關(guān)系應(yīng)如何設(shè)計(jì)。具體來說,蛻變關(guān)系的設(shè)計(jì)涉及2 個(gè)步驟:1) 將待測的智能合約進(jìn)行分析,提取可能觸發(fā)漏洞的函數(shù)及目標(biāo)變量,涉及蛻變關(guān)系中測試用例輸入的變量,需要確定它們的輸入域;2) 針對待測智能合約的功能及可能出現(xiàn)的漏洞類,根據(jù)潛在漏洞涉及的變量與輸出之間的屬性設(shè)計(jì)蛻變關(guān)系。
根據(jù)整型溢出的特點(diǎn),將對可能存在整型溢出漏洞的智能合約進(jìn)行初步分析,使生成的源測試用例和后續(xù)測試用例的蛻變關(guān)系滿足觸發(fā)整型溢出漏洞的必要條件。圖5 為一個(gè)存在加法溢出漏洞的智能合約的部分內(nèi)容,其描述了一個(gè)銀行定期儲蓄的智能合約。在合約中,針對每個(gè)用戶設(shè)置了一個(gè)mapping 用于設(shè)置定期儲蓄時(shí)間,值為uint256 類型。之后的3個(gè)函數(shù):deposit()為存款函數(shù),并且儲蓄時(shí)間至少為一周;increaseLockTime()為增加儲蓄時(shí)間的函數(shù),用戶可以根據(jù)需求自行增加儲蓄時(shí)間;withdraw()為取款函數(shù),當(dāng)用戶擁有余額且當(dāng)前的時(shí)間戳遲于儲蓄結(jié)束后預(yù)定的時(shí)間戳后可以將儲蓄余額取出。
圖5 存在加法溢出漏洞的智能合約的部分內(nèi)容
此案例為智能合約加法溢出的實(shí)例。通過分析,合約中涉及輸入的主要參數(shù)為存款數(shù)和定期儲蓄時(shí)間,這兩類參數(shù)雖然同時(shí)影響取款操作的輸出結(jié)果,但整型溢出漏洞是否觸發(fā)主要在于儲蓄時(shí)間lockTime,由于設(shè)置定期儲蓄時(shí)間時(shí)并未使用SafeMath 進(jìn)行溢出檢測,因此通過增加定期儲蓄時(shí)間,可以使儲蓄時(shí)間發(fā)生上溢,使儲蓄時(shí)間溢出,那么定期儲蓄時(shí)間將會(huì)溢出到0。以此跳過時(shí)間檢測機(jī)制取出存款。
根據(jù)此智能合約的設(shè)計(jì)邏輯,當(dāng)目標(biāo)變量x為定期儲蓄時(shí)間時(shí),f(x)為取款操作執(zhí)行時(shí)取出的存款,得出蛻變關(guān)系設(shè)計(jì)依據(jù)的必要屬性為
在沒有觸發(fā)加法溢出的前提下,每次在確定的輸入域中根據(jù)蛻變關(guān)系改變目標(biāo)變量的輸入,相應(yīng)的輸出就必然遵循該必要屬性。基于此,可以設(shè)計(jì)蛻變關(guān)系MR1
其中,x1、x2分別表示目標(biāo)變量(儲蓄時(shí)間),對應(yīng)的P(x1)、P(x2)分別表示在改變了儲蓄時(shí)間后進(jìn)行取款操作的結(jié)果。
MR 的公式基于已提出的一階謂詞邏輯的蛻變關(guān)系形式化模型[25],具有精確的描述形式,同時(shí)也具備較強(qiáng)的兼容性,MR2~MR6的蛻變關(guān)系也是基于此模型構(gòu)建的。根據(jù)智能合約中的定義,確定了構(gòu)建蛻變關(guān)系涉及的4 個(gè)輸入變量和一個(gè)輸出變量:定期儲蓄時(shí)間A、存款金額B、增加儲蓄時(shí)間P、取款金額M和儲蓄余額在每一次測試前后的差值S。因此,將MR1的形式化公式分解為表格形式,如表2 所示。
表2 MR1 中目標(biāo)變量間的蛻變關(guān)系
在該蛻變關(guān)系中,源測試用例的輸入以隨機(jī)測試的方法隨機(jī)生成。后續(xù)測試用例生成的蛻變關(guān)系在源測試用例的基礎(chǔ)上倍數(shù)遞增,當(dāng)儲蓄時(shí)間遞增到發(fā)生溢出時(shí),儲蓄時(shí)間將會(huì)溢出到從0 開始,因此每次后續(xù)測試用例執(zhí)行后,將執(zhí)行一次取款操作。如果該操作成功執(zhí)行,即取款金額與儲蓄余額的差值相等,說明檢測出了儲蓄時(shí)間的溢出,該合約存在整型溢出的漏洞。
根據(jù)此智能合約的設(shè)計(jì),當(dāng)目標(biāo)變量x為定期儲蓄時(shí)間時(shí),f(x)為取款操作后用戶的余額差值,得出蛻變關(guān)系設(shè)計(jì)依據(jù)的必要屬性為
在沒有觸發(fā)溢出漏洞的前提下,每次在確定的輸入域中根據(jù)蛻變關(guān)系改變目標(biāo)變量的輸入,相應(yīng)的輸出就必然遵循該必要屬性。在表2 所示的蛻變關(guān)系的基礎(chǔ)上,可以進(jìn)一步改進(jìn)對智能合約整型溢出漏洞檢測的蛻變關(guān)系MR2
其中,x1、x2分別表示目標(biāo)變量(單次轉(zhuǎn)賬金額),對應(yīng)的P(x1)、P(x2)分別表示目標(biāo)變量的兩次不同的實(shí)際取值(轉(zhuǎn)賬操作后接收方賬戶的余額)。
在solidity 中,整型變量支持的整數(shù)類型以8 bit為步長遞增,即整型定義的范圍為28~2256,在定義時(shí)沒有特別說明的整型變量是256 bit。在蛻變測試中以28為下限,試圖在一個(gè)uint8 的整型變量中存儲數(shù)字256,則它將會(huì)溢出變成0。
將MR2的形式化公式分解為表格形式,如表3所示。
表3 MR2 中目標(biāo)變量間的蛻變關(guān)系
在隨機(jī)生成的源測試用例的基礎(chǔ)上,每一次生成后續(xù)測試用例時(shí)依次增加28n的儲蓄時(shí)間,這種蛻變關(guān)系可以大大減少測試整型溢出漏洞時(shí)的時(shí)間復(fù)雜度。
通過上述2 種關(guān)于檢測智能合約整型溢出漏洞的蛻變關(guān)系,可以在實(shí)驗(yàn)中針對測試具體的智能合約中觸發(fā)漏洞的變量進(jìn)行修改,從而驗(yàn)證待測合約是否存在整型溢出漏洞。
對于智能合約中出現(xiàn)的減法溢出型漏洞,通常其整型變量的賦值會(huì)超出其下限。圖6 是一個(gè)存在減法溢出漏洞的智能合約的部分內(nèi)容。
圖6 存在減法溢出漏洞的智能合約的部分內(nèi)容
此案例為智能合約減法溢出漏洞的實(shí)例。通過分析,合約中涉及輸入的主要參數(shù)為接收轉(zhuǎn)賬用戶的代幣存量,如果在對balances[owner]的計(jì)算中未使用SafeMath,當(dāng)轉(zhuǎn)出代幣總量大于owner 賬戶余額時(shí),balances[owner]產(chǎn)生減法溢出,變成一個(gè)極大值,造成代幣增發(fā)的嚴(yán)重后果。
根據(jù)此智能合約的設(shè)計(jì),當(dāng)目標(biāo)變量x為單次發(fā)起轉(zhuǎn)賬交易的金額時(shí),f(x)為轉(zhuǎn)賬操作后發(fā)送方賬戶的余額,得出蛻變關(guān)系設(shè)計(jì)依據(jù)的必要屬性為
在沒有觸發(fā)漏洞的前提下,每次在確定的輸入域中根據(jù)蛻變關(guān)系改變目標(biāo)變量的輸入,相應(yīng)的輸出就必然遵循該必要屬性?;诖撕?.1 節(jié)加法溢出中改進(jìn)的蛻變關(guān)系設(shè)計(jì)思路,可以設(shè)計(jì)減法溢出的蛻變關(guān)系MR3
其中,x1、x2分別表示目標(biāo)變量(單次轉(zhuǎn)賬金額),對應(yīng)的P(x1)、P(x2)分別表示目標(biāo)變量的兩次不同的實(shí)際取值(轉(zhuǎn)賬操作后發(fā)送方賬戶的余額)。
根據(jù)智能合約中的定義,確定了構(gòu)建蛻變關(guān)系涉及的4 個(gè)參數(shù):發(fā)送方賬戶A、接收方賬戶的個(gè)數(shù)B、每次給接收方賬戶轉(zhuǎn)賬的金額M、發(fā)送方賬戶余額在每一次測試前后的差值S。將MR3的形式化公式分解為表格形式,如表4 所示。
表4 MR3 中目標(biāo)變量間的蛻變關(guān)系
在該蛻變關(guān)系中,為了檢測后續(xù)測試用例是否會(huì)觸發(fā)減法溢出,將給每個(gè)接收方賬戶轉(zhuǎn)賬的金額作為變量,通過增加該變量逐步增加發(fā)送方轉(zhuǎn)賬的總金額,當(dāng)發(fā)送方賬戶轉(zhuǎn)賬金額的總量大于發(fā)送方本身的余額時(shí),如果觸發(fā)減法溢出,發(fā)送方在轉(zhuǎn)賬操作后的余額將會(huì)大于轉(zhuǎn)賬操作前的余額。
根據(jù)合約功能的設(shè)計(jì),若在合約功能中出現(xiàn)乘法運(yùn)算,則可能伴隨著加減法超過整型定義范圍的上下限導(dǎo)致乘法溢出。圖6 中智能合約中轉(zhuǎn)賬功能的設(shè)計(jì)就伴隨著乘法溢出。
在圖6 所示的合約中,發(fā)送方賬戶每次可以轉(zhuǎn)給不止一個(gè)接收方賬戶等量的金額,那么當(dāng)接收方賬戶數(shù)量遞增時(shí),發(fā)送方每次轉(zhuǎn)賬的總金額也會(huì)根據(jù)乘數(shù)數(shù)量的遞增而增加,從而發(fā)生溢出。
根據(jù)此功能設(shè)計(jì),當(dāng)目標(biāo)變量x為單次發(fā)起轉(zhuǎn)賬交易的目標(biāo)賬戶數(shù)時(shí),f(x)為轉(zhuǎn)賬操作后發(fā)送方賬戶的余額,得出蛻變關(guān)系設(shè)計(jì)依據(jù)的必要屬性為
在沒有觸發(fā)漏洞的前提下,每次在確定的輸入域中根據(jù)蛻變關(guān)系改變目標(biāo)變量的輸入,相應(yīng)的輸出就必然遵循該必要屬性?;诖?,可以設(shè)計(jì)乘法溢出的蛻變關(guān)系MR4
其中,x1、x2表示目標(biāo)變量(接收方賬戶的數(shù)量),對應(yīng)的P(x1)、P(x2)分別表示目標(biāo)變量的兩次不同的實(shí)際取值(轉(zhuǎn)賬操作后發(fā)送方賬戶的余額)。
將MR4的形式化公式分解為表格形式,如表5所示。
表5 MR4 中目標(biāo)變量間的蛻變關(guān)系
在該蛻變關(guān)系中,為了檢測后續(xù)測試用例是否會(huì)觸發(fā)溢出,將接收方賬戶的個(gè)數(shù)作為變量,通過接收方賬戶數(shù)量自增的方式逐步增加發(fā)送方轉(zhuǎn)賬的總金額,當(dāng)發(fā)送方賬戶轉(zhuǎn)賬金額的總量大于發(fā)送方本身的余額時(shí),如果觸發(fā)溢出,發(fā)送方在轉(zhuǎn)賬操作后的余額將會(huì)大于轉(zhuǎn)賬操作前的余額,變?yōu)橐粋€(gè)極大值。
根據(jù)重入攻擊漏洞的原理,將針對存在重入攻擊漏洞的智能合約進(jìn)行初步分析,將源測試用例和生成的后續(xù)測試用例認(rèn)為是滿足觸發(fā)重入攻擊漏洞的必要關(guān)系。和整型溢出漏洞不同的是,觸發(fā)重入攻擊漏洞需要通過重入攻擊操作而不是改變相關(guān)參數(shù),因此測試用例需要在涉及相應(yīng)的攻擊合約的基礎(chǔ)上生成。圖7 是一個(gè)存在重入攻擊漏洞的智能合約和攻擊者在觸發(fā)重入攻擊漏洞時(shí)調(diào)用的攻擊合約的部分內(nèi)容。
圖7 存在重入攻擊漏洞的智能合約和攻擊者在觸發(fā)重入攻擊漏洞時(shí)調(diào)用的攻擊合約的部分內(nèi)容
存在重入攻擊漏洞的代碼在6 行和7 行,根據(jù)1.3 節(jié)中的描述,向合約發(fā)送call 消息存在重入攻擊的風(fēng)險(xiǎn),攻擊者會(huì)在7 行更新用戶余額之前遞歸地從合約中取款。在分析重入攻擊漏洞的原理后,針對這兩行存在重入攻擊漏洞的代碼,設(shè)計(jì)一個(gè)重入攻擊的智能合約,在后續(xù)的實(shí)驗(yàn)中可以通過攻擊操作作為測試用例,檢測智能合約是否存在重入攻擊漏洞的風(fēng)險(xiǎn)。圖8 是一個(gè)發(fā)起重入攻擊的合約的部分內(nèi)容。
圖8 發(fā)起重入攻擊的合約的部分內(nèi)容
此案例是智能合約重入攻擊漏洞的實(shí)例。根據(jù)分析,在合約需要接收ether 時(shí),fallback 函數(shù)必須聲明為payable,因此第4 行函數(shù)為fallback 函數(shù)??梢钥闯觯琭allback 函數(shù)內(nèi)再次調(diào)用了withdraw 方法,使被攻擊合約再次進(jìn)行新一輪的轉(zhuǎn)賬操作,實(shí)現(xiàn)重入攻擊。
重入攻擊的次數(shù)和攻擊合約的gas 數(shù)量由被攻擊合約的具體余額決定,因此可能存在假陰性的問題(合約存在重入攻擊漏洞,但是gas 數(shù)量和余額的條件不滿足二次攻擊的實(shí)現(xiàn),會(huì)誤以為被測合約不存在重入攻擊漏洞)。
根據(jù)此功能設(shè)計(jì),當(dāng)目標(biāo)變量x為fallback 函數(shù)每次使用withdraw 取出的金額時(shí),f(x)為使用攻擊合約調(diào)用該智能合約的地址余額,得出蛻變關(guān)系設(shè)計(jì)依據(jù)的必要屬性為
在沒有觸發(fā)漏洞的前提下,每次在確定的輸入域中根據(jù)蛻變關(guān)系改變目標(biāo)變量的輸入,相應(yīng)的輸出就必然遵循該必要屬性。為了在測試的基礎(chǔ)上減小假陰性出現(xiàn)的概率,針對蛻變關(guān)系進(jìn)行了優(yōu)化,設(shè)計(jì)蛻變關(guān)系MR5
其中,x1、x2表示目標(biāo)變量(fallback 函數(shù)中每次使用withdraw 取出的金額),對應(yīng)的P(x1)、P(x2)分別表示使用攻擊合約模擬重入攻擊時(shí)合約地址的余額數(shù)量。
根據(jù)智能合約中的定義,確定了構(gòu)建蛻變關(guān)系涉及的4 個(gè)輸入變量和一個(gè)輸出變量:攻擊者的錢包地址A、攻擊者發(fā)動(dòng)攻擊使用的合約地址B、被攻擊合約的地址P、fallback 函數(shù)中每次攻擊取出的金額M和攻擊合約中的余額S。因此,將MR5的形式化公式分解為表格形式,如表6 所示。
表6 MR5 中目標(biāo)變量間的蛻變關(guān)系
在該蛻變關(guān)系中,為了準(zhǔn)確檢測是否會(huì)觸發(fā)合約中可能存在的重入攻擊漏洞,將測試用例設(shè)置為調(diào)用攻擊合約發(fā)動(dòng)的重入攻擊,在源測試用例測試階段就有可能觸發(fā)重入攻擊漏洞。為了避免假陰性的情況,源測試用例中的目標(biāo)變量將在合約規(guī)定的范圍內(nèi)隨機(jī)生成,如果滿足蛻變關(guān)系,則說明重入攻擊沒有觸發(fā)。后續(xù)測試用例將會(huì)在源測試用例的基礎(chǔ)上自減并進(jìn)行下一輪的重入測試。直到源測試用例自減到一個(gè)設(shè)置的極小值,如果輸出的結(jié)果仍然滿足輸出關(guān)系,則證明被測合約不存在重入攻擊漏洞。
通過后續(xù)實(shí)驗(yàn)發(fā)現(xiàn)蛻變關(guān)系MR5存在局限性:如果源測試用例和后續(xù)測試用例均能成功地觸發(fā)重入攻擊漏洞,那么仍然無法驗(yàn)證該合約是否存在重入攻擊漏洞。因此,對蛻變關(guān)系進(jìn)行進(jìn)一步的改進(jìn)。
根據(jù)此功能設(shè)計(jì),當(dāng)目標(biāo)變量x為fallback 函數(shù)每次使用withdraw 取出的金額時(shí),f(x)為用戶直接調(diào)用智能合約進(jìn)行取款操作后用戶地址的余額差值,g(x)為用戶通過調(diào)用攻擊合約對該智能合約進(jìn)行取款操作后用戶地址的余額差值,得出蛻變關(guān)系設(shè)計(jì)依據(jù)的必要屬性為
在沒有觸發(fā)漏洞的前提下,每次在確定的輸入域中根據(jù)蛻變關(guān)系改變目標(biāo)變量的輸入,相應(yīng)的輸出就必然遵循該必要屬性。基于此,得到蛻變關(guān)系MR6
其中,x1、x2表示目標(biāo)變量(正常取款操作和調(diào)用攻擊合約的操作取出的金額),對應(yīng)的P(x1)、P(x2)分別表示2 種操作后取款用戶錢包中的余額差值。
為了充分避免假陰性的情況出現(xiàn),將攻擊合約中fallback 函數(shù)中的withdraw 函數(shù)每次遞歸取款的金額設(shè)置為1。
根據(jù)智能合約中的定義,確定了蛻變關(guān)系涉及的4 個(gè)輸入變量和一個(gè)輸出變量:正常取款操作A、調(diào)用的攻擊合約觸發(fā)重入攻擊的操作B、被攻擊合約的地址P、2 種操作取款的金額M和不同操作執(zhí)行后取款用戶的錢包余額差值S。因此,將MR6的形式化公式分解為表格形式,如表7 所示。
表7 MR6 中目標(biāo)變量間的蛻變關(guān)系
在該蛻變關(guān)系中,將目標(biāo)變量轉(zhuǎn)化成2 種不同的操作,分別是正常取款和調(diào)用攻擊合約取款的操作,通過對比不同操作執(zhí)行的結(jié)果是否符合輸出蛻變關(guān)系,判斷合約是否存在重入攻擊漏洞。
為驗(yàn)證本文提出的蛻變測試方法在檢測智能合約中存在相應(yīng)漏洞的有效性,設(shè)計(jì)了多個(gè)存在相關(guān)漏洞的實(shí)例進(jìn)行驗(yàn)證。本節(jié)展示了實(shí)驗(yàn)的細(xì)節(jié),主要包括實(shí)驗(yàn)設(shè)置、實(shí)驗(yàn)流程、實(shí)驗(yàn)數(shù)據(jù)和實(shí)驗(yàn)結(jié)果與分析。
存在不同漏洞的智能合約在進(jìn)行蛻變測試時(shí)需要相應(yīng)的蛻變關(guān)系進(jìn)行測試,利用國家安全漏洞庫CNVD、CVE 和GitHub 中收錄的漏洞實(shí)例及其他文獻(xiàn)的數(shù)據(jù)集中收集的漏洞合約實(shí)例進(jìn)行實(shí)驗(yàn),收集的每個(gè)實(shí)例都有完整的源代碼。使用相應(yīng)的蛻變關(guān)系進(jìn)行蛻變測試,驗(yàn)證蛻變測試的有效性。
本文具體方案設(shè)置如下:在Remix IDE 上編譯和部署待測試的智能合約,版本號為0.4.26+commit.4563c3fc,EVM 鏡像選擇default,調(diào)用合約的用戶地址為 0x5B38Da6a701c568545dC fcB03FcB875f56beddC4,測試重入攻擊漏洞時(shí)調(diào)用攻擊合約的用戶地址為0xAb8483F64d9C6d1EcF9b849 Ae677dD3315835cb2。蛻變測試實(shí)驗(yàn)中待選的蛻變關(guān)系如表8 所示。
表8 蛻變測試實(shí)驗(yàn)中待選的蛻變關(guān)系
第4 節(jié)已經(jīng)完成了針對蛻變關(guān)系設(shè)計(jì)的工作。針對區(qū)塊鏈智能合約安全漏洞的蛻變測試的整體流程介紹如下。
1) 分析智能合約的具體功能和調(diào)用方法涉及的參數(shù)變量,確定測試用例生成的目標(biāo)變量與輸入域,并在輸入域中生成源測試用例集E。
2) 根據(jù)目標(biāo)變量中存在的函數(shù)關(guān)系確定其中的必要屬性,根據(jù)必要屬性設(shè)計(jì)相應(yīng)的蛻變關(guān)系,包括輸入蛻變關(guān)系和輸出蛻變關(guān)系。
3) 使用設(shè)計(jì)的蛻變關(guān)系,基于源測試用例集生成相應(yīng)的后續(xù)測試用例集E’。
4) 將源測試用例e和與之對應(yīng)的后續(xù)測試用例e’在待測智能合約中運(yùn)行,獲取相應(yīng)的輸出結(jié)果。
5) 驗(yàn)證源測試用例e和對應(yīng)的后續(xù)測試用例e’的輸出結(jié)果是否符合輸出蛻變關(guān)系,如果不符合,則證明測試結(jié)果不通過。
智能合約的蛻變測試總體流程如圖9 所示。
被測合約均來源于近年來區(qū)塊鏈的安全事故實(shí)例以及一些經(jīng)典案例,其中大量的智能合約都存在的漏洞類型包含整型(加法、減法及乘法)溢出漏洞和重入攻擊漏洞。實(shí)驗(yàn)驗(yàn)證的合約實(shí)例及存在的漏洞如表9 所示。
表9 實(shí)驗(yàn)驗(yàn)證的合約實(shí)例與存在的漏洞
通過對智能合約的具體功能和相應(yīng)漏洞的分析,待測的智能合約使用的蛻變關(guān)系將在第4 節(jié)中構(gòu)建的6 個(gè)蛻變關(guān)系中選取,蛻變測試流程中所有的源測試用例生成策略采取蛻變測試常用的隨機(jī)測試用例生成技術(shù)[26],即它們都來源于目標(biāo)變量輸入域內(nèi)的隨機(jī)值,在此基礎(chǔ)上獲得的源測試用例和選取的蛻變關(guān)系進(jìn)一步獲得后續(xù)測試用例。根據(jù)表9所示的合約實(shí)例,使用蛻變測試驗(yàn)證在區(qū)塊鏈智能合約上的蛻變測試有效性,并且和常用的智能合約檢測方法Slither 的檢測結(jié)果進(jìn)行對比。
為了評估基于蛻變測試的區(qū)塊鏈智能合約安全漏洞檢測方法的有效性,針對以下2 個(gè)問題對實(shí)驗(yàn)結(jié)果進(jìn)行分析:MT 是否可以檢測到MR 對應(yīng)的智能合約安全漏洞?MT 是否比其他智能合約測試方法更好?
1) MT 是否可以檢測到MR 對應(yīng)的智能合約安全漏洞?
為了驗(yàn)證這個(gè)問題,根據(jù)表8 提供的蛻變關(guān)系,使用圖9 提出的蛻變測試流程對表9 中的智能合約實(shí)例進(jìn)行了完整的蛻變測試,結(jié)果如表10 所示,實(shí)驗(yàn)按照智能合約編號順序依次進(jìn)行檢測。其中,“√”表示檢測出相應(yīng)的漏洞,“×”表示未檢測出相應(yīng)的漏洞。
表10 蛻變測試方法的檢測結(jié)果
根據(jù)表10 的實(shí)驗(yàn)結(jié)果可以看出,針對區(qū)塊鏈智能合約的蛻變測試方法的有效性得到了驗(yàn)證,蛻變關(guān)系使后續(xù)測試用例的生成具有較強(qiáng)的針對性。顯然,蛻變測試開展的基礎(chǔ)就是蛻變關(guān)系的正確性,即需要事先確定蛻變關(guān)系一定符合程序的必要關(guān)系。如果測試輸出不滿足蛻變關(guān)系,則相應(yīng)的測試用例中一定存在失效,即程序一定存在故障?;谶@個(gè)原則,參考沒有通過蛻變關(guān)系驗(yàn)證的輸出結(jié)果(失敗的測試用例),得出相應(yīng)的智能合約存在漏洞的結(jié)果。
如果測試輸出滿足蛻變關(guān)系,則并不意味著相應(yīng)的測試用例執(zhí)行結(jié)果一定是正確的。由于現(xiàn)有的源測試用例的生成策略具有隨機(jī)性,定量的測試用例可能導(dǎo)致測試結(jié)果存在假陰性的概率,使編號2和編號4 的智能合約實(shí)例中存在的漏洞未能檢測出。因此有限類型和數(shù)量的蛻變關(guān)系對不同智能合約檢測仍然存在局限性。
綜上,蛻變測試在區(qū)塊鏈智能合約中的應(yīng)用是可行的,并且具有有效性。
2) MT 是否比其他智能合約測試方法更好?
為了驗(yàn)證這個(gè)問題,使用常用的智能合約檢測方法Slither 對表9 中的智能合約實(shí)例進(jìn)行測試。Slither 方法的結(jié)果針對合約中的方法設(shè)置相應(yīng)指標(biāo)和潛在漏洞的置信度,具體的潛在漏洞以置信度評級的方式顯示,在此將置信度不高的測試指標(biāo)結(jié)果認(rèn)定為Slither 檢出的漏洞。根據(jù)Slither 的檢測結(jié)果和蛻變測試的檢測結(jié)果進(jìn)行對比,實(shí)驗(yàn)結(jié)果如表11和圖10 所示。其中,“?”表示未能檢測出任何漏洞。
圖10 兩種檢測方法的檢測結(jié)果
表11 Slither 測試方法的測試結(jié)果
從表11 和圖10 可以看出,蛻變測試在檢測整型溢出漏洞方面明顯優(yōu)于Slither 方法。在智能合約中,整型溢出漏洞是最常見的智能合約安全漏洞,但是在檢測整型溢出漏洞的各種方法中,蛻變測試可以通過觸發(fā)漏洞的形式以檢測是否存在具體的漏洞,在準(zhǔn)確率上占有明顯優(yōu)勢。
相比于Slither 方法,蛻變測試有較大優(yōu)勢,并且隨著蛻變關(guān)系進(jìn)一步的完善,這種優(yōu)勢將擴(kuò)散到更多的漏洞類型和更廣泛的智能合約應(yīng)用場景中。
根據(jù)文獻(xiàn)[27]可知,Slither 作為一款常用且精確度較高的測試工具,由于自身局限性,截止到該文獻(xiàn)發(fā)表日期,它還無法檢測與整型溢出相關(guān)的智能合約漏洞。之所以選擇Slither作為對比方法,是因?yàn)镾lither工具在提高測試者對合約的了解、協(xié)助代碼審查方面具有優(yōu)秀的表現(xiàn)。在未來的工作中,將參考Slither的相關(guān)功能嘗試為區(qū)塊鏈智能合約的蛻變測試方法增加預(yù)處理機(jī)制,這樣可以為測試時(shí)蛻變關(guān)系的選擇以及蛻變關(guān)系的設(shè)計(jì)階段提供更客觀全面的依據(jù)。
此外,蛻變關(guān)系作為蛻變測試中重要的元素組成部分,蛻變關(guān)系的識別是影響測試結(jié)果的重要因素。蛻變測試在整型溢出漏洞和重入攻擊漏洞的檢測中最明顯的優(yōu)勢在于通過蛻變關(guān)系優(yōu)化了動(dòng)態(tài)測試面臨的測試預(yù)言難以獲取導(dǎo)致影響測試準(zhǔn)確率的問題,由于這類漏洞的目標(biāo)變量較固定,在面臨不同功能的智能合約測試都有較好的泛化性,在動(dòng)態(tài)測試的過程中通過觸發(fā)漏洞從而進(jìn)行檢測也更加實(shí)用。因此蛻變測試在針對這兩類漏洞的測試中有著良好的表現(xiàn)。本文根據(jù)已知漏洞的觸發(fā)原理設(shè)計(jì)蛻變關(guān)系,但此類方法存在部分局限性,主要在于智能合約的常見漏洞并不局限于整型溢出漏洞和重入攻擊漏洞,還有時(shí)間戳依賴漏洞和交易順序依賴漏洞等,這些漏洞的觸發(fā)條件也與區(qū)塊鏈網(wǎng)絡(luò)層的安全熟悉相關(guān),因此暫時(shí)還無法僅通過合約層的漏洞觸發(fā)原理構(gòu)建蛻變關(guān)系,隨著區(qū)塊鏈智能合約的發(fā)展,漏洞類型也將進(jìn)一步增加,筆者將基于此方法分析更多類型的漏洞并設(shè)計(jì)蛻變關(guān)系,使蛻變測試的適用范圍更廣。此外,對于未知漏洞,筆者仍然需要從輸入域和輸出域的關(guān)系入手,識別科學(xué)的蛻變關(guān)系。與蛻變測試在其他的場景中應(yīng)用類似,此方法最重要的內(nèi)容是蛻變關(guān)系的識別,因此該方法的有效性在很大程度上取決于蛻變關(guān)系的選擇,這是此類方法應(yīng)用到區(qū)塊鏈智能合約領(lǐng)域中依然存在的挑戰(zhàn)。在后續(xù)的工作中,尋找有效的蛻變關(guān)系識別方法[28]并應(yīng)用到區(qū)塊鏈智能合約領(lǐng)域?qū)⒆鳛楣P者改進(jìn)此方法的一部分。
在區(qū)塊鏈智能合約方面,本文提出了一種基于蛻變測試的智能合約安全漏洞檢測方法,通過對實(shí)例合約的測試,檢測智能合約中存在的整型溢出和重入攻擊漏洞??紤]到區(qū)塊鏈的不可篡改性和gas的消耗問題,蛻變測試具有明顯的優(yōu)勢。一方面,蛻變測試避免了獲取測試預(yù)言可能存在的大量資源消耗和獲取困難的情況,緩解了動(dòng)態(tài)測試中的Oracle 問題。另一方面,對具體的智能合約漏洞設(shè)計(jì)了相應(yīng)的蛻變關(guān)系,使蛻變測試中測試用例的生成具有更強(qiáng)的針對性,提升了測試過程中觸發(fā)與檢測相應(yīng)漏洞的概率,降低了假陽性的概率。
未來的工作主要有:1) 為了覆蓋更廣泛的智能合約安全漏洞類型,將進(jìn)一步設(shè)計(jì)更多的蛻變關(guān)系,在現(xiàn)有的智能合約功能和已發(fā)現(xiàn)的漏洞的基礎(chǔ)上,完善關(guān)于智能合約的蛻變關(guān)系庫;2) 針對整體測試框架中智能合約分析的部分,將結(jié)合Slither 智能合約分析工具,對蛻變關(guān)系的設(shè)計(jì)和選擇提供更合理的指標(biāo)支撐并提高蛻變測試的自動(dòng)化程度。