GODMODE--999
GODMODE--999
Platform: Uiuctf 2026 | Category: Reversing | Type: Challenge | Difficulty: Hard | OS: NA | Author: D3v0o0Nu11 | Date: 2026-08-08 | Status: Solved Techniques: firmware_static_reverse_engineering, journal_replay_reconstruction, committed_vs_speculative_timeline, blake2s_keystream_file_decryption, raid9_vm_emulation, opcode_remap_recovery, checkpoint_rollback_if_modeling, smt_bitblast_offline_solving, chacha20_poly1305_key_derivation, lldb_gdbstub_ground_truth_extraction
Summary
Task: AArch64 firmware for a 'ranked console' plus an encrypted RankedFS disk; reconstruct the committed (post-rollback) journal timeline and defeat a RAID//9 dataflow VM to unlock a hidden achievement. Solution: reimplement the journaling FS offline, derive BLAKE2s keystream file keys, emulate the VM and solve the 48-byte player code with bitwuzla (modeling op9 as a symbolic checkpoint/rollback If), then ChaCha20-Poly1305-decrypt achievement.bin.
Recon
Port scan
nmap -p- -sV -sC <TARGET> --min-rate 1000 -Pn
| Port | Service | Version | Notes |
|---|---|---|---|
| <PORT> | <SVC> | <VER> | <notes> |
Enumeration highlights
- Event:
UIUCTF 2026| ID:20260808_uiuc2026_godmode_999 - Tags: firmware, custom_vm, qemu, aarch64, chacha20_poly1305, rollback, z3, blake2s, custom_filesystem, journaling_fs, dataflow_vm, smt_solver, bitwuzla, virtio_blk, unaligned_access_fault
- Indicators: superblock magic RNK9, RankedFS journal magic JRNL with committed_generation, RAID//9 header magic and dataflow node/edge table, ACHV999 achievement.bin with ChaCha20-Poly1305 nonce+tag, QEMU cortex-a72 MMU-off Device-memory unaligned LDP fault ESR 0x96000021
- Source:
20260808_uiuc2026_godmode_999.md
Foothold
Vulnerability / Misconfiguration
- Firmware_static_reverse_engineering
- Journal_replay_reconstruction
- Committed_vs_speculative_timeline
- Blake2s_keystream_file_decryption
- Raid9_vm_emulation
<command>
Exploitation
- See original writeup content for detailed exploitation.
Privilege Escalation
Enumeration
sudo -l find / -perm -4000 2>/dev/null getcap -r / 2>/dev/null cat /etc/crontab ps aux
Exploitation
- N/A for challenge-type writeup; see exploitation above.
- Flag obtained via challenge solve.
<command>
Flags
| Flag | Location | Value |
|---|---|---|
| flag | REDACTED |
Key Takeaways / Lessons
- firmware_static_reverse_engineering
- journal_replay_reconstruction
- committed_vs_speculative_timeline
- blake2s_keystream_file_decryption
- raid9_vm_emulation
- opcode_remap_recovery
- checkpoint_rollback_if_modeling
- smt_bitblast_offline_solving
- chacha20_poly1305_key_derivation
- lldb_gdbstub_ground_truth_extraction
- Tags: firmware, custom_vm, qemu, aarch64, chacha20_poly1305, rollback, z3, blake2s, custom_filesystem, journaling_fs, dataflow_vm, smt_solver, bitwuzla, virtio_blk, unaligned_access_fault
Original Writeup
<details><summary>Click to expand original content</summary>Description
Silver Wolf calls it a one-button clear. The ranked server calls it an impossible replay. We recovered the firmware from her LV.999 console and the drive containing her last match. Restore the committed timeline and claim the achievement she left behind. The server remembers every rollback. Only committed ticks affect MMR.
Boot command:
qemu-system-aarch64 -M virt -cpu cortex-a72 -m 128M -global virtio-mmio.force-legacy=false -bios godmode.rom -drive file=ranked.img,format=raw,if=none,id=ranked -device virtio-blk-device,drive=ranked -nographic
English summary: Two files are given — godmode.rom (13672-byte raw AArch64 firmware) and ranked.img (16 MB disk, superblock magic RNK9). The firmware is a bare-metal "ranked console" that mounts a custom journaling filesystem (RankedFS), decrypts per-file blocks, and gates an achievement behind a verify command that runs a RAID//9 dataflow VM over four replay files. The goal is to reconstruct the committed (post-rollback) timeline, recover the 48-byte player code the VM accepts, and decrypt the hidden achievement to obtain the flag.
Analysis
Recon
godmode.rom: raw AArch64 firmware loaded by QEMU-bios(entry PC=0, base 0x0). PL011 UART at0x09000000, virtio-mmio scanned from0x0a000000.ranked.img: 16 MB disk, superblock magicRNK9, block bodies look encrypted/random.- Booting the console prints
GODMODE//999 RANKED CONSOLE.achievement→LOCKED // restore the committed LV.999 replay;verifypromptsPLAYER CODE (48 bytes):. Strings reveal RankedFS tags (RANKEDFS-COMMITTED/ROOT/BLOCK), RAID9 tags (RAID9-MAP/TARGET/DROP),ACHV999, file paths (/profile/player.dat,/replays/{tutorial,placement,promotion,godmode}.raid,/cache/achievement.bin), and the win stringONE-BUTTON CLEAR // HIDDEN MMR 999.
Firmware function map (full disasm via capstone/r2)
reset 0x0, uart_gets 0x80, main 0x140, sb_verify 0x500, uart putc/getc 0x5b4/0x5d4, puts 0x600, virtio_blk_init 0x840, virtio_read_sector 0x984, rankedfs_mount 0xa40, fs_lookup 0x1020, fs_read(+decrypt) 0x10a0, raid9_validate 0x12a0, derive_key 0x1440, verify_part1 0x1500, VM dispatch 0x18d8, VM opcode handlers 0x1c60-0x1e64, verify_part2 0x1e70, cmd_replay 0x2010, BLAKE2s init/update/final 0x2534/0x2590/0x2654, BLAKE2s G-round 0x21c0, ChaCha20 block 0x2780, Poly1305 0x2ad0, ChaCha20-Poly1305 AEAD 0x2dd0.
RankedFS on-disk layout
- Two superblocks: disk
0x0(gen0, committed_generation=0) and0x1000(gen28, committed_generation=28). Struct: magicRNK9, ver=9, hdr_len=0x3c, journal_start_sector=2, journal_count,committed_generation@+0x14, FNV-1a checksum@+0x18 (init0x811c9dc5, prime0x01000193, per bytex ^= x>>13, over 0x3c bytes with the checksum word zeroed). - Plaintext journal at disk
0x2000, 0x80-byte entries, magicJRNL, strictly increasingseq, per-entry FNV checksum @+0x1c. Fields:type@+4,seq@+8,cond/count@+0xc,node_id@+0x10,sizeA@+0x14,offB@+0x18, 0x30-byte payload@+0x20.
The rollback mechanic (core insight)
rankedfs_mount replays the journal, maintaining a live directory of 16 inode slots (0x40 bytes each: rec[0:0x30]=name, f30@0x30, f34@0x34, nid@0x38, present@0x3c). Journal op types:
- type1: create/find inode by
nid, copy 0x30-byte name payload,present=1 - type2: set
inode[0x30:0x38] = entry[0x14:0x1c](f30=block index, f34=size) - type3: set inode record (0x30 bytes)
- type4: DROP (
present=0) - type5: ROOT snapshot (
root = copy(live); save marker =entry[0xc]) - type6: CONDITIONAL ROLLBACK — if
entry[0xc] == saved marker:live = copy(root)(undo speculative changes since the snapshot) - type7: COMMIT (
ctx = copy(live);committed_id = seq)
This disk's journal contains type5/6 pairs with matching markers (101/101 @seq5/10, 202/202 @15/19, 303/303 @24/27), so seq 10/19/27 roll back the speculative writes (the speculative godmode nid99 / achievement nid98 inserts and a cleared.raid rename), leaving the committed directory = {player.dat(nid1), tutorial(nid2), placement(nid3), promotion(nid4), godmode(nid5), achievement(nid6)} with committed_id=28. This is literally "restore the committed timeline / only committed ticks affect MMR."
Environmental note: under this QEMU (cortex-a72, MMU OFF, SCTLR_EL1=0x00c50838 ⇒ all RAM is Device memory), mount always alignment-faults on an unaligned LDP inside sb_verify (ESR=0x96000021, DFSC=0x21). The firmware cannot actually complete mount in this emulator, so the entire solve was reproduced offline in Python by reimplementing the logic and cross-checking against the firmware via lldb over the QEMU gdbstub.
Crypto (all confirmed byte-exactly against firmware)
- The hash is standard BLAKE2s-256 (
hashlib.blake2s(digest_size=32)). It was first mis-read as BLAKE2b because init stores four 64-bit words = eight 32-bit IV words withh[0] ^= 0x01010020. derive_key(name, ctx, scratch, flagbyte, salt16) = blake2s(name || ctx[0x404]=committed_id(4 LE) || flagbyte(1) || [if flagbyte>0: scratch[0:flagbyte*0x10]] || salt16).- File-decryption key
ctx[0x408] = blake2s("RANKEDFS-COMMITTED" || committed_id(4) || for each present committed inode: nid(4)+f30(4)+f34(4)+rec(0x30)). - Block cipher (
fs_read): per 32-byte block,ks = blake2s(ctx[0x408](32) || node.nid(4) || (byteoff>>5)(4) || "RANKEDFS-BLOCK");plaintext = ciphertext ^ ks. File data lives at disk bytenode.f30 * 0x1000, lengthnode.f34. - Validation: decrypting
/profile/player.datwith this key yields clean textPLAYER=SILVER WOLF / MODE=FINAL LV.999 / HIDDEN_MMR=998 / POLICY=COMMITTED_TICKS_ONLY, confirming the key and committed-directory reconstruction.
The verify puzzle — RAID//9 dataflow VM (verify_part1)
verify reads exactly 48 bytes. verify_part1 runs a graph VM over the four committed replay files in order — tutorial(flagc0), placement(flagc1), promotion(flagc2), godmode(flagc3). A hidden-MMR seed starts at ctx[0x50]=0x3e3 (995); each file must add +1, reaching 0x3e7 (999) after all four.
Each file is a RAID//9 program: header (magic RAID//9, ver=9, nodes@0xa, edges, lanes@0x14, code-window offset f16@0x16, window length f18@0x18, section offsets), a node table (0x20-byte nodes: id, f2, op@4, f5, out_reg@8, in_reg@0xa, imm10@0x10, imm14@0x14, imm18@0x18), an edge list, and a lane-init table.
Opcode remap (16-byte table at sp+0xb0): for flagc==0 it is header[0x30:0x40] directly (a firmware quirk: 0x165c cbz flagc → 0x18b4 memcpy); for flagc>0 it is derive_key("RAID9-MAP", cid=28, flag=flagc, salt=header[0x40:0x50])[0:16] XOR header[0x30:0x40]. Nodes execute in deterministic topological order (ready-set, tie-break f2 desc, f5 desc, id asc). Selector = remap[node.op], must be ≤ 0xc.
Post-remap opcodes 0..12: op0 load u32 from code48[f16+imm10] (the ONLY input entry point), op1 imm, op2 ror/add mix, op3 ror/xor mix, op4 (imm10|1)*reg+imm14, op5 FNV/xxhash-style mul+xorshift, op6 two-register mix, op7/op12 swap, op8 RST (snapshot init_regs=regs, set flag), op9 CHK (xxHash over regfile with 0x9E3779B9/0x7f4a7c15/0x85ebca6b/0xaf286bcb, compared to node.imm14; COMMIT on match, ROLLBACK regs=init_regs & acc=chk_acc on mismatch), op10 acc mix (the +1), op11 set completion flag.
After the VM per file: require the op11 flag; then a TARGET-vector check — regfile must equal laneinit_bytes XOR blake2s-keystream(derive_key("RAID9-TARGET", flag=flagc, salt=header[0x68:0x78])). Then a DROP scratch update blake2s("RAID9-DROP" || flagc || regfile || acc || code48[f16:f16+f18]) fills scratch[flagc*0x10:...], integrity-checked against header[0x60:0x68]; scratch[0x30:0x50] is chained and scratch[0x50:0x54]=acc.
Coverage: tutorial reads code48 words 0-3 (bytes 0-15), placement words 2-7, promotion words 6-11, godmode all 12 — so the 48-byte code is pinned across all four files.
Solution
1. Reconstruct the committed timeline offline
Parse both superblocks (verify FNV checksums), replay the JRNL journal in Python with the exact type1–type7 semantics above, honoring the type5/type6 snapshot/rollback pairs. This yields committed_id=28 and the committed directory of six inodes. Build ctx[0x408] (the file key) as BLAKE2s over "RANKEDFS-COMMITTED" + committed_id + each present inode's (nid,f30,f34,rec).
2. Decrypt files with the BLAKE2s keystream
For each committed file, XOR each 32-byte block with blake2s(filekey || nid || (byteoff>>5) || "RANKEDFS-BLOCK"). Decrypting player.dat gives readable text (sanity check). Decrypt the four .raid replays and achievement.bin.
3. Emulate RAID//9 and solve for the 48-byte code
A faithful Python emulator (emu2.py) of verify_part1 + finalization was validated against firmware ground truth (tutorial's op9 checkpoint matched, acc 995→996, integrity check passed because header[0x60:0x68] stores blake2s(scratch[0:0x10]||header[0x40:0x50])[0:8]).
Solve per file symbolically, chaining scratch concretely between files. z3 with QF_BV was far too slow (44 modular multiplications; >30 min, OOM on bit-blast). Exporting to SMT-LIB2 and solving with bitwuzla — defining ext_rotate_left via bvshl/bvlshr/bvor because z3 emits a variable-rotate form bitwuzla doesn't know natively — solved each file in seconds.
Key correctness fix: op9 must be modeled as a symbolic If (commit vs rollback), NOT forced to match and NOT treated as a no-op. With naive "commit-always"/"no-op" op9 the constraints were UNSAT; the correct solution actually relies on the checkpoint branch. With the If-model, placement became SAT and produced ASCII text.
The recovered 48-byte player code is human-readable ASCII (a filler phrase ending in LV999!!!n0w!); the emulator confirms all four TARGET + integrity checks pass and the final seed reaches 999.
4. Extract the flag (verify_part2 → ChaCha20-Poly1305 AEAD)
verify_part2 reads /cache/achievement.bin (ACHV999 header: ver=1, outlen=0x40, magic32=0x20260999@0xc, ctlen=45@0x10, nonce=hdr[0x14:0x20], tag=hdr[0x20:0x30], ciphertext=hdr[0x40:0x6d]).
The ChaCha20 key is blake2s(code48[0:0x30] || scratch[0x30:0x50] || scratch[0x50:0x54] || scratch[0:0x30] || achv_magic32(4)). RFC 8439 ChaCha20-Poly1305 decryption: the Poly1305 tag verifies (definitive), and the plaintext is the flag.
#!/usr/bin/env python3
# High-level offline solve outline for GODMODE//999.
# Full working scripts live in the task dir: emu2.py, gen_smt.py, solve_bw.py,
# chacha_decrypt.py. This is the end-to-end skeleton.
import struct
from hashlib import blake2s
DISK = open("ranked.img", "rb").read()
def fnv1a(buf):
h = 0x811c9dc5
for b in buf:
h ^= b
h = (h * 0x01000193) & 0xffffffff
h ^= h >> 13
return h & 0xffffffff
def b2s(*chunks):
h = blake2s(digest_size=32)
for c in chunks:
h.update(c)
return h.digest()
# --- 1. Superblocks + committed journal replay -----------------------------
def parse_sb(off):
sb = DISK[off:off+0x3c]
assert sb[:4] == b"RNK9"
committed_gen = struct.unpack_from("<I", sb, 0x14)[0]
return committed_gen
def replay_journal():
# 16 inode slots; apply type1..type7 with snapshot/rollback semantics.
live = {} # nid -> dict(rec,f30,f34,present)
root = None
committed = {}
committed_id = 0
off = 0x2000
while True:
e = DISK[off:off+0x80]
if e[:4] != b"JRNL":
break
typ = struct.unpack_from("<I", e, 4)[0]
seq = struct.unpack_from("<I", e, 8)[0]
cond = struct.unpack_from("<I", e, 0xc)[0]
nid = struct.unpack_from("<I", e, 0x10)[0]
a = struct.unpack_from("<I", e, 0x14)[0]
b = struct.unpack_from("<I", e, 0x18)[0]
pay = e[0x20:0x50]
if typ == 1:
live.setdefault(nid, {})
live[nid].update(rec=pay, present=1)
elif typ == 2:
live[nid]["f30"], live[nid]["f34"] = a, b
elif typ == 3:
live[nid]["rec"] = pay
elif typ == 4:
live[nid]["present"] = 0
elif typ == 5:
replay_journal.marker = cond
root = {k: dict(v) for k, v in live.items()}
elif typ == 6:
if cond == replay_journal.marker and root is not None:
live = {k: dict(v) for k, v in root.items()}
elif typ == 7:
committed = {k: dict(v) for k, v in live.items()}
committed_id = seq
off += 0x80
return committed, committed_id
# --- 2. File key + block decrypt -------------------------------------------
def file_key(committed, committed_id):
parts = [b"RANKEDFS-COMMITTED", struct.pack("<I", committed_id)]
for nid in sorted(n for n, v in committed.items() if v.get("present")):
v = committed[nid]
parts += [struct.pack("<III", nid, v["f30"], v["f34"]), v["rec"]]
return b2s(*parts)
def decrypt_file(fkey, nid, f30, f34):
base = f30 * 0x1000
ct = DISK[base:base+f34]
out = bytearray()
for i in range(0, len(ct), 32):
blk = ct[i:i+32]
ks = b2s(fkey, struct.pack("<II", nid, i >> 5), b"RANKEDFS-BLOCK")
out += bytes(x ^ y for x, y in zip(blk, ks))
return bytes(out)
# --- 3. RAID//9 VM + bitwuzla solve -> code48 (see emu2.py/gen_smt.py) ------
# code48 recovered as printable ASCII; final MMR seed == 999.
# code48 = <RECOVERED_48_BYTE_PLAYER_CODE> # do not embed here
# --- 4. ChaCha20-Poly1305 flag decrypt (see chacha_decrypt.py) -------------
# key = b2s(code48[0:0x30], scratch[0x30:0x50], scratch[0x50:0x54],
# scratch[0:0x30], achv_magic32)
# flag = chacha20_poly1305_decrypt(key, nonce, ct, tag) # Poly1305 verifies
</details>
Auto-tracked: saved to WriteUps; run
/xesor-reviseto fold lessons into XESXor_Methodology.md.
signed by XESXOR