selfdestruct函數(shù)(自毀函數(shù))由以太坊智能合約提供,用于銷毀區(qū)塊鏈上的合約系統(tǒng)。當合約執(zhí)行自毀操作時,合約賬戶上剩余的以太幣會發(fā)送給指定的目標,然后其存儲和代碼從狀態(tài)中被移除。
selfdestruct函數(shù)雖然能在緊急情況下幫助開發(fā)人員刪除智能合約并將合約內(nèi)的余額轉(zhuǎn)移到指定的地址,但這一特性也被不法分子利用,使它成為了攻擊手段。
讓我們來看個經(jīng)典游戲“幸運7”的案例:
合約代碼

漏洞分析
在“幸運7”游戲中,玩家每次向 EtherGame 合約中打入一個ETH,第七個成功打入ETH的玩家將成為 winner。winner 可以提取合約中的 7 個ETH。玩家每次玩游戲時都會調(diào)用 EtherGame.deposit 函數(shù)向合約中先打入一個ETH,隨后函數(shù)會檢查合約中的余額(balance)是否小于等于 7 ,只有合約中的余額小于等于 7 時才能繼續(xù)否則將回滾。合約中的余額(balance)是通過 address(this).balance 取到的,這就意味著我們只要有辦法在產(chǎn)生 winner 之前改變 EtherGame 合約中的余額讓他等于 7 就會使該合約癱瘓。這樣我們的攻擊方向就明確了,只要我們強制給 EtherGame 合約打入一筆ETH讓該合約中的余額大于7 這樣后面的玩家將無法通過 EtherGame.deposit 的檢查,從而使 EtherGame 合約癱瘓,永遠無法產(chǎn)生 winner。
但是EtherGame.deposit函數(shù)中存在驗證:require(msg.value == 1 ether, "You can only send 1 Ether"),這里要求我們每次只能打一個ETH進去,所以通過正常路徑是不可能一次向 EtherGame 打入大于 1 枚的ETH的,但是我們又需要打入大于 1 枚的ETH到 EtherGame 合約中,所以selfdestruct函數(shù)就登場了。
攻擊合約

攻擊者調(diào)用攻擊函數(shù)attack()銷毀合約并將合約中的余額強制轉(zhuǎn)賬到“幸運7”的游戲合約地址,制造出游戲合約查詢余額address(this).balance大于7的情況,導致函數(shù)回退,無法正常進行游戲的情況,使得游戲合約癱瘓。
修復建議
我們來分析一下攻擊者的思路:利用selfdestruct函數(shù)強制轉(zhuǎn)賬給游戲地址,導致unit balance = address(this).balance查詢出的balance的值大于7,誘發(fā)require(balance <= targetAmount, "Game is over");無法通過使得游戲合約癱瘓。
我們不難發(fā)現(xiàn)攻擊者是通過影響balance從而達到目的的,那么我們就讓balance不受這個影響,在最初先定義balance,再讓balance的值只受通過游戲規(guī)則內(nèi)的正常途徑的影響,這樣就不會出現(xiàn)balance值異常的情況了。
修復的代碼:

如果想了解更多的智能合約和區(qū)塊鏈知識,歡迎到區(qū)塊鏈交流社區(qū)CHAINPIP,一起交流學習~
社區(qū)地址:https://www.chainpip.com/