Ethernaut靶场通关记录|以太Ethernaut靶场打靶—1 Fallback


以太Ethernaut靶场打靶—1 Fallback

    • 源码审计
    • 攻击流程

现在来到第一关,打靶主要还是先对智能合约进行审计如果没有学过的可以去https://cryptozombies.io/
进行学习,有代码经验的还是能很快入门
源码审计
pragma solidity ^0.6.0; import '@openzeppelin/contracts/math/SafeMath.sol'; contract Fallback {using SafeMath for uint256; mapping(address => uint) public contributions; address payable public owner; constructor() public { owner = msg.sender; //构造当前拥有者 contributions[msg.sender] = 1000 * (1 ether); //初始1000 }modifier onlyOwner {//当前调用修饰 require( msg.sender == owner, "caller is not the owner" ); _; }function contribute() public payable { // require(msg.value < 0.001 ether); //判断当前发送者是否小于0.001 contributions[msg.sender] += msg.value; //贡献为1000+值 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(address(this).balance); }fallback() external payable {//fallback将owner 设置为msg.sender //fallback0.6.0后写法 require(msg.value > 0 && contributions[msg.sender] > 0); //条件(漏洞所在) owner = msg.sender; } }

靶场要求是要获得合约所有权并且转出余额
首先我们来到源码进行分析发现有两处可以拿到合同所有权
1
function contribute() public payable { //每次转账只能小于0.001个eth,转多少个eth,就增加同贡献值,贡献值最多的人获得合约所有权 require(msg.value < 0.001 ether); //判断当前贡献值是否小于0.001 contributions[msg.sender] += msg.value; //贡献为1000+值 if(contributions[msg.sender] > contributions[owner]) { owner = msg.sender;

2
require(msg.value > 0 && contributions[msg.sender] > 0); //条件(漏洞所在)fallback函数,用于接收用户向合约发送的代币,如果转入的金额和代币的金额不等于0,将获得合约所有权 owner = msg.sender;

要拿所有权后就可以通过withdraw()将余额提取出来;
而当前拥有者可以使用fallback()或者contribute()将当前拥有者替换为当前信息发送者。

contribute()显然发送值小于0.01要转10^6次才能达到1000贡献值。
fallback()条件是发送的值大于0,且当前贡献值大于0
显然fallback() 可触发条件,但是需要转账触发fallback()需要转账触发,且转两次第一次实现贡献值大于0,然后再转一次触发fallback()。
攻击流程 【Ethernaut靶场通关记录|以太Ethernaut靶场打靶—1 Fallback】首先查看一下合约拥有者
Ethernaut靶场通关记录|以太Ethernaut靶场打靶—1 Fallback
文章图片

我调用contribute向合约转1wei
Ethernaut靶场通关记录|以太Ethernaut靶场打靶—1 Fallback
文章图片

使贡献值大于0,为满足发送值大于0执行contract.send(1); 再转1wei满足后面条件触发fallback获得合约所有权
Ethernaut靶场通关记录|以太Ethernaut靶场打靶—1 Fallback
文章图片

此时可以看看合约所有权地址已经变成我们的地址了
Ethernaut靶场通关记录|以太Ethernaut靶场打靶—1 Fallback
文章图片

现在我们再通过函数withdraw()将余额提出来
Ethernaut靶场通关记录|以太Ethernaut靶场打靶—1 Fallback
文章图片

然后提交实例
Ethernaut靶场通关记录|以太Ethernaut靶场打靶—1 Fallback
文章图片

    推荐阅读