2008年,中本聰提出了一種完全通過點對點技術(shù)實現(xiàn)的電子現(xiàn)金系統(tǒng)(比特幣)。該方案的核心價值在于其提出了基于工作量證明的解決方案,使現(xiàn)金系統(tǒng)在點對點環(huán)境下運行,并能夠防止雙花攻擊。如今比特幣已經(jīng)誕生十年,無數(shù)種數(shù)字貨幣相應誕生,但人們對雙花攻擊的討論似乎仍然停留在比特幣51%攻擊上。實際上,我們的研究發(fā)現(xiàn),實用的數(shù)字貨幣雙花攻擊還有很多種其他形式。在本文中,我們通過介紹我們發(fā)現(xiàn)的針對EOS、NEO等大公鏈平臺的多個雙花攻擊漏洞,總結(jié)出多種造成數(shù)字貨幣雙花攻擊的多種原因,并提出一種高效的減緩措施。
1. 工作量證明和雙花攻擊
2008年,中本聰提出了一種完全通過點對點技術(shù)實現(xiàn)的電子現(xiàn)金系統(tǒng),它使得在線支付能夠直接由一方發(fā)起并支付給另外一方,中間不需要通過任何的金融機構(gòu)。雖然數(shù)字簽名部分解決了這個問題,但是如果仍然需要第三方的支持才能防止雙重支付的話,那么這種系統(tǒng)也就失去了存在的價值。比特幣的工作量證明機制(PoW)的本質(zhì),就是要使現(xiàn)金系統(tǒng)在點對點的環(huán)境下運行,并防止雙花攻擊。
工作量證明機制的原理如下:網(wǎng)絡中每一個區(qū)塊都包含當前網(wǎng)絡中的交易和上一個區(qū)塊的區(qū)塊頭哈希。新區(qū)塊產(chǎn)生,其區(qū)塊頭哈希必須滿足工作量證明條件(需要進行大量的哈希計算)。整個網(wǎng)絡將滿足工作量證明的哈希鏈連接起來,從而形成區(qū)塊鏈。除非攻擊者重新完成全部的工作量證明,否則形成的交易記錄將不可更改。最長的區(qū)塊鏈不僅將作為被觀察到的交易序列的證明,而且被看做是來自算力最大的群體的共識。只要整個網(wǎng)絡中大多數(shù)算力都沒有打算合作起來對全網(wǎng)進行攻擊,那么誠實的節(jié)點將會生成最長的、超過攻擊者的鏈條,從而實現(xiàn)對雙花攻擊的抵抗。
雙花攻擊實際上是一個結(jié)果。如果一個攻擊者A將同一個比特幣同時支付給B和C兩個用戶,并且B和C兩個用戶都認可了這筆交易。那么我們說A將該比特幣花了兩次,A實現(xiàn)了一次雙花攻擊。針對工作量證明機制的雙花攻擊中,51%攻擊是被討論的最多的一種攻擊形式。但針對工作量證明機制的雙花攻擊實際上有多種形式,包括芬妮攻擊、競爭攻擊、Vector76攻擊等。這些攻擊實際上也得到了充分的關(guān)注和討論,本文中不做贅述。實際上,實用的數(shù)字貨幣雙花攻擊還有很多種其他形式。下文中,我們將通過多個我們發(fā)現(xiàn)的多個安全漏洞,討論多種數(shù)字貨幣雙花攻擊的多種原因,并提出一種高效減的緩措施。
2. 雙花攻擊的新分類
智能合約平臺,本質(zhì)上是要在全網(wǎng)共享一個賬本。這可以看成是一個分布式狀態(tài)機復制問題。當前的賬本狀態(tài),我們可以認為是State_n。當一個新交易Tx_產(chǎn)生的時候,Tx_將對State_n產(chǎn)生一個作用。從而使State_n狀態(tài)過渡到State_狀態(tài)。這個過程我們可以用公式表示:State_n × Tx_{n+1} → State_{n+1}
智能合約平臺共識機制,本質(zhì)上是將所有的交易【Tx_1 Tx_2 ……。 Tx_n】按順序作用到初始State_0上,使全網(wǎng)始終保持相同的狀態(tài)。區(qū)塊鏈中的每一個區(qū)塊,實際上將交易序列【Tx_1 Tx_2 ……。 Tx_n】按順序拆分成不同的區(qū)塊 Block1,Block2并按順序鏈接起來。在全網(wǎng)狀態(tài)機復制的過程中,如果一旦因為某些原因產(chǎn)生了全網(wǎng)狀態(tài)不一致,則我們可以認為全網(wǎng)產(chǎn)生了一個分叉。分叉被攻擊者利用,可進一步實現(xiàn)雙花攻擊。
本文中,我們將我們發(fā)現(xiàn)的這些雙花攻擊漏洞分成3類:
· 驗證不嚴格造成的雙花攻擊。
· 狀態(tài)機State_n × Tx_{n+1}→State_{n+1}不一致執(zhí)行造成的雙花攻擊。
· 共識機制造成的雙花攻擊。
驗證不嚴格造成的雙花攻擊,主要原因在于具體實現(xiàn)邏輯校驗問題。比特幣的漏洞CVE-2018-17144實際上就是這樣一個漏洞。
狀態(tài)機不一致執(zhí)行造成的雙花攻擊,主要是由于智能合約虛擬機因為各種原因?qū)е轮苯咏Y(jié)果不一致,從而在整個網(wǎng)絡中創(chuàng)造分叉,造成雙花攻擊。
共識機制漏洞可能產(chǎn)生整個網(wǎng)絡的分叉,從而進一步造成雙花攻擊。人們常說的51%攻擊,實際上就是PoW共識機制的分叉漏洞。
3. 驗證不嚴格造成的雙花攻擊
驗證不嚴格造成的雙花攻擊,主要原因在于具體實現(xiàn)邏輯校驗問題。這里我們介紹兩個關(guān)于區(qū)塊與交易綁定時校驗不嚴格,從而產(chǎn)生雙花攻擊的漏洞。
在區(qū)塊鏈項目中,一筆交易Tx_1被打包的某個區(qū)塊Block_1中的方式如下:首先計算交易Tx_1的哈希值Hash_1,然后用Hash_1與其他交易的哈希值Hash_2…Hash_n組合構(gòu)成Merkle Hash Tree。計算出哈希樹的根節(jié)點root,然后將root打包到Block_1中。這樣即形成一筆交易與區(qū)塊的綁定。一般來講,除非攻擊者能夠攻破哈希函數(shù)的抗碰撞性,否則無法打破一筆交易與區(qū)塊的綁定。如果攻擊者能夠打包交易與區(qū)塊的綁定,則攻擊者能通過造成全網(wǎng)的分叉從而實現(xiàn)雙花攻擊。下面我們介紹兩個我們在NEO上發(fā)現(xiàn)的雙花攻擊漏洞:
3.1 NEO虛擬機GetInvocationScript雙花攻擊漏洞:
區(qū)塊鏈項目中,一個交易一般是由未簽名的部分(UnsignedTx,交易要執(zhí)行的內(nèi)容)和簽名的部分(交易的witness)構(gòu)成的。在如比特幣之類的區(qū)塊鏈項目中,交易的hash計算實際上是包含了該交易的簽名部分的。而在如NEO、ONT等多種區(qū)塊鏈平臺中,交易的計算公式為hash=SHA256(UnsignedTx)。即交易的哈希是由未簽名的部分計算的來的,與交易的witness無關(guān)。而NEO智能合約在執(zhí)行的時候,能夠通過Transaction_GetWitnesses方法,從一個交易中獲得該交易的witnesses。其具體實現(xiàn)如下:
某個合約交易獲得自己的witness之后,還能夠通過Witness_GetVerificationScript方法獲得該witness中的驗證腳本。如果攻擊者針對同一個未簽名交易UnsignedTx1,可以構(gòu)造兩個不同的驗證腳本。則可以造成該合約執(zhí)行的不一致性。正常情況下,合約的VerificationScript是由合約的輸入等信息決定的,攻擊者無法構(gòu)造不同的驗證腳本并通過驗證。但是我們發(fā)現(xiàn)在VerifyWitness方法中,當VerificationScript.length=0的時候,系統(tǒng)會調(diào)用EmitAppCall來執(zhí)行目標腳本hash。
所以當VerificationScript=0,或者VerificationScript等于目標腳本的時候,均可滿足witness驗證條件。即攻擊者可以對于同一個未簽名的交易UnsignedTx_1,構(gòu)造兩個不同的VerificationScript。攻擊者利用這個性質(zhì),可以對NEO智能合約上的所有代幣資產(chǎn)進行雙花攻擊,其具體攻擊場景如下:
步驟1:攻擊者構(gòu)造智能合約交易Tx_1(未簽名內(nèi)容UnsignedTx_1,驗證腳本為VerficationScript_1)。在UnsignedTx_1的合約執(zhí)行中,合約判斷自己的VerficationScript是否為VerficationScript_1。如果為VerficationScript_1,擇發(fā)送代幣給A用戶。如果VerficationScript為空,則發(fā)送代幣給B用戶。
步驟2:Tx_1被打包到區(qū)塊Block_1中。
步驟3: 攻擊者收到Block_1后,將Tx_1替換成Tx_2(Tx_1具有與Tx_1相同的未簽名內(nèi)容UnsignedTx_1,但驗證腳本為空)從而形成Block_2。攻擊者將Block_1發(fā)送給A用戶,將Block_2發(fā)送給B用戶。
步驟4:當A用戶收到Block_1時,發(fā)現(xiàn)自己收到攻擊者發(fā)送的代幣。當B用戶收到Block_2時,也會發(fā)現(xiàn)自己收到了攻擊者發(fā)送的代幣。雙花攻擊完成。
可見,該漏洞的利用門檻非常低,且可以對NEO智能合約上的所有代幣資產(chǎn)進行雙花攻擊。危害非常嚴重。
3.2 NEO MerlkeTree綁定繞過造成交易雙花攻擊漏洞:
智能合約交易與區(qū)塊的綁定,通常通過MerkleTree來完成。如果攻擊者能繞過該綁定,則能實現(xiàn)對任意交易的雙花。這里我們看看NEO的MerkleTree的實現(xiàn)如下:
在MerkleTreeNode函數(shù)中,NEO進行了MerkleTree葉節(jié)點到父節(jié)點的計算。但這里存在一個問題,當leaves.length為奇數(shù)n的時候。NEO的MerkleTree會將最后一個葉節(jié)點復制一次,加入到MerkleTree的計算中。也就是說當n為奇數(shù)時,以下兩組交易的MerkleRoot值會相等:
【Tx_1 Tx_2 …… Tx_n】
【Tx_1 Tx_2 …… Tx_n Tx_】其中 Tx_= Tx_n
利用這個特性,攻擊者可以實現(xiàn)對任意NEO資產(chǎn)的雙花攻擊。其具體攻擊場景如下:
步驟1:假設正常的一個合法Block_1,包含的交易列表為【Tx_1 Tx_2 … Tx_n】。攻擊者收到Block_1后,將交易列表替換為【Tx_1 Tx_2 … Tx_n Tx_】,形成 Block_2。然后將Block_2發(fā)布到網(wǎng)絡中去。
步驟2:一個普通節(jié)點收到Block_2后,會對Block_2的合法性進行校驗。然而因為【Tx_1 Tx_2 … Tx_n Tx_】與【Tx_1 Tx_2 … Tx_n】具有相同的MerkleRoot。所以Block_2能夠通過區(qū)塊合法性校驗,從而進如區(qū)塊持久化流程。NEO本地取消了普通節(jié)點對合法區(qū)塊中交易的驗證(信任幾個共識節(jié)點)。則Tx_n交易可以被普通節(jié)點執(zhí)行兩次,雙花攻擊執(zhí)行成功。
可見,該漏洞的利用門檻非常低,且可以對NEO上的所有資產(chǎn)進行雙花攻擊。危害非常嚴重。
4. 虛擬機不一致性執(zhí)行
智能合約平臺共識機制,本質(zhì)上是將所有的交易【Tx_1 Tx_2 ……。 Tx_n】按順序作用到初始State_0上,使全網(wǎng)始終保持相同的狀態(tài)。在狀態(tài)機復制過程中,我們要求State_n × Tx_èState_是決定性的。State_n × Tx_èState_實質(zhì)上就是智能合約虛擬機對Tx_的執(zhí)行過程,如果智能合約虛擬機中存在設計或者實現(xiàn)漏洞,導致虛擬機不一致性執(zhí)行(對相同的輸入State_n 和Tx_,輸出State_不一致)。則攻擊者可以利用該問題在網(wǎng)絡中產(chǎn)生分叉和并進行雙花攻擊。下面我們介紹多個EOS和NEO上我們發(fā)現(xiàn)的虛擬機不一致執(zhí)行漏洞和其產(chǎn)生原因。
4.1 EOS虛擬機內(nèi)存破壞RCE漏洞:
此前,我們公開了文章《EOS Node Remote Code Execution Vulnerability --- EOS WASM Contract Function Table Array Out of Bound》(http://blogs.#/post/eos-node-remote-code-execution-vulnerability.html)。在該文中,我們發(fā)現(xiàn)了一個EOS WASM虛擬機的一個內(nèi)存越界寫漏洞,針對該漏洞我們編寫的利用程序可以成功利用該漏洞使EOS虛擬機執(zhí)行任意指令,從而完全控制EOS所有出塊和驗證節(jié)點。
究其本質(zhì)而言,是在State_n × Tx_{n+1} → State_{n+1}過程中。攻擊者能讓EOS虛擬機完全脫離原本執(zhí)行路徑,執(zhí)行任意指令,自然可以完成雙花攻擊。其攻擊流程如下:
步驟1:攻擊者構(gòu)造能夠?qū)崿F(xiàn)RCE的惡意智能合約,并將該合約發(fā)布到EOS網(wǎng)絡中。
步驟2:EOS超級節(jié)點解析到該合約后,觸發(fā)漏洞,執(zhí)行攻擊者自定義的任意指令。
步驟3:攻擊者實現(xiàn)雙花攻擊。
該漏洞的危害非常嚴重,且是第一次智能合約平臺受到遠程代碼執(zhí)行攻擊事件。讀者可以閱讀該文章了解相關(guān)細節(jié),在此不再贅述。
4.2 EOS虛擬機內(nèi)存未初始化造成雙花攻擊:
我們在編寫《EOS Node Remote Code Execution Vulnerability --- EOS WASM Contract Function Table Array Out of Bound》的利用程序的過程中,還利用了EOS中當時的一個未公開的內(nèi)存未初始化漏洞。在內(nèi)存破壞攻擊中,內(nèi)存未初始化漏洞通常能夠造成信息泄露、類型混淆等進一步問題,從而輔助我們繞過如ASLR之類的現(xiàn)代二進制程序的緩解措施,進一步實現(xiàn)攻擊。然而在智能合約虛擬機中,內(nèi)存未初始化漏洞有更直接的利用方式,可以直接造成雙花攻擊。以下為我們在EOS RCE中利用的一個內(nèi)存未初始化漏洞的細節(jié),其可以被用來直接實現(xiàn)EOS智能合約代幣資產(chǎn)雙花攻擊。
當WASM虛擬機通過grow_memory偽代碼來申請內(nèi)存新的內(nèi)存。在EOS WASM grow_memory最初的實現(xiàn)中,未對申請到的內(nèi)存進行清零操作。該塊內(nèi)存的空間實際上是隨機的(依賴于合約執(zhí)行機器的內(nèi)存狀態(tài))。則攻擊者可以構(gòu)造惡意合約,實現(xiàn)對EOS上任意合約資產(chǎn)的雙花攻擊。其攻擊流程如下:
步驟1: 攻擊者構(gòu)造惡意智能合約。合約中通過grow_memory獲得一塊新的內(nèi)存地址。
步驟2:合約中讀取該地址中的某個bit內(nèi)容(此時該bit可能為0,也可能為1,依賴于合約執(zhí)行機器的狀態(tài))。
步驟3:合約判斷該bit的內(nèi)容,如果為1則發(fā)送代幣給A用戶,如果為0則發(fā)送代幣給B用戶,從而實現(xiàn)雙花攻擊。
4.3 EOS虛擬機內(nèi)存越界讀造成雙花攻擊:
在傳統(tǒng)的內(nèi)存破壞中,內(nèi)存越界讀漏洞主要將會導致信息泄露,從而輔助我們繞過如ASLR之類的現(xiàn)代二進制程序的緩解措施,進一步與其他漏洞一起實現(xiàn)攻擊。然而在智能合約虛擬機中,內(nèi)存越界讀漏洞有更直接的利用方式,可以直接造成雙花攻擊。下面為一個我們發(fā)現(xiàn)的EOS內(nèi)存越界讀漏洞,我們可以利用該漏洞實現(xiàn)雙花攻擊。
當EOS WASM將一個offset轉(zhuǎn)換內(nèi)WASM內(nèi)存地址時,其邊界檢查過程如下:
在這里|ptr|的類型實際上是一個I32類型,它可以是一個負數(shù)。那么當:-sizeof(T) 《 ptr 《 0
的時候,ptr+sizeof(T)是一個很小的數(shù)可以通過該邊界檢查。在之后的尋址中,我們看到代碼:T &base = (T)(getMemoryBaseAddress(mem)+ptr);
|base|的地址將會超過WASM的內(nèi)存基址,從而讓智能合約實現(xiàn)內(nèi)存越界讀(讀到的內(nèi)存地址內(nèi)容取決于虛擬機當前執(zhí)行狀態(tài),可被認為使隨機的)。攻擊者可以利用該漏洞實現(xiàn)雙花攻擊。其攻擊過程如下:
步驟1: 攻擊者構(gòu)造惡意智能合約。合約中利用內(nèi)存越界讀漏洞,讀取超越WASM內(nèi)存基址的某個bit)此時該bit可能為0,也可能為1,依賴于合約執(zhí)行機器的狀態(tài))。
步驟2:合約判斷該bit的內(nèi)容,如果為1則發(fā)送代幣給A用戶,如果為0則發(fā)送代幣給B用戶,從而實現(xiàn)雙花攻擊。
4.4 標準函數(shù)實現(xiàn)不一致造成雙花攻擊:
總結(jié)上面雙花攻擊兩個例子的本質(zhì),實際上是EOS合約在執(zhí)行過程中因為某些內(nèi)存漏洞原因讀取到了隨機變量,從而打破了原本虛擬機執(zhí)行的一致性,造成了雙花攻擊。事實上,合約執(zhí)行的不一致性,不一定完全依賴于隨機性。這里我們介紹一個因為各個平臺(版本)對標準C函數(shù)實現(xiàn)不一致造成的雙花攻擊。
在C語言標準定義中,memcmp函數(shù)的返回時被要求為:小于0,等于0,或者大于0。然而各種不同的C版本實現(xiàn)中,具體返回的可能不一樣(但依然符合C標準)。攻擊者可以利用該標準實現(xiàn)的不一致性,造成運行在不同系統(tǒng)上的EOS 虛擬機執(zhí)行結(jié)果不一致,進而實現(xiàn)雙花攻擊。其攻擊流程如下:
步驟1:攻擊者構(gòu)造惡意智能合約,在合約中調(diào)用memcmp函數(shù),并獲取返回值。
步驟2:此時,不同的平臺和版本實現(xiàn)Memcmp的返回值不一致(即使EOS虛擬機的二進制代碼是相同的)。惡意合約判斷Memcmp的返回值,決定轉(zhuǎn)賬給A或B,從而完成雙花。
該漏洞的具體修復如下:
EOS強制將memcmp的返回值轉(zhuǎn)換為0,-1或者1,從而抵抗這種不一致執(zhí)行。
Memcmp這個問題,是同一種語言對相同標準實現(xiàn)的不一致性造成的。事實上,同一個區(qū)塊鏈項目經(jīng)常會有多個不同版本語言的實現(xiàn)。不同語言對相同標準的實現(xiàn)通常也會有偏差,比如一個我們發(fā)現(xiàn)的因標準定義實現(xiàn)不一致造成不一致執(zhí)行是ECDSA函數(shù)。ECDSA簽名標準中要求私鑰x不為0。如python、JS中的多個密碼學庫中對該標準由嚴格執(zhí)行,但是我們發(fā)現(xiàn)部分golang的ECDSA庫允許私鑰x=0進行簽名和驗證計算,惡意攻擊者利用該問題可以對同一個區(qū)塊鏈平臺的不同版本實現(xiàn)(比如golang實現(xiàn)和python實現(xiàn))構(gòu)造不一致執(zhí)行惡意合約,從而進一步完成雙花攻擊。
4.5版本實現(xiàn)不一致造成雙花攻擊:
同一個區(qū)塊鏈項目經(jīng)常會有多個不同版本編程語言的實現(xiàn)。不同編程語言的實現(xiàn)同樣存在著各種這樣的不一致執(zhí)行的可能性。上面ECDSA是一個例子。大整數(shù)運算也是一個常見的例子。比如在曾經(jīng)的NEO的C#版本實現(xiàn)和python版本實現(xiàn)中,大整數(shù)(BigInteger)除法運算可導致不同編程語言實現(xiàn)版本見的不一致執(zhí)行現(xiàn)象,從而造成雙花攻擊。類似的現(xiàn)象在多個區(qū)塊鏈項目中產(chǎn)生過。
4.6其他問題不一致性問題
系統(tǒng)時間、隨機數(shù)、浮點數(shù)計算等因素也是可以造成虛擬機不一致執(zhí)行的原因。但是在我們的審計中,并沒有發(fā)現(xiàn)此類漏洞在大公鏈項目中出現(xiàn)。多數(shù)區(qū)塊鏈項目在設計之初就會考慮到這些明顯可能造成的問題。
但可能造成不一致執(zhí)行的因素可能遠遠超過我們上面發(fā)現(xiàn)的這些問題。事實上,一些主觀因素(取決于機器當前運行狀態(tài)的因素,我們稱之為主觀因素)都可能造成虛擬機的不一致執(zhí)行。舉個例子,比如在4G內(nèi)存,8G內(nèi)存的機器在執(zhí)行過程中產(chǎn)生內(nèi)存溢出(OOM)的主觀邊界就不一樣,攻擊者利用OOM可能造成虛擬機的不一致執(zhí)行。
5. 共識機制造成的雙花攻擊
共識機制造成的雙花攻擊實際上是在業(yè)界中獲得充分討論的一個問題,然而各種公鏈方案在共識機制實現(xiàn)上仍然可能存在分叉問題,從而造成雙花攻擊。
5.1 ONT vBFT VRF隨機數(shù)繞過漏洞
Long range attack 是目前所有PoS共識機制都面臨的一種分叉攻擊方法。攻擊者可以選擇不去分叉現(xiàn)有的鏈,而實回到某個很久之前的鏈狀態(tài)(攻擊者在這個狀態(tài)曾占有大量貨幣),造一跳更長的新鏈出來讓網(wǎng)絡誤以為是主鏈,從而完成雙花。目前業(yè)界針對Long range attack并沒有根本的解決辦法,只能保證在“Weak Subjectivity”不發(fā)生的情況下,防止分叉發(fā)生。
ONT的vBFT共識算法提出了一種依靠可驗證隨機函數(shù)(VRF)來防止惡意分叉擴展的方法。網(wǎng)絡首先基于VRF在共識網(wǎng)絡中依次選擇出一輪共識的備選區(qū)塊提案節(jié)點集,區(qū)塊驗證節(jié)點集和區(qū)塊確認節(jié)點集,然后由選出的節(jié)點集完成共識。由于每個區(qū)塊都是由VRF確定節(jié)點的優(yōu)先級順序的,對于惡意產(chǎn)生的分叉,攻擊者很難持續(xù)維持自己的高優(yōu)先級(如果攻擊者沒有控制絕大多數(shù)股權(quán)的話),因此惡意產(chǎn)生的分叉將很快消亡,從而使vBFT擁有快速的狀態(tài)終局性。
然而我們發(fā)現(xiàn)vBFT中的VRF實現(xiàn)存在一個漏洞,導致私鑰為0的用戶的可對任意區(qū)塊數(shù)據(jù)生成相同的vrfValue。具體的,vBFT中的vrf是對由波士頓大學提出的VRF標準草稿(https://hdl.handle.net/2144/29225)的一個實現(xiàn)。具體在該草案的5.1和5.2章節(jié)中,我們可以看到證明生成,和隨機數(shù)計算的算法。如圖:
漏洞在于x=0時候,此時從計算上 y仍然為一個合法的公鑰,且能通過vBFT實現(xiàn)中ValidatePublicKey的校驗。gamma為橢圓曲線上固定的點(無窮遠點)。即對任意輸入alpha,該vrf產(chǎn)生的值為固定一個值。完全沒有隨機性。該問題可導致攻擊者利用固定vrf破壞共識算法隨機性,從而長期控制節(jié)點選舉。
5.2 NEO dBFT共識分叉
NEO的dBFT共識機制,本質(zhì)上可以看成是一個POS+pBFT方案。在原版NEO代碼中,我們發(fā)現(xiàn)NEO和ONT在實現(xiàn)其dBFT共識機制的時候存在分叉問題。惡意的共識節(jié)點可以產(chǎn)生一個分叉塊,從而造成雙花的發(fā)生。具體細節(jié)可以參考我們之前的文章:《Analysis and Improvement of NEO’s dBFT Consensus Mechanism》(http://blogs.#/post/NEO_dBFT_en.html),在此我們不做贅述。
6. 一種針對虛擬機執(zhí)行不一致雙花問題的高效減緩措施
對于校驗繞過之類的邏輯漏洞和共識機制問題產(chǎn)生的雙花漏洞,還是需要深入到業(yè)務邏輯中具體問題具體分析。這里我們提出一種針對虛擬機執(zhí)行不一致的減緩措施。
一種簡單的解決虛擬機執(zhí)行不一致造成的雙花問題的方法是由出塊者將運行完交易后的全局狀態(tài)State_進行哈希散列,然后將該散列打包到區(qū)塊中。普通節(jié)點在收到區(qū)塊后,將本地運行完交易后的狀態(tài)State’_的哈希散列與State_的哈希散列進行對比。如果相等,則說明沒有分叉產(chǎn)生。然而由于本地數(shù)據(jù)是先行增長的,所以每次對全局狀態(tài)進行散列計算的開銷極大。針對這個問題,以太坊使用了MekleTree的結(jié)構(gòu)來提高性能,同時應對分叉回滾問題。但以太坊的方案并不適用于采用其他數(shù)據(jù)結(jié)構(gòu)存儲狀態(tài)信息的區(qū)塊鏈項目。這里我們提出一種新的解決方案,其工作流程如下:
1. 區(qū)塊生產(chǎn)者在區(qū)塊打包階段,將該區(qū)塊中所有的交易運行過程中的對數(shù)據(jù)庫的寫操作序列【write_db_1 write_db_2 …。 write_db_n】記錄下來,并計算該序列的哈希值write_db_hash。
2. 普通節(jié)點收到新的區(qū)塊后,對區(qū)塊進行校驗。然后在虛擬機中執(zhí)行交易。同時本地記錄這些交易對數(shù)據(jù)庫的寫操作序列【write_db_1’ write_db_2’ …。 write_db_n’】,然后計算write_db_hash’。判斷其與write_db_hash是否相等。如果相等,則認為沒有不一致執(zhí)行發(fā)生。如果不等,則拒絕對該寫操作序列進行commit。
本方法的核心思路在于,智能合約平臺虛擬機執(zhí)行不一致產(chǎn)生的原因在于:合約中各種功能函數(shù)和圖靈完備性的支持中,可能引入多種不確定因素,從而造成執(zhí)行不一致。各種各樣復雜的小原因,可能導致這種不一致執(zhí)行防不勝防。但是我們退一步看,雙花攻擊的本質(zhì)是要對全局狀態(tài)State_進行修改,本質(zhì)上就是一系列的簡單寫操作(簡單的寫操作往往并不會產(chǎn)生二義性)。要防止雙花,只需要對所有的寫操作序列進行匹配校驗便可。本地對這些寫操作進行匹配和記錄的開銷非常小,同時本地記錄這些寫操作序列,也方便應對分叉回滾等其他因素。
7. 后記
在本文中,我們通過介紹我們發(fā)現(xiàn)的針對EOS、NEO等大公鏈平臺的多個雙花攻擊漏洞的案例發(fā)現(xiàn),總結(jié)出多種造成數(shù)字貨幣雙花攻擊的多種原因,并提出了一種通用的安全減緩措施。從上面的分析中,我們可以看到,區(qū)塊鏈安全目前的形式仍然十分嚴峻。各種大公鏈項目實際上都產(chǎn)生過能夠產(chǎn)生雙花攻擊之類的嚴重安全問題。我們的職業(yè)道德經(jīng)受住了無數(shù)次的考驗。 make a billion or work hard? Of course, work hard. 不過幸運的是,在幾個月的區(qū)塊鏈安全研究中,我們收到了來自各個項目方價值超過30萬美金的數(shù)字貨幣漏洞報告獎勵,感謝。
本文中所有提到的漏洞均已被修復。在漏洞報告和解決的過程中我們發(fā)現(xiàn)EOS與NEO項目方對于安全問題處理專業(yè)高效,反應及時。項目安全性也一步一步得到完善。我們會繼續(xù)關(guān)注和研究區(qū)塊鏈相關(guān)技術(shù)的安全問題,推動區(qū)塊鏈技術(shù)向前發(fā)展。
評論
查看更多