[Ethernaut CTF] Preservation

0xDave·2022년 10월 8일
0

Ethereum

목록 보기
35/112

(수정중)

소스코드


// SPDX-License-Identifier: MIT
pragma solidity ^0.6.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) public {
    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;
  }
}

해결과제


This contract utilizes a library to store two different times for two different timezones. 
The constructor creates two instances of the library for each time to be stored.

The goal of this level is for you to claim ownership of the instance you are given.

  Things that might help

    Look into Solidity's documentation on the delegatecall low level function, how it works, 
    how it can be used to delegate operations to on-chain. 
    libraries, and what implications it has on execution scope.
    Understanding what it means for delegatecall to be context-preserving.
    Understanding how storage variables are stored and accessed.
    Understanding how casting works between different data types.

ownership 가져오기


해결과정


delegatecall

delegatecall 은 외부 컨트랙트의 함수를 호출할 때 사용된다. 그냥 call을 사용해도 되지만 delegatecall의 특징은 context 유지에 있다. 위 사진에 잘 나와있듯이, delegatecall을 하면 호출된 컨트랙트에서 msg.sendermsg.value는 EOA의 것으로 인식된다. 또한 변수의 값이 변경되면 변경된 값이 Target contract의 storage에 저장되는 것이 아니라 Caller contract의 storage에 저장된다. Target contract에서 함수가 실행되지만 저장은 Caller contract에 되기 때문에 이러한 특징을 이용해서 컨트랙트 업그레이드가 가능하다.

업그레이드가 가능하다는 말이 처음엔 와닿지 않을 수 있다. 스마트컨트랙트는 원래 한 번 디플로이되면 수정할 수 없는 것 아닌가? 물론 맞다. 여기서 업그레이드가 가능하다는 말은 Target contract가 바뀌어도 Caller contract에는 아무 이상 없다는 말이다. 즉, Target contract에 이상이 생기면 이 점을 보완해서 Target contract B를 Caller contract와 연결시키면 된다. 어차피 그 동안의 데이터는 Caller contract에 저장되어 있을 것이고, delegatecall을 이용하면 문제가 있는 함수의 이름은 그대로 두고 로직만 바꿔서 그대로 호출할 수 있기 때문이다.

이번 문제의 핵심은 delegatecall을 제대로 이해했는지에 달렸다. 처음에 코드를 봤을 때는 ownership을 변경하는 함수도 없고, 이와 관련된 로직도 발견할 수 없었기 때문에 setFirstTime이나 setSecondTime 함수가 왜 존재하는지 이해할 수 없었다. 하지만 delegatecall과 컨트랙트를 업그레이드 할 수 있다는 것을 알고나면 문제에 좀 더 쉽게 접근할 수 있다.


출처 및 참고자료


  1. DelegateCall: Calling Another Contract Function in Solidity
profile
Just BUIDL :)

0개의 댓글