새소식

인기 검색어

Write Up/Ethernaut

[Ethernaut] Fallout

  • -
반응형

// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;

import 'openzeppelin-contracts-06/math/SafeMath.sol';

contract Fallout {
  
  using SafeMath for uint256;
  mapping (address => uint) allocations;
  address payable public owner;


  /* constructor */
  function Fal1out() public payable {
    owner = msg.sender;
    allocations[owner] = msg.value;
  }

  modifier onlyOwner {
	        require(
	            msg.sender == owner,
	            "caller is not the owner"
	        );
	        _;
	    }

  function allocate() public payable {
    allocations[msg.sender] = allocations[msg.sender].add(msg.value);
  }

  function sendAllocation(address payable allocator) public {
    require(allocations[allocator] > 0);
    allocator.transfer(allocations[allocator]);
  }

  function collectAllocations() public onlyOwner {
    msg.sender.transfer(address(this).balance);
  }

  function allocatorBalance(address allocator) public view returns (uint) {
    return allocations[allocator];
  }
}

소스 코드가 주어지고 해당 contract의 owner를 탈취하라는 mission이 있다.

/* constructor */
function Fal1out() public payable {
    owner = msg.sender;
    allocations[owner] = msg.value;
  }

코드를 한번 살펴보면 Fal1out() 함수를 호출하면 owner는 해당 contract를 호출한 사람의 것으로 바뀌고 allocations[owner]는 입력한 값으로 바뀐다....?? Fal1out() ?? 조금 이상하다 constructor이면 Fallout()인데?? 암튼 넘어 가자

  function allocate() public payable {
    allocations[msg.sender] = allocations[msg.sender].add(msg.value);
  }

  function sendAllocation(address payable allocator) public {
    require(allocations[allocator] > 0);
    allocator.transfer(allocations[allocator]);
  }

  function collectAllocations() public onlyOwner {
    msg.sender.transfer(address(this).balance);
  }

  function allocatorBalance(address allocator) public view returns (uint) {
    return allocations[allocator];
  }
}

allocate() 함수를 이용하여 allocations[msg.sender]에 ETH를 전송할 수 있다.

sendAllocation() 함수는 allocations[allocator] > 0 이면 allocator에게 해당 금액 만큼 송금한다.

collectAllocations() 함수는 owner만 호출할 수 있는데 msg.sender에게 해당 balance만큼 송금한다.

그리고 allocatorBalance() 함수는 allocations[allocator]의 금액을 return해준다.

이렇게 전체적인 흐름을 보고나니까 contract의 owner를 얻을 수 있는 함수는 Fal1out() 함수밖에 없다.

함수를 호출한 결과 owner를 얻을 것을 확인할 수 있다.

그러자 바로 level을 completed 했다는 msg가 나왔다.

무슨 일이지....??

Ethernaut의 해석을 보니까 실제 Rubixi의 해킹 사례를 가지고 문제를 만들었다.

Rubixi는 Dynamic Pyramid에서 Rubixi로 회사 이름을 변경했다.

// https://etherscan.io/address/0xe82719202e5965Cf5D9B6673B7503a3b92DE20be#code
contract Rubixi {

        //Declare variables for storage critical to contract
        uint private balance = 0;
        uint private collectedFees = 0;
        uint private feePercent = 10;
        uint private pyramidMultiplier = 300;
        uint private payoutOrder = 0;

        address private creator;

        //Sets creator
        function DynamicPyramid() {
                creator = msg.sender;
        }
        modifier onlyowner {
                if (msg.sender == creator) _
        }

위 코드를 보면 contract는 Rubixi이다. 하지만 Set creator부분에서 function DynamicPyramid()로 그대로이다. 그러면 누구나 DynamicPyramid() 함수를 호출하여 creator가 될 수 있다.

잘못된 함수이름 때문에 생긴 취약점을 가르쳐 주려고 이런 문제를 낸 것 같다

반응형

'Write Up > Ethernaut' 카테고리의 다른 글

[Ethernaut] Preservation  (0) 2023.02.01
[Ethernaut] Telephone  (0) 2023.01.30
[Ethernaut] CoinFlip  (0) 2023.01.30
[Ethernaut] Fallback  (0) 2023.01.29
[Ethernaut] Hello Ethernaut  (0) 2023.01.28
Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.