Transfer

向合約發送餘額失敗

  • January 16, 2022

我在玩ethernaut,山丘之王。我做了一個練習目標契約和一個進攻契約。我想向攻擊合約發送價值(這可行),然後我想將攻擊合約的餘額發送給實踐目標合約(這失敗)。我可以將餘額發送到零地址,但不能發送到這個練習目標,所以我認為問題在於我如何編寫練習目標。我在這裡的其他執行緒中讀到它需要一個應付的備份或接收功能,但不管有沒有這些,它都會失敗。這是練習目標:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract practice {
   uint public amount;
   event paid(uint amount);
   event received(uint amount);
   receive() external payable {
       emit received(msg.value);
       amount += msg.value;
   }
   fallback() external payable {
       emit paid(msg.value);
       amount += msg.value;
   }
}

這是攻擊合約:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract King {
   uint public amount;
   address public target;
 
   constructor() payable {
       amount += msg.value;
   }

   event attempt(uint amount, address sender);
   event fallbackCalled(uint amount, address sender);
   event valueAdded(uint amount, address sender);
   event successfulTransfer(uint amount);
   event failed(uint amount);

   function setTarget(address _target) public {
       target = _target;
   }

   function addValue() public payable {
       emit valueAdded(msg.value, msg.sender);
       amount += msg.value;
   }

   function sendBalance() public {
       emit attempt(address(this).balance, msg.sender);
       bool success = payable(target).send(address(this).balance);
       if (success) {
           emit successfulTransfer(address(this).balance);
           amount = 0;
       }
       if (!success) emit failed(address(this).balance);
   }
}

不確定這是否重要,但我正在通過 remix injection web3 在 rinkeby 網路上對其進行測試。

這裡的問題是payable(target).send(). 這是因為send()氣體限制為 2,300,這幾乎足以記錄一個事件。但由於該receive()函式包含狀態改變操作,send()會因gas overrun而失敗。

您可以改為使用target.call{value: address(this).balance}("")將 ETH 發送到目標合約。

與ethernaut一切順利!

引用自:https://ethereum.stackexchange.com/questions/119102