selfdestruct函數(自毀函數)由以太坊智能合約提供,用於銷毀區塊鏈上的合約系統。當合約執行自毀操作時,合約賬戶上剩餘的以太幣會發送給指定的目標,然後其存儲和代碼從狀態中被移除。
selfdestruct函數雖然能在緊急情況下幫助開發人員刪除智能合約並將合約內的餘額轉移到指定的地址,但這一特性也被不法分子利用,使它成為了攻擊手段。
讓我們來看個經典遊戲“幸運7”的案例:
合約代碼
漏洞分析
在“幸運7”遊戲中,玩家每次向EtherGame 合約中打入一個ETH,第七個成功打入ETH的玩家將成為winner。 winner 可以提取合約中的7 個ETH。玩家每次玩遊戲時都會調用EtherGame.deposit 函數向合約中先打入一個ETH,隨後函數會檢查合約中的餘額(balance)是否小於等於7 ,只有合約中的餘額小於等於7 時才能繼續否則將回滾。合約中的餘額(balance)是通過address(this).balance 取到的,這就意味著我們只要有辦法在產生winner 之前改變EtherGame 合約中的餘額讓他等於7 就會使該合約癱瘓。這樣我們的攻擊方向就明確了,只要我們強制給EtherGame 合約打入一筆ETH讓該合約中的餘額大於7 這樣後面的玩家將無法通過EtherGame.deposit 的檢查,從而使EtherGame 合約癱瘓,永遠無法產生winner。
但是EtherGame.deposit 函數中存在驗證:require(msg.value == 1 ether, "You can only send 1 Ether"),這裡要求我們每次只能打一個ETH進去,所以通過正常路徑是不可能一次向EtherGame 打入大於1 枚的ETH的,但是我們又需要打入大於1 枚的ETH到EtherGame 合約中,所以selfdestruct函數就登場了。
攻擊合約
攻擊者調用攻擊函數attack()銷毀合約並將合約中的餘額強制轉賬到“幸運7”的遊戲合約地址,製造出遊戲合約查詢餘額address(this).balance大於7的情況,導致函數回退,無法正常進行遊戲的情況,使得遊戲合約癱瘓。
修復建議
我們來分析一下攻擊者的思路:利用selfdestruct函數強制轉賬給遊戲地址,導致unit balance = address(this).balance查詢出的balance的值大於7,誘發require(balance <= targetAmount, "Game is over" );無法通過使得遊戲合約癱瘓。
我們不難發現攻擊者是通過影響balance從而達到目的的,那麼我們就讓balance不受這個影響,在最初先定義balance,再讓balance的值只受通過遊戲規則內的正常途徑的影響,這樣就不會出現balance值異常的情況了。
修復的代碼:
如果想了解更多的智能合約和區塊鏈知識,歡迎到區塊鏈交流社區CHAINPIP社區,一起交流學習~
社區地址:https://www.chainpip.com/