Ethernaut - Level 2. Fallout

김윤성·2024년 10월 10일

이 레벨을 완료하기 위해서는, 컨트랙트의 owner가 되어야 한다.

문제에서는 Solidity Remix IDE가 힌트가 될 수 있을 것이라고 한다.
Remix IDE는 스마트 컨트랙트를 작성할 때 실시간으로 여러 유용한 기능, 정보를 제공한다.


코드


먼저, 문제 이름이 Fallout이므로, 왜 Fallout일지(문제 내의 사진을 보면 Fal1out이다.) 생각해보자. Fallback처럼 기존에 있는 함수인지 찾아보았으나 없다.

일단 모르겠다.

그럼 코드를 살펴보자.


먼저, SafeMath 라이브러리를 가져와 안전한 연산과 overflow/underflow를 방지한다.
using SafeMath for uint256;은 uint 자료형으로 수행되는 연산을 안전하게 수행할 수 있도록 한다.

mapping(key => value) variable;
는 매핑으로, 키-값 쌍으로 동작한다.
여기서는 allocation[owner] = msg.value에서 owner는 address, msg.value는 uint로, owner주소에 msg.value 값을 기록하는 것이다.


Fal1out

constructor가(자칭) 정의되어있는데, 자세히 보면 함수의 이름이 Fallout이 아니라 Fal1out이다.
즉, 문제에서도 보았듯이 훼이크를 친 것 같다.
/constructor/구문은 깔끔히 무시해주고, 일반 함수로 받아들이자!

이 함수가 실행되면 함수 호출자가 owner가 된다.
함께 전송된 이더는 allocation[owner]에 저장된다.


onlyOwner

이전 문제에도 있었듯이, modifier를 사용해 require문이 참인지 확인한다. 즉, 함수 호출자 = 소유주인지 확인하여 옳지 않으면 "caller is not the owner" 메세지를 출력하고 트랜잭션에 실패하며, 참이라면, modifier가 적용된 함수 본문의 코드가 실행된다.(_;코드 덕분에)


allocate

이 함수가 실행되면 함수 호출자가 보낸 이더의 값을 allocation 매핑에 추가하여 저장한다.
추가로, add함수를 사용하여 오버플로우를 방지하였다.

function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
이는 SafeMath 라이브러리에 있는 add함수 코드이다.

위 방식을 통해 오버플로우를 감지한다.
만약 위 방식을 사용하지 않았더라면 어떤 일이 발생할 지 모르며, 의도치 않은 결과가 나올 수 있다.


sendAllocation

이더를 보낼 수 있는 allocator의 주소를 파라미터로 받아온다.
transfer함수는 지정된 주소에 이더를 전송하는 함수이다.
allocator에게 할당된 값이 0 이상이라면, allocator의 주소에 할당된 이더의 값이 전송된다.


collectAllocations

위의 modifier에서 require문이 참인 경우(함수 호출자 = 소유주인 경우) 실행할 수 있는 함수이다. 즉, owner만 실행할 수 있다.
여기에서 this는 해당 스마트 계약의 인스턴스 주소이므로, address(this).balance는 이 스마트 계약에 저장된 모든 잔액이다. 즉, msg.sender(=owner)에게 스마트 계약의 모든 남은 금액을 전송하는 함수로, 내용만 보아도 owner만이 실행해야 한다.


allocatorBalance

view로 선언되었으므로 단순히 정보를 볼 때만 쓰는 함수이다.(상태를 변경하지 않는 조회 함수)
allocator의 주소에 할당된 이더가 얼마인지 보여준다. 즉, 잔액 조회 용도이다.


실습!

인스턴스를 생성한다. 그림은 생략하겠다.

contract.owner()로 owner주소 체크

0x000000~~0000
아직 owner가 없다.
내가 owner가 되어야겠다.

contract.Fal1out()

이제 컨펌받고 다시 owner주소를 확인해보면 내가 owner이다!
아마 이 문제에서 의도한 것은 외부인이 owner가 될 수 없도록 설계해야 하는데 생성자에 오타를 내어 일반 함수로, 그것도 owner가 되기 쉬운 매우 취약한 함수로 만들었다. 오타를 조심하라는 문제인 듯 하다.

생각해보니 owner가 되기 전에 다른 함수들 실행해볼걸,,(owner가 아닐 때 오류 발생하는 구문을 보고싶엇다..)

일단 안에 얼마있는지 확인해보자.

Fal1out함수로 이더를 따로 전송하진 않았으므로 비어있다고 뜬다.(i라고 뜸, 이게 비었다는 뜻인가? 이것도 질문해야겠다)

contract.allocate({value:toWei("0.0000000000000001")})
로 1Wei를 보내보았다.

근데 왜 contract.allocatorBalance(player)을 하면 가진 금액이 뜨지 않는지 모르겠다. 위 사진과 동일하게 나온다. (질문 예정, 이후 수정)

내가 소유주이므로 컨트랙트의 모든 이더를 나에게 보내기 위해 collectAllocations()를 실행하고 컨펌받았다.

일단 할 것은 다 했으니 Submit instance를 하였다.

성공!

소감문

사실 0, 1을 풀 땐 크게 느끼지 못했는데, 확실히 이렇게 정리를 하면서 각 함수를 제대로 이해하고, 모르는 것은 찾아보고, 그 후에 문제를 푸니까 술술 풀린다. (물론 초반 문제라 쉬운 것도 있다. 하지만 느낌이 달라졌다는 것이 중요!)
문제 유형에 익숙해진 것도 있지만, 어떻게 해야할 지 조금씩 알겠다. 코드들도 아직은 익숙치 않음에도 몇 번 보니까 익숙해졌고, 정리하면서도 까먹을 수 있었던 부분들이 기억이 난다. 이제 꾸준하게만 하면 된다.. 아직 너무 쪼끔했다.

열공~

profile
윤성킴

0개의 댓글