Don't think! Just do it!

종합 IT 기술 정체성 카오스 블로그! 이... 이곳은 어디지?

Smart contract/Ethernaut 문제풀이

Ethernaut 문제풀이 #7 - Force

방피터 2022. 2. 28. 16:35

7번 Ethernaut 문제 Force입니다.

 

목표는 컨트랙트의 돈을 0보다 크게 만드는 것입니다. ㅎㅎ 이상한 문제군요.

힌트로 제시된 것은 역시 fallback 함수이고 컨트랙트를 공격하는 가장 좋은 방법은 다른 컨트랙트라고 하네요. Beyond the console의 내용도 마찬가지입니다. 이번에도 Remix를 사용하여 문제를 풀어야 하나 봅니다.

 

코드를 봅시다.

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

contract Force {/*

                   MEOW ?
         /\_/\   /
    ____/ o o \
  /~____  =ø= /
 (______)__m_m)

*/}

ㅋㅋㅋㅋㅋ 뭐야 이게 ㅋㅋㅋ 아무것도 없습니다. 다른 컨트랙트로 fallback을 일으켜보면 뭔가 알 수 있을까요? 음... 어떻게 저 고양이한테 돈을 밀어넣지? ㅠㅠ 갑자기 난이도가 확 올라가는 느낌입니다. 

 

일단 컨트랙트의 주소는 0입니다.

fallback이 없으면 어떻게 되는지 문서를 한번 살펴봐야 할 것 같습니다. fallback이 없으면 반환된다고 합니다. 음... 반환되는 주소를 속일 수 있나?... 아니면 정할 수 있나? 어쨌든 인터넷에 올라와 있는 다른 문제풀이는 참고하지 않으려고 합니다. 고민을 해야 내것이 됩니다.

 

Solidity 문서를 살펴보겠습니다. fallback으로 검색해보니 if it does not have a fallback function이라는 구문이 있네요 살펴보도록 합시다. 보다 보니 아래와 같은 문구가 있습니다.

  • Neither contracts nor "external accounts" are currently able to prevent that someone sends them Ether. Contracts can react on and reject a regular transfer, but there are ways to move Ether without creating a message call. One way is to simply "mine to" the contract address and the second way is using selfdestruct(x).

컨트랙트나 외부 계정으로부 둘다 누군가 보내는 이더를 막을 수 없습니다. 컨트랙트들은 전송에 반응하거나 거부할 수 있지만 message calld 생성하는 것 없이 이더를 옮길 수 있는 방법들이 있습니다. 첫번째는 단순히 컨트랙트 주소로 mine to 하는 것이고 두번째는 selfdestruct하는 것입니다.

 

mine to the contract address는 컨트랙트 주소를 채굴 보상 주소로 지정하는 것 같은데 이 문제가 채굴을 하라는 소리는 아니겠지요? 그럼 selfdestruct(x)라는 것을 활용해야 할 것 같습니다. 그럼 selfdestruct에 대해 알아봅시다.

 

solidity 문서 사이트에서 selfdestruct로 검색하자마자 해법을 찾은 것 같네요.

메뉴얼에 다 있다!

이런 순서가 되겠네요. 1. 새로운 컨트랙트를 만든다 2. 그 컨트랜트에 메타마스트를 통해 이더를 보낸다. 3. selfdestruct를 Force 컨트랙트의 주소를 넣어 호출한다. 4. 내 컨트랙트가 파괴되며 남은 잔고가 Force의 주소로 반환된다.

고얌미...

 

그럼 새로운 컨트렉트를 만들어 봅시다.

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

contract myContract {
  address public owner;
  
  constructor() public{
    owner = msg.sender; //내가 이 컨트랙트의 주인
  }

  receive() external payable{}//receive()으로 돈 받을 수 있고
  
  //modifier 연습
  modifier onlyOwner {
  	require(
      msg.sender == owner,
      "caller is not the owner"
    );
    _;
  }
  function destroy(address payable meow) public onlyOwner {//self destroy 함수.
    selfdestruct(meow);//고얌미에게 내 가진 것 모두를 보낸다~ 귀여우니까
  }
}

remix에서 문제가 있으면 조언을 해줍니다. 참고하면서 코드를 작성해봅시다.

이후 작성한 컨트랙트를 배포하고 메타 마스크를 이용해서 배포된 컨트랙트 주소에 돈을 약간 보냅니다. 배포된 컨트랙트의 주소는 remix deploy tap의 하단에서 복사하실 수 있습니다.

메타마스크에서 보내기 -> 금액 수정 후 다음 버튼으로 보내기

 

저는 1이더를 보내봤습니다. 조금 있으면 잔고가 줄어든 게 보일 거에요. 자 그럼 아래와 같이 콘솔창에 입력해 잔고를 확인해 봅시다. 

그럼 이제 destory()를 실행하겠습니다. contract의 address를 복사하여 remix 창에 붙여 넣습니다.

그리고 destory 버튼을 눌러 수행합니다. 자 그럼 level 7 컨트랙트에 아까 보낸 1 이더가 갔는지 확인해 봅시다.

성공입니다. 고양이 밖에 없던 컨트랙트에 1이더를 보냈습니다. 으하하 남의 컨트랙트에 억지로 돈을 보낸것을 좋아해야 하는지 모르겠지만 어쨋든 레벨은 클리어 되었습니다. 힌트에서 fallback을 사용하라고 되어 있지만 저는 receive()를 사용했어요. receive도 fallback의 일종이라서 그런 건가요? receive를 fallback으로 대체해도 동일한 결과를 얻을 수 있을 것 같습니다. 테스트 해보셔요 ㅎㅎ

 

인스턴스를 제출하기 버튼을 눌러 완료하고 또 칭찬을 받아봅시다 ㅎㅎ

오늘의 교훈!! 응? 남의 컨트랙트에 억지로 돈을 보낼일이 있나?? 음.. 그래도 selfdestruct에 대해 배웠으니까요 ㅎㅎ

계좌나 컨트랙트가 이더리움을 받는 것은 막을 길이 없다

 

이 정도로 하죠 ㅎㅎ 그럼 레벨 8에서 봅시다.

 

안녕~!

반응형