[Ethereum] Ethernaut 풀이 - 3.Coin Flip

in kr-dev •  6 years ago 

modolee_logo
안녕하세요. 개발자 모도리입니다.
Ethernaut 문제 풀이 시리즈를 계속 진행해 보려고 합니다.
이번에는 Level 3 문제를 풀어보겠습니다. 지난 게시물은 아래를 확인해 주세요.


3. Coin Flip

임무 확인

00_mission.png

  • 동전 뒤집기 게임이라고 하네요. 앞면이 나올 지 뒷면이 나올지 예측을 하는 게임입니다.
  • 임무 완수 조건은 10번 연속 예측에 성공하는 것입니다.
  • 초능력을 써서 하라고 합니다...ㅠㅠ
  • 난이도는 총 6단계 중 3단계에 해당합니다.

Solidity 코드 분석

  • 초능력은 모르겠고, 일단 코드를 보겠습니다.
  • 01_source.png
  • 버전 및 상태변수 정의
  • 02_source_01.png
  • Solidity 0.4.18 버전을 기준으로 작성되었습니다.
  • CoinFlip 컨트랙트가 정의되어 있습니다.
  • 3개의 상태 변수가 있습니다.
    • consecutiveWins : 연속해서 승리한 횟수를 저장하는 변수입니다. public입니다.
    • lastHash : 마지막 실행할 때 사용되었던, hash 값 입니다.
    • FACTOR : hash 값과 함께 사용하여 랜덤한 값을 만들어 낼 때 사용하는 값입니다.
  • 생성자
  • 03_source_02.png
  • 컨트랙트를 생성하면서 연속해서 승리한 횟수를 0으로 초기화합니다.
  • 동전 뒤집기 게임 함수
  • 04_source_03.png
  • 입력 예측 값(_guess)으로 앞면/뒷면(true/false) 값을 받고, 실제 랜덤하게 생성한 앞면/뒷면 결과와 비교해서 같으면 true, 다르면 false 값을 반환하는 함수입니다.
  • blockValue는 solidity 내장 함수인 block.blockhash 함수를 이용해서 한 개 전 블록(block.number - 1)의 blockhash 값을 가져옵니다.
  • blockVale의 값과 lastHash의 값이 같은 경우 (이전 게임에서 사용되었던 것과 같은 랜덤 시드를 사용할 경우) revert 시킵니다.
    • revert() : 실행을 중단 시키고, 상태는 실행 전으로 돌립니다.
  • lastHash에 새로 생성 된 blockValue를 저장합니다.
  • coinFlip에는 아까 구한 blockValue를 FACTOR로 나눈 값을 저장합니다.
  • 그리고 그 값이 1이면 side 변수에 true를 저장하고 그렇지 않으면 false를 저장합니다.
  • side와 _guess가 같으면 consecutiveWins에 1을 더하고 true를 반환하고, 그렇지 않으면 다시 처음부터 카운팅하도록 consecutiveWins를 0으로 만들고 false를 반환합니다.

새로운 인스턴스 생성

  • Get new instance 버튼을 눌러서 임무를 시작합니다.

Remix 이용하기

  • 소스코드를 붙여 넣습니다.
  • 05_remix_source.png
  • 콘솔에서 Instance address를 확인합니다.
  • 06_instance_address.png
  • 해당 주소를 복사해서 Remix의 Load contract from Address 창에 붙여 넣고 At Address 버튼을 눌러주면 해당 컨트랙트를 불러옵니다.
  • 07_deploy.png

예측을 해 보겠습니다!

  • 계속 true만 넣었더니 5번 만에 처음으로 맞췄습니다!!! ㅠㅠ
  • 08_flip_1.png
  • 오! 6번째에도 맞췄습니다. 우주의 기운이 모여진 것 같습니다. 이대로 10번까지 가즈아~~
  • 09_flip_2.png
  • 아........... 틀렸어요.
  • 10_flip_0.png
  • 당연히 이렇게 풀라고 낸 문제가 아니겠죠?

문제 풀이

  • 초능력을 발휘할 수 있는 컨트랙틀 작성합니다.
  • 11_psychic_ability.png
  • 어디서 많이 본 코드 같죠?? 맞습니다. CoinFlip 코드를 복사해서 붙여넣고, 조금만 수정했습니다.
  • 다른 부분은 크게 2가지 입니다.
    • 생성자에서 target 변수에 배포 된 CoinFlip 컨트랙트 주소를 받아 올 수 있게 합니다.
    • side를 예측해서 _guess와 비교하는 것이 아니라. 그냥 예측한 그 side 값을 CoinFlip의 flip 함수를 호출하는데 사용합니다.
  • 이렇게 하면 정말 초능력이 발휘되어 10번 연속 결과를 맞출 수 있을까요?
  • PsychicAbility 컨트랙트를 배포할 때 아까 확인했던 instance address를 넣어서 배포합니다.
  • 12_psychic_ability_deploy.png
  • 이제 시작해 보겠습니다! flip을 실행해 주세요!
  • 12_new_flip.png
  • 1번! 13_consecutive_win_1.png
  • 2번! 14_consecutive_win_2.png
  • 중간 중간 숫자가 안 올라가는 경우가 있는데, 이 경우에 트랜잭션 내역을 살펴보면 Out of gas 에러나 나 있는 것을 확인할 수 있습니다.
  • 18_oog.png
  • 동일 블록에서 랜덤 값을 뽑지 않게 하려고 작성한 코드 때문에 이런 에러가 발생합니다. 그냥 무시하시고 계속~ 하시면 됩니다.
  • 18_oog_source.png
  • 5번! 15_consecutive_win_5.png
  • 7번! 16_consecutive_win_7.png
  • 9번! 17_consecutive_win_9.png
  • 10번! 19_consecutive_win_10.png
  • 이게 어떻게 가능할까요?
  • PsychicAbility 컨트랙트의 flip 함수에서 CoinFlip 컨트랙트의 flip 함수를 부르게 되는데, 이 트랜잭션은 동일 블록에 담기게 됩니다. 그러면 blockhash를 이용해서 난수를 생성하는데, PsychicAbility에서 미리 해당 값을 구한 후 CoinFlip의 flip 함수를 호출한다면 매번 동일한 값이 나오게 될 것입니다.

답안 제출

  • 콘솔에서도 확인해 보겠습니다.
  • 20_console_10.png
  • Submit instance 버튼을 누릅니다.
  • submit_button
  • 임무를 완수했습니다.
  • 21_complete.png

말하고자 하는 취약점

오늘은 이것으로 마치겠습니다. 감사합니다.

Authors get paid when people like you upvote their post.
If you enjoyed what you read here, create your account today and start earning FREE STEEM!