Day 31:
Instructions
To solve this level, you only need to provide the Ethernaut with a Solver
, a contract that responds to whatIsTheMeaningOfLife()
with the right number. BUT the solver's code needs to be really tiny (10 opcodes at most).
Contract
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract MagicNum {
address public solver;
constructor() {}
function setSolver(address _solver) public {
solver = _solver;
}
/*
____________/\\\_______/\\\\\\\\\_____
__________/\\\\\_____/\\\///////\\\___
________/\\\/\\\____\///______\//\\\__
______/\\\/\/\\\______________/\\\/___
____/\\\/__\/\\\___________/\\\//_____
__/\\\\\\\\\\\\\\\\_____/\\\//________
_\///////////\\\//____/\\\/___________
___________\/\\\_____/\\\\\\\\\\\\\\\_
___________\///_____\///////////////__
*/
}
Breakdown
- the function setSolver takes an address type of another contract called 'solver' (whereby the contract returns 42 when the function whatIsTheMeaningOfLife called; and
- the user then calls the solver function to enter the magic number.
As these challenges have become more complex I need more than an evening to solve them. The complexity with this solution is I will have to write a bytecode contract that is outside of the regular remix IDE I've previously been using. I'd note for those wondering: why can't you simply deploy a contract with the following code and be done?:
functionwhatIsTheMeaningOfLife() external pure returns (uint) {
return 42;
}
Well the real issue at play here is by entering '42' via this methodology, it does not apply to the opcode specification. So the real challenge is actually creating code outside the compiler (i.e. a via byte contract) and obtaining a better understanding of working with bytecode. As a starting point I've started to look at Solidity by example and found the following contract:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract Factory {
event Log(address addr);
// Deploys a contract that always returns 42
function deploy() external {
bytes memory bytecode = hex"69602a60005260206000f3600052600a6016f3";
address addr;
assembly {
// create(value, offset, size)
addr := create(0, add(bytecode, 0x20), 0x13)
}
require(addr != address(0));
emit Log(addr);
}
}
interface IContract {
function getMeaningOfLife() external view returns (uint);
}
// https://www.evm.codes/playground
/*
Run time code - return 42
602a60005260206000f3
// Store 42 to memory
mstore(p, v) - store v at memory p to p + 32
PUSH1 0x2a
PUSH1 0
MSTORE
// Return 32 bytes from memory
return(p, s) - end execution and return data from memory p to p + s
PUSH1 0x20
PUSH1 0
RETURN
Creation code - return runtime code
69602a60005260206000f3600052600a6016f3
// Store run time code to memory
PUSH10 0X602a60005260206000f3
PUSH1 0
MSTORE
// Return 10 bytes from memory starting at offset 22
PUSH1 0x0a
PUSH1 0x16
RETURN
*/
I will be looking to manipulate the above and post the solution in the coming days!