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