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

Fraternal Twins

Prey slaughtered

Static ELF triage (format by magic, e_machine); HCFG config records found by a backward scan and checked by CRC32; proving two builds share one config; per-role fields picked by basename

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-29-variant-diffing (+ /time_trial, /mastery)
Solved
2026-10-29: all three modes CORRECT, both timed modes ranked, each timed mode on its first submit. Single 09:04:49.1 ET (two submits: core, then plug-ins), Time Trial 09:05:00.9, Mastery 09:05:17.1: 35.4 s from the Single's start (09:04:41.7) to the Mastery CORRECT, 5 min 17.1 s after release
Player
Zwesty (solo), with Claude Code (Opus 5.5): our automated morning solver (a scheduled script on our Kali VM) solved all three modes on its own with our existing Binary solver and its plug-ins. No one worked this challenge by hand, and no code was changed

The challenge

"Two binaries, same source, different compiler moods. Diff the variants, account for every optimization, and prove they're semantically the same program."

Binary is cumulative: 406 questions, 14 per day for Days 1–29, all on one evidence.zip. The 14 for Day 29:

answer_id Question (shape) Format
d29_artifact_sha256_1, _2 SHA-256 of member 1 / 2 of the d29-- members in ZIP central-directory order 64 lowercase hex
d29_analysis_(optimized\|unoptimized)_format "Use the unique d29-- member whose basename ends O3.elf" (or O0.elf); classify by magic and parsed container enum (elf, pe, pe_cli, jar, wasm, neko_bytecode)
d29_analysis_(optimized\|unoptimized)_machine The same member: "Read the ELF e_machine field and submit its unsigned base-10 numeric value" integer
d29_analysis_config_(campaign\|domain\|mutex\|url) "Decode both d29-- role members and require their config objects to be byte-for-byte equal before using the common value"; scan backward for HCFG, first CRC32-valid record exact text
d29_analysis_optimized_config_(campaign\|domain\|mutex\|url) The O3.elf build alone: scan backward from the end for HCFG, first CRC32-valid decoded JSON record exact text

The Time Trial asked the same 406; the Mastery asked them once per set (13 sets, 5,278 answers).

Reading the prompt: what it said we'd need

Clue What it pointed to What the solver did about it
"Two binaries, same source" A pair of builds of one program d29--sample-O0.elf and d29--sample-O3.elf
"different compiler moods" Optimization levels: -O0 and -O3 The roles unoptimized (O0.elf) and optimized (O3.elf), picked by basename, as on Day 27's gcc/tcc pair
"Diff the variants, account for every optimization" Comparing the builds No question asked for a code diff. The fields were format, machine, and config per build
"prove they're semantically the same program" Show the two builds carry the same thing The common-config fields require both builds' decoded configs to be byte-for-byte equal before the value is used
"Fraternal Twins" (title) Same parents, not identical Two builds, same config, different code

Safety

Static analysis only. Nothing was executed. The solver reads ELF headers and HCFG records as bytes. For this write-up only the saved replies, the solver's output, the archive listing (names and sizes), and the solver source were read. The archives stayed on our Kali VM. Indicators below are defanged.

What was in the files

  • Single Solve: evidence.zip, 35,956,121 bytes, 42 members. Day 29's: d29--sample-O0.elf and d29--sample-O3.elf, 43,931 bytes each.
  • Time Trial: evidence.zip, 17,042,042 bytes, the same 406 fields. The solver's output shows the O3.elf build's config for this variant: campaign BR29-498F5621, domain gate-f782745a[.]day29[.]example[.]invalid, a Global\Huntress… mutex, and a sync-… URL under the same domain.
  • Mastery: 13 archives, each an evidence.zip of about 16.9–17.0 MB, delivered as separate downloads.

Single Solve: the automated solver

The solver sent its answers in two batches: the core Binary solver's, then the plug-ins'.

started_at  09:04:41.7 ET   deadline none
submit 1    core: 250 answers              -> accepted (156 remaining)
submit 2    plug-ins merged: 156 answers   -> CORRECT
start        422 ms
artifact    3494 ms   (35,956,121 bytes)
solve       2050 ms
submit      2331 ms   (core 362 ms, plug-ins 1,966 ms)
TOTAL       8298 ms
result      CORRECT   flag 8d1aacb0…2650   (09:04:49.1 ET)

Why no change was needed. Every Day 29 field had an owner before the day began: - The core answered six: the two member SHA-256s and the four common-config fields. The core already decoded one shared config from both builds of a pair, as it had for the Day 24 pair. - Our Day 27 plug-in answered the other eight. It was written for Day 27's gcc/tcc builds, but it takes any single-word role and finds the member by the basename ending the question names. So optimized → O3.elf and unoptimized → O0.elf needed nothing new. Its config rule, "scan backward from the end for HCFG, first CRC32-valid record", is the procedure the Day 29 questions spell out. The Day 28 plug-in leaves single-word roles to it, so the two didn't compete.

Time Trial

CORRECT on the first attempt, ranked (fresh variant).

started_at  09:04:57.1 ET   deadline 09:06:17.1 (80 s clock)
answers     406
start       1420 ms
artifact    1949 ms   (17,042,042 bytes)
solve        747 ms
submit      1840 ms
TOTAL       5957 ms   (submit +4,927 ms after started_at)
result      CORRECT   flag a39b91f8…24c7   (09:05:00.9 ET)

Mastery

CORRECT on the first attempt, ranked (fresh variant). The 13 archives were downloaded in parallel; the last arrived 3,617 ms after started_at.

started_at  09:05:09.5 ET   deadline 09:15:29.5 (10 min 20 s clock)
answers     5,278 (13 sets × 406)
downloads   13, done +3,617 ms after started_at
solve       3615 ms
submit      1348 ms
TOTAL       9828 ms   (submit +8,589 ms after started_at)
result      CORRECT   flag 8ed5e6ce…6531   (09:05:17.1 ET)

Speed boards (final boards; time = server start to solve): - Time Trial: 10th of 50, 3.790 s (fastest 1.463 s). - Mastery: 2nd of 47, 7.526 s (fastest 6.410 s).

Time to solve

The Single was CORRECT at 09:04:49.1 ET, 7.4 s after its start; the ranked Time Trial (09:05:00.9) and ranked Mastery (09:05:17.1) each went through on the first submit, 35.4 s after the Single's start and 5 min 17.1 s after release.

Takeaways

  • Write plug-ins for the shape, not the day. Our Day 27 role-builds plug-in keyed on "a single-word role, found by basename", so Day 29's optimized / unoptimized pair was covered two days before it appeared.
  • "Prove they're the same" was a config check. The question text defined it: decode both builds' HCFG records and require byte-for-byte equality before answering.
  • The prompt's diffing story wasn't asked. No field asked about optimizations or code differences; format, machine, and config were enough.
  • Clear ownership kept it fast. Core and plug-ins each took their own fields, and the Mastery placed 2nd of 47.