zwesty
Write-ups / Day 22 / Binary
Day 22 · Thu, Oct 22 · Binary

Bridge Troll

Prey slaughtered

Static PE/CLI triage (managed .NET assembly), machine-field labels, CLR metadata root (BSJB), HCFG config record, telling the primary assembly from dependency DLLs, streamed Mastery delivery

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-22-managed-native-bridge (+ /time_trial, /mastery)
Solved
2026-10-22: all three modes CORRECT in the morning, Time Trial and Mastery unranked. Single Solve 09:12:45.2 ET (one batch and one value rejected on the way), Time Trial 09:18:24.4 (first submit incorrect), Mastery 09:21:53.1. 19 min 58.2 s from the first start (09:01:54.9 ET) to the Mastery CORRECT. Ranked that evening: Time Trial 22:41 ET; Mastery 22:57 and 23:56 ET after one ranked try crashed. Mastery finished 1st on the speed board
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 17 answers; the Homebase agent finished the Single Solve and ran unranked Time Trial and Mastery runs; after Binary moved back to Claude later that morning, Claude ran the ranked runs and fixed the plug-in crash

The challenge

"Managed code on one side, native code on the other, and a bridge of P/Invoke and FFI in between. Pay the troll: trace every crossing and map what goes over."

Binary is cumulative: 264 questions, 12 per day for Days 1–22, all on one evidence.zip. Every Day 22 analysis question named its target the same way: "the unique d22-- primary managed assembly whose basename starts d22-- and ends .dll; .runtimeconfig.json and dependency DLL members are delivery support only." The 12 Day 22 fields:

answer_id Question (short) Format
d22_artifact_sha256_1, _2, _3 SHA-256 of d22-- members 1–3, in ZIP central-directory order 64 lowercase hex
d22_analysis_format Classify the primary assembly by magic and container enum: elf, pe, pe_cli, jar, wasm, neko_bytecode
d22_analysis_machine Architecture from the executable-header machine field "label or its equivalent decimal or hexadecimal code"
d22_analysis_metadata_version CLR metadata-root version string at BSJB + 16, trailing NULs removed exact text
d22_analysis_config_campaign, _domain, _mutex, _url First HCFG record in the assembly whose bounds, CRC32, and JSON validate exact text
d22_analysis_config_crc32_hex HCFG header bytes 8..11 0x + 8 lowercase hex
d22_analysis_hcfg_version HCFG header byte 4 base-10 integer

The Time Trial asked the same 264; the Mastery asked them once per set (6 sets, 1,584 answers).

Reading the prompt: what it said we'd need

Clue What it pointed to What was done about it
"Managed code on one side, native code on the other" A .NET assembly, possibly next to native code The primary member is a managed PE (pe_cli) DLL; the question text marked the other .dll as "dependency … delivery support only"
"a bridge of P/Invoke and FFI" Calls from managed code into native libraries None of the 12 questions asked about the P/Invoke calls. They asked for hashes, header fields, the metadata version, and the config
"trace every crossing and map what goes over" Follow data across the boundary In practice: find the HCFG record in the assembly and decode it
"label or its equivalent decimal or hexadecimal code" (question text) Any form of the machine value should pass It didn't. The decimal 332 was rejected twice (Single Solve and Time Trial); the label i386 was accepted

Safety

Static analysis only. Nothing was executed. The DLLs were parsed as bytes (PE and CLI headers, the BSJB metadata root, an HCFG scan); no .NET runtime was started. The archives stayed on our Kali VM and on the Homebase.

What was in the files

  • Single Solve: evidence.zip, 35,671,617 bytes, 28 encrypted members covering Days 1–22. Today's members are d22--sample.dll (18,812 bytes, the primary assembly), d22--sample.runtimeconfig.json (98 bytes), and d22--FSharp.Core.dll (5,726,208 bytes, a dependency).
  • The runtimeconfig hash repeats an older one. d22_artifact_sha256_2 (d6109426…6cf3) is the same digest as Day 3's second member.
  • Header fields: format pe_cli, machine 0x014c (accepted as i386), metadata version v4.0.30319.
  • The HCFG record decoded to a synthetic config (defanged): campaign BR22-000, domain node-000[.]day22[.]example[.]invalid, mutex Global\HuntressBR22_000, URL hxxps://node-000[.]day22[.]example[.]invalid/api/BR22-000, CRC32 0x1704bcdf, version 1.
  • Time Trial: one evidence.zip of about 35.7 MB per attempt. Mastery: six archives of 35.67–35.80 MB. Same template with different values; for example, one Homebase Mastery set's Day 22 config was campaign BR22-002, CRC32 0x84384be2, and its six samples all had machine 0x014c.

Single Solve

The morning solver: 17 sent

The solver started the Single Solve at 09:01:54 and sent one batch of 17 add-on answers (Day 1 ELF fields and Day 19 overlay fields), all accepted. It then reported 17 of 264 covered and left the rest, and the timed modes, to the Homebase. The records don't say why its core batch wasn't sent.

started_at  2026-10-22T13:01:54.867Z   (solver)
deadline    none (Single Solve)
start        651 ms
artifact    4564 ms   (35.7 MB)
solve        719 ms
submit       323 ms   (17 add-on answers -> accepted, remaining 247)
TOTAL       6261 ms

The finish (Homebase agent)

The Homebase reopened the attempt at 09:09:14 and sent its core batch of 189, which included 10 Day 22 fields. It came back incorrect. It then split the batch to find the bad value:

started_at  2026-10-22T13:01:54.867Z   (same attempt, reopened by the Homebase at 13:09:14Z)
submit 1    189 core (incl. 10 d22 fields)          -> incorrect
submit 2    179 core minus the d22 fields           -> accepted, remaining 68
submit 3    3 d22 artifact digests                   -> accepted, remaining 65
submit 4    6 d22 HCFG fields                        -> accepted, remaining 59
submit 5    d22_analysis_machine 332                 -> incorrect
submit 6    d22_analysis_machine i386                -> accepted, remaining 58
submit 7    58 add-on answers (reopened 13:12:31Z)   -> CORRECT
result      CORRECT   flag c5130ff3…372d   (server stamp 13:12:45.191Z)

Rejected value: d22_analysis_machine 332 (the decimal of 0x014c). The Homebase ranked i386 (the PE parser's label) first and x86 second; the first was accepted. Final coverage was 264 of 264.

Time Trial

Homebase, unranked (2026-10-22 morning)

CORRECT, not ranked, on the third submit of one attempt. The first full submit still carried 332 for the machine field and came back incorrect; i386 was then sent alone and accepted, and the remaining 263 answers went through.

started_at  2026-10-22T13:16:54.902Z
deadline    none (unranked)
start        245 ms after started_at
artifact   11450 ms
solve      13147 ms
submit 1   13555 ms after started_at   264 answers -> incorrect
submit 2   13:18:15Z                   d22_analysis_machine i386 -> accepted, remaining 263
submit 3   13:18:25Z                   263 answers -> CORRECT
result      CORRECT   flag b476addb…b582   (server stamp 13:18:24.395Z)

Claude, ranked (2026-10-22 evening)

The first ranked run after Binary moved back to Claude. CORRECT on the first submit (80 s clock).

started_at  2026-10-23T02:41:49.514Z
deadline    2026-10-23T02:43:09.514Z   (80 s)
answers     264
retry       +185 ms
artifact    +2388 ms   (35.7 MB)
solve       +3093 ms
submit      +5010 ms after started_at
result      CORRECT   flag b476addb…b582

Board: 2nd at 3.453 s. Not rerun. Final board on 2026-11-01: 4th of 65, 3.453 s.

Mastery

Homebase, unranked (2026-10-22 morning)

CORRECT, not ranked, in one attempt. The first pass (started 13:18:32) stopped before submitting because its per-set format and architecture fields weren't covered yet. Once they were covered (1,584 of 1,584), a second start at 13:21:22 reopened the same attempt and the full set went through.

started_at  2026-10-22T13:18:32.115Z
deadline    none (unranked)
answers     1,584 (6 sets × 264)
start      170537 ms after started_at   (second start, same attempt)
artifact   189332 ms   (6 × ~35.7 MB)
solve      200133 ms
submit     201993 ms after started_at
result      CORRECT   flag d3aa449e…cf07   (server stamp 13:21:53.082Z)

Claude, ranked (2026-10-22 evening)

First ranked try: crashed, nothing sent. It was started at 02:42:04.797Z (400 s clock) with streamed delivery, which hands the solver one archive per delivery. The Day 21 plug-in picked its archive by absolute set number and broke. A streamed replay of all 39 saved Binary Masteries found the same bug in the Day 20 and Day 22 plug-ins; the earlier checks had only run whole solves, so they missed it. The attempt expired: 1 of 16 ranked Mastery tries lost. The fix (when given one archive, use it) replayed 39/39 clean and went in at 22:50 ET. Without it, the next morning's solver would have crashed the same way.

Then two ranked runs, both CORRECT on the first submit:

run         1                   2
started_at  02:57:02.910Z       03:56:41.774Z      (2026-10-23 UTC)
deadline    +400 s              +400 s
answers     1,584               1,584
retry       +834 ms             +311 ms
artifact    +6957 ms            +4035 ms           (6 archives streamed, last one in)
solve       +7706 ms            +4716 ms
submit      +9017 ms            +5792 ms           after started_at
result      CORRECT             CORRECT            flag d3aa449e…cf07

Board: 2nd at 8.118 s after the first run (1st was 7.664 s). After the second run: 1st at 5.111 s. Final board on 2026-11-01: 1st of 59, 5.111 s.

Time to solve

All three modes were solved by 09:21:53.1 ET, 21 min 53.1 s after release (19 min 58.2 s from the first start request); the machine value 332 was rejected once in the Single Solve and once in the Time Trial before i386 went through. The morning Time Trial and Mastery were unranked; that evening Claude's ranked Time Trial placed 2nd (4th by the end of the event) and the ranked Mastery, after one crashed try, took 1st on the board at 5.111 s and held it to the end.

Takeaways

  • "Decimal is accepted" wasn't true for this field. The Game Master rejected 332 and took i386 for a managed PE's machine field, in both the Single Solve and the Time Trial. Day 20's ELF machine took the decimal 62. Prefer the tool label that was accepted before; Day 23's Trust Issues hit the same field and needed the same fix.
  • Splitting a rejected batch finds the bad value fast. Sending the inherited core, then each new group of Day 22 fields, isolated the one wrong answer in five submits.
  • A rule learned in one mode has to reach the others. The Time Trial's first submit repeated the same 332 the Single Solve had just had rejected.
  • Checks must replay the streamed path. Whole-solve checks passed, but streamed delivery broke three plug-ins; a streamed replay would have caught it before a ranked try was lost.
  • The prompt's theme didn't change the questions. "P/Invoke and FFI" sounded like call-tracing; the 12 fields were header, metadata, and config reads.