새소식

인기 검색어

Write Up/Ethernaut

[Ethernaut] Alien Codex

  • -
반응형

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

import '../helpers/Ownable-05.sol';

contract AlienCodex is Ownable {

  bool public contact;
  bytes32[] public codex;

  modifier contacted() {
    assert(contact);
    _;
  }
  
  function make_contact() public {
    contact = true;
  }

  function record(bytes32 _content) contacted public {
    codex.push(_content);
  }

  function retract() contacted public {
    codex.length--;
  }

  function revise(uint i, bytes32 _content) contacted public {
    codex[i] = _content;
  }
}

우리의 mission은 owner를 탈취하는 것이다.

흐음.. 그런데 여기에 owner를 가질 수 있는 함수가 없다.

helpers/Ownable-0.5.sol

Ownable-0.5.sol을 보면 _transferOwnership() 함수를 이용하여 _owner를 탈취할 수 있을 것 같다.

그런데 이 함수들은 전부 owner일 때만 가능하다.

 

이 문제는 Storage 저장 방식에 관한 문제이다. 코를 잘 보면 Secure coding이 부족한 부분이 있다. 바로 retract() 함수이다.

보통 Secure coding을 위해 +=, -= 을 .add, .sub로 설정하고, push, pop을 사용하면 안전한 함수이다. 그런데 retract() 함수는 codex.length--로 underflow가 일어난다.

 

Storage에 저장되는 조건이 4가지 있다.

  • Storage slot의 첫 번째 항목은 하위 순서로 정렬되어 저장된다.
  • 값 유형은 저장하는 데 필요한 만큼의 바이트만 사용한다.
  • 값 유형이 Storage slot의 나머지 부분에 맞지 않으면 다음 Storage slot에 저장된다.
  • 구조체 및 배열은 항상 새 slot에 시작하고 채워진다.

그리고 동적배열 같은 경우는 크기가 정해지지 않아서 다른 변수들 사이에 저장될 수가 없다. 그래서 외부 저장 장소에 동적 배열을 저장한다. 외부 저장 장소이지만 2 ** 256 범위 안에 있으며 계산은 keccak256으로 한다.

즉 새로운 slot p에 저장된다하면 keccak256(p)의 위치에 저장이 된다.

 

문제에서 같이 한 번 보자. 해당 컨트랙트의 함수를 호출하려면 contact가 true가 되어야 한다.

그러면 make_contact()함수를 호출한 다음 나머지 함수 호출이 가능하다.

이제 해당 Contract의 Storage를 보자

slot0은 contact랑 owner가 같이 있다. 그리고 다음 slot부터 codex이 들어가는데 그림으로 Storage를 살펴보자

Storage를 보면 2 ** 256개의 저장공간이 있고 저장공간 하나당 32byte가 들어간다. codex은 동적배열이니까 keccak256(1)을 한 위치에 codex[0]이 들어간다. 실제로 한 번 확인해 볼까??

확실히 keccak(1)에 내가 입력한 값이 들어가 있다.

input의 시작은 keccak256(1)부터 시작이다. 그리고 owner index에 접근하기 위해 ((2**256)-1) - keccak256(1)을 해주고 +1을 하면 0번 index에 접근할 수 있다. 또 Contract의 모든 변수에 액세스 하기 위해여 codex의 길이를 -1 시켜 underflow를 일으킨다.

index가 ((2**256)-1) - keccak256(1)이고 player32는 player의 address를 32byte로 늘린 거다. revise()를 하면 owner를 탈취할 수 있다.

반응형

'Write Up > Ethernaut' 카테고리의 다른 글

[Ethernaut] Shop  (0) 2023.02.13
[Ethernaut] Denial  (0) 2023.02.13
[Ethernaut] Recovery  (0) 2023.02.09
[Ethernaut] Privacy  (0) 2023.02.09
[Ethernaut] Naught Coin  (0) 2023.02.09
Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.