跳至主要内容

[solidity hack] Selfdestruct

說明

Github 連結

影片連結

Selfdestruct 能夠將智能合約銷毀,並將合約內的所有以太幣轉移到其他智能合約。

假使有一智能合約名為 EtherGame,每個使用者可以在該智能合約存入以太幣,而當智能合約的以太幣總數量到達 10 的時候,系統會將最後一個存入以太幣的使用者取回所有智能合約的以太幣,使用者一次只能存入一枚以太幣。

Image

假設 EtherGame 存入的以太幣數量來到了 9 枚,下一位存入以太幣的時候將能夠將 10 枚以太幣全部取走。

Image

正當一名使用者準備存入最後一枚以太幣時,另一名攻擊者準備利用 Selfdesturct 攻擊 EtherGame。

Image

攻擊者將智能合約部署後,將 EtherGame 的智能合約地址傳入 attack function,並利用 Selfdesturct 將智能合約銷毀,發送以太幣到 EtherGame。

攻擊者使用 Selfdesturct 發送 1 枚以太幣成功將 EtherGame 智能合約內的以太幣永遠無法領取出來。

解決方法

解決的方法是將 EtherGame 的 despoit function 改寫,之所以會被 Selfdestruct 攻擊是因為 EtherGame 在取得智能合約以太幣餘額時,是使用 address(this).balance,這段程式碼會直接取得當下智能合約的以太幣餘額。

只要將 balance 改成全域的狀態變數,並在每次使用者發送以太幣的時候更新 balance 即可解決,因為 Selfdesturct 是發送以太幣到 "智能合約" 不是 balance 狀態變數,所以 EtherGame 的使用者還是能夠正常執行。

Image

值得一提的是,Selfdesturct 還是能夠強制發送以太幣到 EtherGame,但發送到 EtherGame 的以太幣將永遠無法提領出來。

Image

所以可以寫個 function,在遊戲結束的時候才能將剩餘的以太幣提取出來。

Image

假設使用者 A 是 EtherGame 的贏家,他成功存入了最後一顆以太幣,使 balance 等於 10 以太幣,在遊戲運行的過程中,有一攻擊者發送了 5 顆以太幣想來攻擊 EtherGame,使該智能合約的以太幣為 15 枚。

Image

而使用者 A 使用 claimReward function 將他所應得的以太幣領回,此時智能合約的以太幣剩下 5 枚。

Image

而這時部署合約的擁有者即可透過 withdrawAfterEnd function,將剩餘的以太幣領回。

Image