✅Preservation
delegatecall and storage
Description
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.
Background Knowledge
https://medium.com/coinmonks/ethernaut-lvl-16-preservation-walkthrough-how-to-inject-malicious-contracts-with-delegatecall-81e071f98a12
Code Audit
Recall that:
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.
In this challenge, LibraryContract::setTime()
modifies the state variable at slot 0. In a delegatecall scenario such as Preservation::setFirstTime()
, it is going to modify the state variable at slot 0 in contract Preservation
, that is, timeZone1Library
.
Now we know that timeZOne1Library
is controllable, we can deploy a malicious contract that has the same storage layout as Preservation
and contains a function named setTime()
. This malicious setTime()
function should modify the state variable at slot 2. When Preservation::setFirstTime()
is called again, it is going to change the state variable at slot 2 in contract Preservation
, that is, owner
. And we are done.
Solution
Step 1: In Remix, deploy the following malicious contract:
Step 2: Invoke setFirstTime(<malicious_contract_address>)
:
Now tiemZone1Library
should be updated to malicious contract's address:
Step 3: Invoke setFirstTime()
again with some random uint256
such as 1337
:
Now owner
should be updated to your Metamask wallet's address:
Summary
As the previous level, delegate
mentions, the use of delegatecall
to call libraries can be risky. This is particularly true for contract libraries that have their own state. This example demonstrates why the library
keyword should be used for building libraries, as it prevents the libraries from storing and accessing state variables.
Last updated