Magic Number
To solve this level, you only need to provide the Ethernaut with a Solver
, a contract that responds to whatIsTheMeaningOfLife()
with the right number.
Easy right? Well… there’s a catch.
The solver’s code needs to be really tiny. Really reaaaaaallly tiny. Like freakin’ really really itty-bitty tiny: 10 opcodes at most.
Hint: Perhaps its time to leave the comfort of the Solidity compiler momentarily, and build this one by hand O_o. That’s right: Raw EVM bytecode.
Good luck!
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract MagicNum {
address public solver;
constructor() {}
function setSolver(address _solver) public {
solver = _solver;
}
/*
____________/\\\_______/\\\\\\\\\_____
__________/\\\\\_____/\\\///////\\\___
________/\\\/\\\____\///______\//\\\__
______/\\\/\/\\\______________/\\\/___
____/\\\/__\/\\\___________/\\\//_____
__/\\\\\\\\\\\\\\\\_____/\\\//________
_\///////////\\\//____/\\\/___________
___________\/\\\_____/\\\\\\\\\\\\\\\_
___________\///_____\///////////////__
*/
}
Solution:
I recommand using this playground https://www.evm.codes/playground to debug what your are doing.
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
import "forge-std/Script.sol";
import "forge-std/console.sol";
interface Solver {
function whatIsTheMeaningOfLife() external returns(uint);
}
interface MagicNum {
function solver() external returns(address);
function setSolver(address _solver) external;
}
contract POC is Script {
function run() external {
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
address addr = vm.envAddress("INSTANCE_18");
vm.startBroadcast(deployerPrivateKey);
MagicNum magicNum = MagicNum(addr);
address deployedAddress;
assembly {
let ptr := mload(0x40)
let paddedBytes := shl(mul(8, 13), 0x69602a60005260206000f3600052600a6016f3)
mstore(ptr, paddedBytes)
deployedAddress := create(0, ptr, 19)
}
magicNum.setSolver(address(deployedAddress));
Solver(address(magicNum.solver())).whatIsTheMeaningOfLife();
vm.stopBroadcast();
}
}
There are two parts in this byte code : 0x69{602a60005260206000f3}600052600a6016f3
. The part inside the curly braces {} correspond to the part that will be deployed by the contract constructor (which is the part outside the curly braces).
Each two digit byte corresponnd to an op code + its following parameters if expected.
So:
Then if we call the return value: