zwesty
Write-ups / Day 31 / Binary
Day 31 · Sat, Oct 31 · Binary

Final Boss

Prey slaughtered

Static ELF triage, carving a config record by magic from the end of a member, the HCFG-style 12-byte header (version, XOR key, length, CRC32), XOR decoding, JSON paths read from the question text, plug-ins written for day >= N

STTMClaude Homebase Both

Flags are shortened to their first 8 and last 4 characters because they're unique per team.

Challenge ID
binary-reverse-engineering-day-31-reversing-gauntlet (+ /time_trial, /mastery)
Solved
2026-10-31: all three modes CORRECT, both timed modes ranked. Our automated morning solver (a scheduled script on our Kali VM) started the Single at 09:03:47.1 ET, sent 423 of 434 answers (all accepted), and left 11 open, so it didn't start the timed modes. A Claude helper agent wrote one new plug-in. Single CORRECT at 09:08:12.9, ranked Time Trial at 09:08:31.7, ranked Mastery at 09:08:46.8 (all solved_by.at). 4 min 59.7 s from the solver's first start to the Mastery CORRECT; 8 min 46.8 s from the 9 AM release. No value was rejected
Player
Zwesty (solo), with Claude Code (Opus 5.5): the automated solver answered 423 questions and stopped; a Claude helper agent wrote the Day 31 plug-in, sent the 11, ran both timed modes, and added the plug-in to the solver

The challenge

The gauntlet: everything you've learned, across platforms and languages, in one campaign. Reconstruct it all, end to end, offline. No hints. Good luck.

Binary is cumulative: 434 questions, 14 per day for Days 1–31, all on one evidence.zip. Day 31 added 14 ids on one new member, d31--sample.elf. The solver's core answered the member's SHA-256, format (elf), and machine (62). The other 11 were new:

answer_id Question (shape) Format
d31_analysis_config_(campaign\|domain\|mutex\|url) "Locate the unique CRC32-valid HGAU record by scanning backward from the end of the ELF member … decoded HGAU JSON property config.<key>" exact text, no quotes
d31_analysis_gauntlet_config_(campaign\|domain\|mutex\|url) The same record, property gauntlet.config.<key> exact text, no quotes
d31_analysis_gauntlet_crc32_hex "Read the little-endian uint32 at header bytes 8..11 and render it as 0x plus exactly" eight hex digits 0x + 8 lowercase hex
d31_analysis_xor_key_hex "Render header byte 5 as 0x plus exactly two lowercase hexadecimal digits" 0x + 2 lowercase hex
d31_analysis_gauntlet_trace_branch "Submit the exact gauntlet.trace_branch token" enum: success, decoy, failure

Every question says the HGAU record uses "the same 12-byte layout as HCFG". The Time Trial asked the same 434; the Mastery asked them once per delivery (15 deliveries, 6,510 answers).

Reading the prompt: what it said we'd need

Clue What it pointed to What I did about it
"everything you've learned … in one campaign" All 31 days' fields at once The solver's core and plug-ins already covered Days 1–30 and the three plain Day 31 fields: 423 accepted
"across platforms and languages" The mixed formats of earlier days (ELF, PE, .NET, JAR, WASM, NEKO) Nothing new needed: today's one member is an ELF64 x86-64 (e_machine 62)
"Reconstruct it all, end to end" The decoded config record, read like the earlier HCFG and HGRF ones One new plug-in reads the HGAU record and answers the 11 open ids
"offline. No hints." Static work only Bytes only: nothing was run, which the lane needs anyway
"Final Boss" (title) The last day, so the hardest version of the lane It turned out to be a known layout with a new magic. The new parts were in the question wording, not the bytes

Safety

Static analysis only. Nothing was executed. The ELF member was read as bytes: header fields, then a backward scan for the HGAU record, its CRC32 check, and the XOR decode. No sample was run, loaded, or debugged. For this write-up only the helper's notes, the plug-in's docstring, the solver's report and run logs, the saved answer records, and the Game Master request log were read. The evidence archives stayed on our Kali VM. Indicators below are defanged (they are all example[.]invalid names anyway).

What was in the files

  • Single Solve: 01-evidence.zip (36,185,026 bytes on disk). The Day 31 member is one ELF, d31--sample.elf, with an HGAU record near its end. The header is the HCFG layout with magic HGAU: version 1, XOR key at byte 5, a u16 LE length, a u32 LE CRC32 at bytes 8..11, then the XOR-encoded UTF-8 JSON. Exactly one record in the member passes its CRC32.
  • The decoded JSON (schema huntress.binary.reversing_gauntlet.v1) has keys languages, capabilities, prior_anchors, config{…}, and trace{branch, …}. The Single's accepted values were:
  • campaign BR31-000, domain gauntlet-000[.]day31[.]example[.]invalid, mutex Global\HuntressBR31-000, URL hxxps://gauntlet-000[.]day31[.]example[.]invalid/final/BR31-000
  • CRC32 0x5ed06133, XOR key 0xa1, trace branch success
  • Two wording traps, both in the question text. The JSON root has no gauntlet key: gauntlet. names the root, as graph. did on Day 30, so gauntlet.config.campaign is the same value as config.campaign. And there is no trace_branch key: the value is at trace.branch.
  • Time Trial: a fresh evidence.zip (17,167,079 bytes) with the same 434 fields.
  • Mastery: 15 streamed deliveries of about 17.1–17.2 MB each. The variants differ per set (campaign, key, CRC, URLs). The trace branch was success on the sets seen; whether every set was checked wasn't recorded.

Single Solve

The automated solver: 423 sent, 11 open

started_at  09:03:47.1 ET   (started by the solver)
deadline    none (Single Solve)
answers     256 core (accepted, 09:03:51), then 167 plug-in answers (accepted, 09:03:52)
result      not CORRECT: 11 questions unanswered, no rejected values; timed modes not started

The solver's report shows the older Day 28 plug-in reading the gauntlet_config_* ids as a role field named "gauntlet" and printing "left open (a role field without a basename rule)". The other 7 open ids were left open silently. Leaving them open was right: nothing was guessed.

The plug-in (Claude helper agent)

The helper wrote a new plug-in for days ≥ 31 and tested it on a staging copy of the solver: - Magic and member kind come from the question text. It parses "CRC32-valid <MAGIC> record by scanning backward from the end of the <KIND> member" and takes the last CRC32-valid record in file order. - JSON path from the text. A root name that isn't a key (gauntlet., graph.) is dropped. When a_b is missing, it is read as a.b (so trace_branch becomes trace.branch). - Header fields rendered as the text asks: 0x plus a zero-padded width taken from the wording ("two", "eight"). - Enum answers are checked against the question's accepted_values. - Archive choice as on earlier days (one archive, sets, or delivery N).

Staged check: coverage 434/434, every already-accepted answer reproduced (0 mismatches), and no conflicts between plug-ins.

The answers: - config_* and gauntlet_config_*: the decoded JSON string as stored (no form choice). - Mutex, the one judgement call: 1) the decoded string with one backslash (the form accepted for every HCFG and HGRF mutex so far); 2) the JSON-escaped text with two backslashes, only if 1 was rejected. - CRC32 and XOR key: only one form each. Trace branch: the trace.branch value, the only one of the three enum values present.

The helper then reconnected to the solver's open attempt:

reconnected to the solver's attempt 09:08:07 ET
answers     11 (the plug-in's)
start        394 ms
artifact    2024 ms
solve       1967 ms
submit      2169 ms
TOTAL       6559 ms
result      CORRECT   flag ae14a627…20e7   (09:08:12.9 ET, solved_by.at 13:08:12.857Z)

The one-backslash mutex was accepted. The second form was never sent.

Time Trial

CORRECT on the first attempt, ranked (fresh variant). The run used the staging copy, before the plug-in was added to the live solver.

started_at  09:08:29.6 ET   deadline 09:09:49.6 (80 s clock)
answers     434
start        764 ms
artifact     900 ms   (17,167,079 bytes)
solve        770 ms
submit       738 ms
TOTAL       3173 ms   (submit +2,573 ms after started_at)
result      CORRECT   flag b1f1d62a…6f38   (09:08:31.7 ET)

The solver's fast path had no stored shortcut for this profile, so this was a full solve, as it has been since Day 24.

Mastery

CORRECT on the first attempt, ranked (streamed).

started_at  09:08:39.6 ET   deadline 09:20:34.6 (11 min 55 s clock)
answers     6,510 (15 deliveries × 434)
start       1442 ms
downloads   15 streamed, the last landing +3,827 ms after started_at
solve       2979 ms
submit      1387 ms
TOTAL       9131 ms   (submit +8,203 ms after started_at)
result      CORRECT   flag 91113e36…d0c2   (09:08:46.8 ET)

Install

At about 09:09, after the three CORRECTs, one new file, the plug-in, went into the live solver. No existing file changed, so removing the file rolls it back. Live coverage after: 434/434. Unlike Day 28's plug-in, no unit-test file or regression rehearsal was recorded for this one.

Time to solve

All three modes were solved by 09:08:46.8 ET, 8 min 46.8 s after release and 4 min 59.7 s after the solver's first start; the Single took 4 min 25.8 s from that start, most of it writing the plug-in. Both timed modes were ranked and went through on the first submit (2.05 s and 7.26 s), with no rejected value.

Takeaways

  • The "final boss" was a known layout with a new magic. HGAU is HCFG's 12-byte header; most of the work was reading the question text, not the bytes.
  • A dotted path's first name isn't always a key. gauntlet. named the root, as graph. had on Day 30. Dropping a root name that isn't a key is now a standing rule in the plug-in.
  • An answer-id spelling can hide nesting. trace_branch lived at trace.branch. Trying a.b only when a_b is missing kept the rule safe.
  • Leaving fields open beats guessing, again. The solver sent the 423 it was sure of and stopped on 11 with no rule.
  • The stored form goes first. The one-backslash mutex had been accepted all event, so it led the list and was right.
  • A new file is the cheapest install. No existing plug-in changed, so rollback is just deleting one file.