摘要:很多以太坊的智能合約控制著有實際價值的數(shù)字資產(chǎn)。這幾期為大家?guī)硪黄陮σ蕴缓霞s攻擊調(diào)研的文獻,來幫助大家避免以太坊智能合約設(shè)計中的一些可能導致安全性問題的弱點。攻擊攻擊是以太坊歷史上最著名的攻擊,盜走了價值萬美元的以太幣。
很多以太坊的智能合約控制著有實際價值的數(shù)字資產(chǎn)。因此,保證合約沒有安全漏洞是十分重要的事情。這幾期為大家?guī)硪黄?2017 年對以太坊合約攻擊調(diào)研的文獻,來幫助大家避免以太坊智能合約設(shè)計中的一些可能導致安全性問題的弱點。在這里,你也可以看到,導致以太坊分叉的著名事件 The DAO 攻擊,其原理是什么。
這篇文獻分兩部分,第一部分介紹了一些如果對 Solidity 語言和智能合約不當使用會導致問題的弱點;第二部分則用一些實例展示了這些弱點可能會導致怎樣的問題。
我們今天還推送了另一篇文章作為背景資料,為不熟悉智能合約和 Solidity 語言的讀者介紹一些背景內(nèi)容。
不當使用會導致問題的點
合約內(nèi)函數(shù)調(diào)用
在使用 Solidity 編寫智能合約時,可以調(diào)用其他合約中的函數(shù)。假設(shè) Alice 合約里有一個 ping(uint) 函數(shù), c 是一個 Alice 合約在以太坊上的地址。如果其他合約(或 Alice 合約自身)想要以參數(shù) 42 調(diào)用 ping 函數(shù),有三種方式:
第一種
call 調(diào)用:通過合約地址,合約函數(shù),函數(shù)簽名和調(diào)用參數(shù)進行調(diào)用。如果被調(diào)用函數(shù)中有修改合約變量的代碼,將修改被調(diào)用合約中相應(yīng)的變量。
第二種
delegatecall 與 call 類似,區(qū)別是 delegatecall 執(zhí)行時,僅僅使用被調(diào)用函數(shù)的代碼,而代碼中如果涉及到合約變量的修改,則是修改調(diào)用者合約中的變量。如果被調(diào)用的函數(shù)中有 d.send(amount)的指令,表示向地址 d 轉(zhuǎn)一定數(shù)額的以太幣,在 call 模式下這筆錢從被調(diào)用合約的余額中轉(zhuǎn)出。在 delegatecall 模式下將從調(diào)用者合約的余額中轉(zhuǎn)出。
因此, delegatecall 是更危險的命令,如果這一命令加載的函數(shù)代碼是合約編寫者不可控的,可能會導致合約的錢被轉(zhuǎn)走或合約被銷毀等嚴重后果。
第三種
這第三種調(diào)用方式在論文中被稱為直接調(diào)用 (direct call). 它先在合約里聲明了 Alice 合約需要調(diào)用的函數(shù),然后調(diào)用它。這種方式與以上兩種方式在異常處理上會有區(qū)別。
需要注意的是,以上三種方式如果將函數(shù)名或者參數(shù)類型設(shè)置錯誤,則會調(diào)用回退函數(shù) (fallback function). 如果是因為筆誤打錯了內(nèi)容,可能會觸發(fā)本不該執(zhí)行的回退函數(shù)中的代碼。
Gasless Send
在 Solidity 中,如果變量 rec 的類型為 address, 那么 rec.send(amount) 表示由合約向地址 rec 轉(zhuǎn)賬數(shù)額為 amount 的 wei. (10^18 wei = 1 ether ) 在這個執(zhí)行的過程中,還會觸發(fā)地址 rec 的回退函數(shù)。如果回退函數(shù)執(zhí)行過程中消耗的 gas 大于 2300,則會觸發(fā)一個異常,導致轉(zhuǎn)賬失敗。
異常處理
在背景介紹中我們提到過,使用 Solidity 執(zhí)行智能合約時會拋出異常,但是不同的合約內(nèi)函數(shù)調(diào)用方式對異常(exception)的處理方式不一樣。
如果合約執(zhí)行過程中沒有函數(shù)調(diào)用,或者只有 direct call 直接調(diào)用,那么當觸發(fā)一個異常的時候,視為合約執(zhí)行失敗,直接停止合約的執(zhí)行,回滾執(zhí)行過程中的轉(zhuǎn)賬和對合約變量的修改等操作,并扣除全部的交易費用。
如果通過 call, delegatecall 或 send 調(diào)用其他合約函數(shù),在執(zhí)行期間觸發(fā)的異常不會影響原有函數(shù)。也就是說,如果在執(zhí)行 send 的觸發(fā)的回退函數(shù)過程中,如果 gas 不足引起了異常,轉(zhuǎn)賬會失敗,但是原有合約會被成功地執(zhí)行。
如果對這一點缺乏足夠的理解,錯誤地認為合約執(zhí)行成功意味著 call 調(diào)用也一定成功,錯誤地認為沒有觸發(fā)異常就意味著 ether 轉(zhuǎn)賬成功,就可能導致合約有安全性問題。正確的做法應(yīng)當是通過函數(shù)調(diào)用返回的結(jié)果判斷其執(zhí)行是否成功。而一些研究表明有 28% 的合約沒有檢查返回結(jié)果。(當然,這不意味著一定有安全問題)
重入問題
Solidity 中回調(diào)函數(shù)的機制,可能會讓合約調(diào)用其他函數(shù)后,被調(diào)用的函數(shù)又調(diào)用了調(diào)用者合約的函數(shù),造成循環(huán),下面是一個例子
假設(shè)區(qū)塊鏈上已經(jīng)如下的合約 Bob,如果 sent 變量為 false, 就向給定地址發(fā)送一筆錢。
而 Mallory 是攻擊者惡意構(gòu)造的合約,代碼如下所示。
Bob 合約設(shè)計的本意是,如果 sent 變量為 false, 就向給定地址發(fā)送一筆錢。然而,當這筆錢發(fā)往攻擊者合約時,會觸發(fā)攻擊者合約的回退函數(shù),回退函數(shù)再次調(diào)用 ping 函數(shù),如此無限循環(huán),直到交易費耗盡或調(diào)用深度達到上限 1024 次觸發(fā)異常。但之前提到了,對于 call 調(diào)用的函數(shù)在執(zhí)行過程中觸發(fā)的異常,不會影響原來的函數(shù)的成功執(zhí)行。也就是說,除了最后一步轉(zhuǎn)賬會失敗,之前的轉(zhuǎn)賬都會成功。
幾種攻擊
接下來,我們介紹幾種利用上面提到弱點的攻擊例子。
DAO 攻擊
DAO 攻擊是以太坊歷史上最著名的攻擊,盜走了價值 6000 萬美元的以太幣。以太坊社區(qū)通過強行回滾硬分叉了以太坊,導致了以太坊和以太經(jīng)典兩條分叉鏈并存的局面。
下面是一個簡化版的 DAO 智能合約,但足以描述 DAO 合約的漏洞。
這個合約的功能很簡單,任何人可以向指定地址捐獻以太幣,受捐贈人可以提走自己受捐贈的幣。
而攻擊者通過以下的合約,就可以大量轉(zhuǎn)走合約中的幣。
其原理與上文所說的重入問題完全一樣, SimpleDAO 合約的 withdraw 函數(shù)執(zhí)行時向攻擊者合約轉(zhuǎn)賬,轉(zhuǎn)賬會觸發(fā)攻擊者合約的回退函數(shù),攻擊者合約的回退函數(shù)會重新調(diào)用 SimpleDAO 合約的 withdraw 函數(shù),形成一個循環(huán)。當循環(huán)因為各種原因結(jié)束的時候,除了最后一步,之前的執(zhí)行都不會失敗。攻擊者轉(zhuǎn)出了大量的錢。
另外,這個合約沒有考慮整數(shù)溢出問題,因此有如下攻擊成本更低的方案
在這個合約中,攻擊者設(shè)計了一個函數(shù) attack, 當這個函數(shù)被執(zhí)行的時候,攻擊合約先給自己捐贈 1 wei, 然后把這 1 wei 取出來。在取錢的時候,會觸發(fā)攻擊者合約的回退函數(shù)。與之前的攻擊不同,這次我們只利用重入問題 1 次,也就是 withdraw 函數(shù)被執(zhí)行了兩遍。在 withdraw 第二次向攻擊者轉(zhuǎn)賬以后,攻擊者不再調(diào)用 withdraw.
于是 withdraw 函數(shù)中的轉(zhuǎn)賬操作 msg.sender.call.value(amount)() 發(fā)生了2次,自然地,它的下一行也會被調(diào)用 2 次。這兩次被調(diào)用將 credit[攻擊者地址] 變成了 -1 wei, 會被虛擬機解讀為 2^256-1 wei. 這時,攻擊者可以從中取出幾乎無限多的錢出來。
特別的是,即使 withdraw 函數(shù)在轉(zhuǎn)賬后檢查 send 執(zhí)行是否成功,也只能防范第一種攻擊。
以太王座
考慮下面一個游戲合約,在游戲中,大家將競爭一個王座。后來者可以通過向王座上的人支付一筆錢來取而代之,每一輪取得王座需要的錢都要比上一輪高。最后取得王座的人有額外的收益。(沒有在合約中體現(xiàn)。)
這個合約看上去沒什么問題。事實上,他人在向王座上的人(地址)支付費用的時候,會觸發(fā)那個地址(如果是一個合約)的回退函數(shù)。如果王座上合約地址的回退函數(shù)需要的交易費過高,會觸發(fā) gasless send 的問題,就會導致轉(zhuǎn)賬失敗。但后續(xù)變更王座擁有者的代碼還會照常執(zhí)行,新來者可以毫無成本地獲得王座。
修改這一問題的思路看上去很簡單,只要將轉(zhuǎn)賬的代碼 king.send(compensation) 變成 if(!king.call.value(compensation)())throw; 來判斷一下轉(zhuǎn)賬是否成功就可以了。然而這會導致另一個問題。王座上的地址(合約)將自己的回退函數(shù)設(shè)定成一定會觸發(fā)異常,例如 function(){throw;},就沒有人有能力將他從王座上趕下去了,因為所有轉(zhuǎn)賬的結(jié)果都會失敗。
以上就是這一期的內(nèi)容,在接下來的文章中,我們將會介紹文獻中提到的其他的 Solidity 的弱點與可能導致的問題。
參考文獻:
[1] Atzei, Nicola, Massimo Bartoletti, and Tiziana Cimoli. "A survey of attacks on ethereum smart contracts (sok)." Principles of Security and Trust. Springer, Berlin, Heidelberg, 2017. 164-186.
Conflux 是致力于打造下一代高性能的 DAPP 公鏈平臺
歡迎關(guān)注我們的微信公眾號:Conflux中文社區(qū)(Conflux-Chain)
添加微信群管理員 Confluxgroup 回復(fù)“加群”加入 Conflux官方交流群
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/17895.html
摘要:新年前,我們最后來談一談以太坊安全性的特點。以太坊使用了一個硬分叉解決了這一問題。合約擁有者利用函數(shù)的異常處理和調(diào)用棧大小限制進行攻擊。結(jié)語通過這幾期對參考文獻的學習,我們看到了一些以太坊合約中設(shè)計的弱點。 新年前,我們最后來談一談以太坊安全性的特點。 不可能修改的bug 當合約公開在區(qū)塊鏈上之后,它就不能去修改了。相應(yīng)的,合約中出現(xiàn)的任何 bug 也沒有機會改正。如果希望能夠修改bu...
摘要:新年前,我們最后來談一談以太坊安全性的特點。以太坊使用了一個硬分叉解決了這一問題。合約擁有者利用函數(shù)的異常處理和調(diào)用棧大小限制進行攻擊。結(jié)語通過這幾期對參考文獻的學習,我們看到了一些以太坊合約中設(shè)計的弱點。 新年前,我們最后來談一談以太坊安全性的特點。 不可能修改的bug 當合約公開在區(qū)塊鏈上之后,它就不能去修改了。相應(yīng)的,合約中出現(xiàn)的任何 bug 也沒有機會改正。如果希望能夠修改bu...
摘要:新年前,我們最后來談一談以太坊安全性的特點。以太坊使用了一個硬分叉解決了這一問題。合約擁有者利用函數(shù)的異常處理和調(diào)用棧大小限制進行攻擊。結(jié)語通過這幾期對參考文獻的學習,我們看到了一些以太坊合約中設(shè)計的弱點。 新年前,我們最后來談一談以太坊安全性的特點。 不可能修改的bug 當合約公開在區(qū)塊鏈上之后,它就不能去修改了。相應(yīng)的,合約中出現(xiàn)的任何 bug 也沒有機會改正。如果希望能夠修改bu...
閱讀 3200·2021-11-25 09:43
閱讀 3206·2021-11-23 09:51
閱讀 3518·2019-08-30 13:08
閱讀 1568·2019-08-29 12:48
閱讀 3594·2019-08-29 12:26
閱讀 396·2019-08-28 18:16
閱讀 2561·2019-08-26 13:45
閱讀 2428·2019-08-26 12:15