Skip to content

Solidity Game - Preservation Attack - why need `library` keyword?

License

Notifications You must be signed in to change notification settings

maAPPsDEV/preservation-attack

Repository files navigation

Solidity Game - Preservation Attack

Inspired by OpenZeppelin's Ethernaut, Preservation Level

⚠️Do not try on mainnet!

Task

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 game is for you to claim ownership of the instance you are given.

Hint:

  1. 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.
  2. Understanding what it means for delegatecall to be context-preserving.
  3. Understanding how storage variables are stored and accessed.
  4. Understanding how casting works between different data types.

What will you learn?

  1. delegatecall low level operation
  2. library vs contract
  3. Layout of State Variables in Storage

What is the most difficult challenge?

This game requires you to combine knowledge from Delegation Attack and Privacy Attack to claim ownership of the contract.

Delegation Attack 🤗

preservation1

  • Delegate call is a special, low level function call intended to invoke functions from another, often library, contract.
  • If Contract A makes a delegatecall to Contract B, it allows Contract B to freely mutate its storage A, given Contract B’s relative storage reference pointers.

Hint: if Contract A invokes Contract B, and you can control Contract B, you can easily mutate the state of Contract A.

Privacy Attack 🙄

preservation2

  • Ethereum allots 32-byte sized storage slots to store state. Slots start at index 0 and sequentially go up to 2²⁵⁶ slots.
  • Basic datatypes are laid out contiguously in storage starting from position 0, then 1, until 2²⁵⁶-1.
  • If the combined size of sequentially declared data is less than 32 bytes, then the sequential data points are packed into a single storage slot to optimize space and gas.

Hint: If you can match up storage data locations between Contract A and Contract B, you can precisely manipulate the desired variables in Contract A.

Security Considerations

  • Ideally, libraries should not store state.
  • When creating libraries, use library, not contract, to ensure libraries will not modify caller storage data when caller uses delegatecall. Use higher level function calls to inherit from libraries, especially when you i) don’t need to change contract storage and ii) do not care about gas control.

Source Code

⚠️This contract contains a bug or risk. Do not use on mainnet!

// SPDX-License-Identifier: MIT
pragma solidity >=0.4.22 <0.9.0;

contract Preservation {
  // public library contracts
  address public timeZone1Library;
  address public timeZone2Library;
  address public owner;
  uint256 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(uint256 _timeStamp) public {
    timeZone1Library.delegatecall(abi.encodePacked(setTimeSignature, _timeStamp));
  }

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

// Simple library contract to set the time
contract LibraryContract {
  // stores a timestamp
  uint256 storedTime;

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

Configuration

Install Truffle cli

Skip if you have already installed.

npm install -g truffle

Install Dependencies

yarn install

Test and Attack!💥

Run Tests

truffle develop
test

You should take ownership of the target contract successfully.

truffle(develop)> test
Using network 'develop'.


Compiling your contracts...
===========================
> Everything is up to date, there is nothing to compile.



  Contract: Hacker
    √ should claim ownership (393ms)


  1 passing (457ms)

About

Solidity Game - Preservation Attack - why need `library` keyword?

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published