1. Fallback
Difficulty: 1/10
Level: https://ethernaut.openzeppelin.com/level/0x9CB391dbcD447E645D6Cb55dE6ca23164130D008
先看通关要求:
- you claim ownership of the contract
- you reduce its balance to 0
再分析合约代码,要成为合约的owner,有两种方式:
第一种
1 | ... |
现在合约的owner初始化写入 contributions[msg.sender] = 1000 * (1 ether);
,也就是说需要传入大于 owner 的数量,但是 contribute
函数中已定义 require(msg.value < 0.001 ether);
所以此方法行不通。
第二种
最下方有一段跟其他函数不一样的代码,前面没有 function关键字,而是以 receive()
开头。这里有一个知识点 fallback function:
The fallback function is executed on a call to the contract if none of the other functions match the given function signature, or if no data was supplied at all and there is no receive Ether function. The fallback function always receives data, but in order to also receive Ether it must be marked payable.
也就是说,如果我们调用一个合约时函数签名不一致,或者函数名称不正确,就会直接运行这个 fallback function
。
分析这个合约里的 fallback function
的代码:
1 | receive() external payable { |
只需要达成 msg.value > 0
和 contributions[msg.sender] > 0
两个条件,msg.sender 就会被设定成 owner。
msg.value
可以在调用合约时控制。
而另外一个条件需要再分析下代码:contributions
是一个 mapping
,需要写入数据才能调用,而在 contribute
函数中有写入数据能力,分析 contribute
函数可知,以msg.value < 0.001 ether
调用 contribute
函数,可令 contributions[msg.sender] > 0
。打开 Console(F12),输入:
1 | > contract.contribute({value: toWei("0.00001")}) |
由于1 ether= 10^18^ wei,这里使用了预设的 toWei
函数将 0.00001 ether
转换成 wei
单位发送。
待交易确认后,再调用 fallback
:
1 | > contract.sendTransaction({value: toWei("0.00001")}) |
此时 owner
已经转为 msg.sender
。
再调用 withdraw
函数把balance 归零:
1 | > contract.withdraw() |
最后提交,本关完成。