zwesty
Write-ups / Day 21 / Binary
Day 21 · Wed, Oct 21 · Binary

Shared Custody

Prey slaughtered

Static ELF triage of a loader and its shared-library plug-in, e_machine as a number, HCFG config records with CRC32 found by scanning for the magic, cumulative question sets

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-21-shared-library-boundaries (+ /time_trial, /mastery)
Solved
2026-10-21: all three modes CORRECT in the morning, Time Trial and Mastery unranked. Single Solve 09:08:29.4 ET, Time Trial 09:08:52.2, Mastery 09:11:27.1. 8 min 8.2 s from the first start (09:03:18.9 ET) to the Mastery CORRECT. First ranked Time Trial and Mastery on 2026-10-22 from 23:17 ET (five ranked runs, all CORRECT)
Player
Zwesty (solo), with the Homebase agent (GPT-6 Luna) and Claude Code (Opus 5.5). Binary was the Homebase's lane on Days 20–22. Our automated morning solver (a scheduled script on our Kali VM) started the Single Solve and sent the 246 answers it could cover; the Homebase agent wrote a new plug-in, sent the last 6, and ran unranked Time Trial and Mastery runs; Claude ran the ranked runs the next evening

The challenge

"No binary is an island — this one leans on shared libraries for everything. Trace the exports, the imports, and the loader's handshake, and mind the boundaries."

Binary is cumulative: 252 questions, 12 per day for Days 1–21, all on one evidence.zip. The 12 Day 21 fields:

answer_id Question (short) Format
d21_artifact_sha256_1, _2 SHA-256 of d21-- members 1 and 2, in ZIP central-directory order 64 lowercase hex
d21_analysis_loader_machine e_machine of the member ending loader.elf base-10 integer, "do not substitute an architecture label"
d21_analysis_plugin_machine e_machine of the member ending plugin.cmxs base-10 integer, same rule
d21_analysis_plugin_config_campaign, _domain, _mutex, _url HCFG record in the plugin.cmxs member: scan for the magic, take the first record whose bounds, CRC32, and JSON validate exact text
d21_analysis_config_campaign, _domain, _mutex, _url The same record, worded as "the d21-- plugin.cmxs member; the loader has no scored HCFG config" exact text

The Time Trial asked the same 252; the Mastery asked them once per set (5 sets, 1,260 answers).

Reading the prompt: what it said we'd need

Clue What it pointed to What was done about it
"leans on shared libraries" A main program plus a library it loads Two Day 21 members: a loader.elf and a plugin.cmxs (an OCaml native plug-in, itself an ELF)
"Trace the exports, the imports, and the loader's handshake" Symbol and dynamic-linking work None of the 12 questions asked about exports or imports. They asked for hashes, e_machine, and the plug-in's config
"mind the boundaries" Which file a question is about The config lives in the plug-in only; the question says outright that "the loader has no scored HCFG config"
Two sets of config fields (question text) A plain d21_analysis_config_* set and a d21_analysis_plugin_config_* set Both point at the same record, so both sets got the same four values

Safety

Static analysis only. Nothing was executed. The Homebase agent's notes record that no sample was run and that its plug-in only parses the assigned archive bytes. Claude's ranked runs used the same static solver. The archives stayed on our Kali VM and on the Homebase.

What was in the files

  • Single Solve: evidence.zip, 33,477,071 bytes, 25 encrypted members covering Days 1–21. Today's members are d21--variant-000-loader.elf (780,008 bytes) and d21--variant-000-plugin.cmxs (9,120 bytes).
  • Both members have e_machine 62 (x86-64), sent as the number as the question asked.
  • The HCFG record in the plug-in decoded to a synthetic config (defanged): campaign BR21-000, domain node-000[.]day21[.]example[.]invalid, mutex Global\HuntressBR21_000, URL hxxps://node-000[.]day21[.]example[.]invalid/api/BR21-000. Both config field sets took these values.
  • Day 20's two RS20 fields came back with the same values as on Day 20 (380 and 1200888).
  • Time Trial: one evidence.zip of 33.48–33.58 MB per attempt. Mastery: five archives of 33.48–33.58 MB each. Same 252 questions with different values; each setN. question reads the matching delivered archive by the same rule.

Single Solve

The morning solver: 246 sent, 6 open

Our scheduled run started late that morning (a leftover login session held its lock until a catch-up started it at 09:01:40). The solver started the Single Solve at 09:03:18 and sent two batches, both accepted. It then reported 246 of 252 covered and left the rest, and the timed modes, to the Homebase.

started_at  2026-10-21T13:03:18.894Z   (solver)
deadline    none (Single Solve)
submit 1    205 core answers      -> accepted, remaining 47   (13:03:21Z)
submit 2    41 add-on answers     -> accepted, remaining 6    (13:03:22Z)
result      not CORRECT: 6 fields uncovered, nothing rejected; timed modes not started

The six open fields were the four plain d21_analysis_config_* fields and the two Day 20 RS20 fields.

The plug-in and the finish (Homebase agent)

The Homebase agent wrote a new plug-in, a static reader for the HCFG record in the unique plugin.cmxs member, answering the four plain d21_analysis_config_* fields. Its Day 20 RS20 reader answered the two RS20 fields. Coverage came to 252 of 252. The Homebase sent the six into the solver's open attempt. No value was rejected.

started_at  2026-10-21T13:03:18.894Z   (same attempt)
submit 3    4 d21 config answers           -> accepted, remaining 2   (13:08:28Z)
submit 4    d20 RS20 fields 380, 1200888   -> CORRECT                 (server stamp 13:08:29.400Z)
flag        not recorded (the final reply wasn't kept)

Time Trial

Homebase, unranked (2026-10-21)

CORRECT on the first attempt, not ranked.

started_at  2026-10-21T13:08:44.942Z
deadline    none (unranked)
answers     252
start        108 ms after started_at
artifact    5568 ms
solve       7150 ms
submit      7960 ms after started_at
result      CORRECT   flag 3ab46add…ead9   (server stamp 13:08:52.234Z)

Claude, ranked (2026-10-22 evening)

Three ranked runs (80 s clock), all CORRECT on the first submit.

run         1                   2                   3
started_at  03:17:11.621Z       03:36:40.222Z       03:40:20.468Z      (2026-10-23 UTC)
answers     252                 252                 252
retry       +170 ms             +168 ms             +163 ms
artifact    +2869 ms            +3172 ms            +1933 ms           (33.5–33.6 MB)
solve       +3605 ms            +3860 ms            +2670 ms
submit      +4494 ms            +4619 ms            +3499 ms           after started_at
result      CORRECT             CORRECT             CORRECT            flag 3ab46add…ead9

Board: 9th at 3.920 s after the first run; 6th at 2.940 s after the other two. Final board on 2026-11-01: 7th of 63, 2.940 s.

Mastery

Homebase, unranked (2026-10-21)

CORRECT, not ranked, in one attempt. The attempt was started at 13:09:07 and started again at 13:11:01, which reopened the same attempt; the records don't say why the first run didn't submit. The timings below are from the second run.

started_at  2026-10-21T13:09:07.646Z
deadline    none (unranked)
answers     1,260 (5 sets × 252)
start      114267 ms after started_at   (second start, same attempt)
artifact   131373 ms   (5 × ~33.5 MB)
solve      139160 ms
submit     140347 ms after started_at
result      CORRECT   flag ae10b9ab…b9fc   (server stamp 13:11:27.117Z)

Claude, ranked (2026-10-22 evening)

Two ranked runs (325 s clock), with the archives streamed, both CORRECT on the first submit.

run         1                   2
started_at  03:17:26.780Z       03:37:08.287Z      (2026-10-23 UTC)
answers     1,260               1,260
retry       +327 ms             +280 ms
artifact    +3991 ms            +4060 ms           (5 archives streamed, last one in)
solve       +4650 ms            +4763 ms
submit      +5799 ms            +5649 ms           after started_at
result      CORRECT             CORRECT            flag ae10b9ab…b9fc

Board: 4th at 4.998 s after the first run; unchanged after the second. Final board on 2026-11-01: 4th of 57, 4.998 s.

Time to solve

All three modes were solved by 09:11:27.1 ET, 11 min 27.1 s after release (8 min 8.2 s from the first start request, which came late because our scheduled run started late); no value was rejected in any mode. The morning Time Trial and Mastery were unranked; the ranked times came from Claude's runs on the evening of 2026-10-22 (Time Trial 2.940 s, Mastery 4.998 s on the board).

Takeaways

  • The prompt's theme didn't match the questions. "Exports, imports, the loader's handshake" sounded like symbol and linking work; the 12 fields were hashes, e_machine, and a config record.
  • Two field names, one answer. The plain config_* set and the plugin_config_* set pointed at the same record. The question text said so ("the loader has no scored HCFG config"); reading it, not the field name, decided which member to parse.
  • This e_machine wanted a number. Day 21 said "do not substitute an architecture label", so 62 was right. On Day 22 the opposite happened (a label was needed), so the field's own wording decides.
  • Old days come back. Day 20's RS20 fields reappeared unchanged; a day >= 20 plug-in covered them.
  • The late start cost little on this lane. Binary's Single Solve still had to wait for the Homebase's new plug-in.