The challenge
A .NET binary is a database wearing a trench coat. Read the CLI metadata tables (methods, strings, signatures) and find where the configuration is stashed.
ilspycmdandstringsare your friends.
This is the first compounding Binary day. One evidence.zip (AES, password infected) holds Day 1's C ELF, Day 2's C++ ELF, and the new Day 3 .NET DLL with its runtimeconfig.json, and the questions cover all three. 24 questions in the Single Solve and Time Trial, 48 in Mastery (two sets, set1. / set2.). Every one is normalised exact; the exact-text ones say "use the literal none only when the requested field is absent".
| Group | answer_id |
Question | Format |
|---|---|---|---|
| d01, the Day 1 C ELF (8) | d01_ |
The file's SHA-256 | Exact text |
d01_ / machine / elf_ / endianness |
The ELF header fields | Exact text | |
d01_ |
The section names | Compact JSON array, exact values and order | |
d01_ |
The symbols | Compact JSON array, exact values and order | |
d01_ |
Disassemble the recover_ FUNC symbol; [] if it's absent |
JSON array of instruction strings (Intel or AT&T) | |
| d02, the Day 2 C++ ELF (8) | d02_ |
The file's SHA-256 | Exact text |
d02_ |
"What is the exact analysis format value…" (no other hint) | Exact text | |
d02_ / domain / mutex / url |
The embedded config's fields | Exact text | |
d02_, d02_ |
The config record's CRC32 and XOR key | Lowercase hex with 0x |
|
| d03, the new .NET binary (8) | d03_ / _ |
The two files' SHA-256s | Exact text |
d03_ |
"its combined executable-container and managed-runtime format classification… one compound token… not just the generic container label" | One container_ token; CLI, CLR, or .NET for the managed part; separators and case are free |
|
d03_ |
The machine | Exact text | |
d03_ |
The metadata version | Exact text | |
d03_ / domain / mutex |
The embedded config's fields | Exact text |
Reading the prompt: what it said we'd need
| Clue | What it pointed to | What I did about it |
|---|---|---|
| "A .NET binary is a database wearing a trench coat" | A PE file (the coat) wrapped around CLI metadata tables (the database) | Read the PE header for the machine, then the CLI header and the metadata root |
| "Read the CLI metadata tables: methods, strings, signatures" | The metadata streams: the tables, #Strings, #US (user strings), and #Blob |
Found the BSJB metadata root and read its version string straight from the bytes |
| "find where the configuration is stashed" | A config held in the metadata, not in a resource file | Decompiled, found the field that holds it, then followed the field to its initial data |
"ilspycmd and strings are your friends" |
Decompile for the logic; strings - for the UTF-16 #US heap |
Installed ilspycmd 7.2.1 (the last build for .NET 6) on our Kali VM |
| d03 format: "one compound token containing both parts, not just the generic container label" | Container + runtime, e.g. PE + CLI | Tried the most specific container name first (PE32), then the generic one (PE) |
| The d01 and d02 groups | Earlier days' files, byte-identical | Carried over the accepted ELF Aware and Mangled answers by SHA-256 |
Safety
Static analysis only. No binary was ever executed. The evidence was opened with 7z -pinfected inside our Kali VM (and, for the Homebase agent's runs, inside the Homebase), and nothing was copied out. The ELFs were read with readelf and objdump. The .NET DLL was read as bytes and decompiled with ilspycmd, which reads IL; it doesn't run it. Its behaviour, and the JSON trace it would print, come from the decompiled source. The timed runs used the same kind of static readers (see the Time Trial and Mastery sections).
What was in the files
The Single Solve's evidence.zip (10,238 B, SHA-256 9d7a0aee…d777, zip comment huntress:day03-single) held 4 files:
| File | Size | What it is |
|---|---|---|
d01- |
15,616 B | Day 1's C ELF: . = BR01-, recover_ at 0x401126, entry 0x401040, 28 sections |
d02- |
16,640 B | Day 2's C++ ELF (SHA-256 e9ca3054…d0d7, byte-identical to Mangled's): a DiscoveryEngine class and an HCFG config record in a . section |
d03- |
9,216 B | The new one: a .NET 8 console assembly, PE32, machine 0x14c |
d03- |
98 B | The .NET host's config, naming net8. |
The .NET DLL:
- PE32, machine 0x14c (i386). That's normal for an "Any CPU" .NET assembly: the PE header is x86, and the IL inside runs on any CPU.
- The metadata root (BSJB magic) is at 0x688, with version v4.0.30319. Even a .NET 8 assembly carries that string. The runtime version people expect (net8.0) lives in the runtimeconfig.json, not in the metadata.
- Decompiled, it's namespace Huntress.Binary.Day03. ManagedHttpPlan.Evaluate(model) returns success/7 for workstation, decoy/3 for analysis, and failure/1 otherwise. The model comes from the HUNTRESS_HOST_MODEL variable, defaulting to workstation. The program would print a fixed JSON trace (huntress.binary.dynamic_trace.v1, application_layer_protocol_configuration, sinkhole_only).
- Where the config is stashed: Program.Config is a 379-byte array set from a field's initial data (a FieldRVA blob at file offset 0x1898). That's how the C# compiler stores a constant byte array: the bytes sit in the file, and a metadata table row points the field at them. The blob is an HCFG record, the same layout as Day 2's: the magic HCFG, a version byte, a one-byte XOR key (0x41 here), a length (367), a CRC32, then the XOR'd JSON. The JSON holds campaign, domain, ipv4, url, mutex, windows_path, linux_path, service, and host_model. Only campaign, domain, and mutex are asked.
The timed variants used the same template: each delivery carried the same three days' files. Mastery set 2 was different: its d02 was a native Windows PE (.exe, MinGW C++), not an ELF. (The timed runs' per-variant d03 values aren't recorded in this write-up.)
Single Solve: CORRECT (17 by Claude, 6 by the Homebase agent, the last after two days)
Claude's part, 2026-10-07, 15:15:51 → 15:17:58 (17 of 24 accepted in 6 submits):
| Submit | Fields | Result |
|---|---|---|
| 1 | d01 SHA-256, ELF64, x86-, ET_, little, disassembly ["mov eax,0x1fa4","ret"] |
accepted, 18 left |
| 2 | d01 section names: without section 0 ✗, then with "" first ✓. d01 symbols: ["recover_ ✗, then every named . entry in table order ✓ (crt1., _, variant-, marker, …) |
2 accepted, 16 left |
| 3 | d03 sha256_ (the .), sha256_ (the .), metadata version v4.: accepted. Format PE32_ ✗. Machine i386 ✓ |
12 left |
| 4 | d03 format PE_ ✓: the container part is the generic PE, not PE32 |
11 left |
| 5 | d02 SHA-256 e9ca3054…d0d7 |
accepted, 10 left |
| 6 | d03 config: campaign BR03-, domain node-, mutex Global\HuntressBR03_ |
accepted, 7 left |
The 7 open fields were all Day 2's. Six are the config fields. Claude's safety filter had stopped one step of the analysis on the same work in Mangled, and we didn't work around it, so Claude didn't attempt them here. The seventh, d02_analysis_format, was a wording question, not blocked.
The Homebase agent's part:
- 2026-10-07, 17:13:06: its config analyser read the HCFG record from the C++ ELF and sent the six config fields as one batch: accepted, 1 left (23 of 24).
- 2026-10-08: it sent ELF64_CXX for d02_analysis_format: incorrect. That submit was only recorded in the agent's own notes, with no time.
- 2026-10-09, 15:30:21: the agent's persistent probing on Mangled found lowercase elf, a plain container name. Sent here alone: CORRECT, flag a2e1fd2a…3424. Uppercase ELF and ELF64 had both been rejected in Mangled; dozens of other spellings went through Mangled first (see the Mangled write-up).
Time Trial: CORRECT, 11.3 s on the agent's clock (Homebase)
Run by the Homebase agent, in its batch of timed runs for Days 3–9. The answers came from three pieces: our core binary solver (SHA-256s, the d03 format, machine, metadata version and config, and d02's elf), the agent's Day 1 ELF reader (wrapped), and its Day 2 config analyser.
started_at 2026-10-09T16:57:21.186Z deadline 2026-10-09T16:57:27.186Z (24 answer_ids, 24 answered)
start 9937 ms · artifact 409 ms · solve 39 ms · submit 912 ms · TOTAL 11297 ms
result CORRECT flag ca678b52…c49d
The total is longer than the 6-second window, yet it counted. The Game Master stamped the start at 16:57:21.186 and logged the submit at 16:57:22, so the server saw about one second. Most of the agent's 9.9 s "start" phase must have come before the server stamped the attempt; the agent didn't record why.
Mastery: CORRECT as untimed practice (from our Kali VM)
First attempt, Homebase, 2026-10-09 16:57:29 (deadline 16:57:35.396): stopped before submit, nothing sent. Set 2 delivered d02 as a MinGW C++ PE (.exe). The config analyser skipped the setN. answer ids and .exe files, so the six d02 config fields in both sets were unanswered, and set2.d02_analysis_format had no known value for a PE. The attempt expired.
Fixes (Claude, the same afternoon): the config analyser now accepts setN. ids and .exe files; a shared helper maps each "delivery N:" question to its own NN-evidence.zip and never answers from another set; archive reads and the config scan are cached. Re-run offline against the saved deliveries, our solvers gave the same answers as every Time Trial submit.
The PE format value: pe (lowercase, like elf and jar; the d03 question itself calls PE "the generic container label"). It was confirmed one minute earlier in Nimble's Mastery (17:27), so it went into this run as a known value, not a guess.
Finish, from our Kali VM: a retry twice said rotation_exhausted (no fresh variant left), so our runner switched to op=practice: untimed, the last delivery again, and no Hall of Fame.
practice started_at 2026-10-09T17:28:29.221Z deadline None practice True
(48 answer_ids: set1 + set2, set 2's d02 a MinGW PE; one 48-answer submit)
retry 667 ms · practice 382 ms · artifact 380 ms · solve 46 ms · submit 1652 ms · TOTAL 3127 ms
result CORRECT flag af41154b…8086
The confirmed rules
| Field | Rule |
|---|---|
dNN_ |
SHA-256 of that day's files in name order (. = 1, . = 2) |
| d01 header | ELF64 · x86- · ET_ · little |
d01_ |
Every section name in header order, including section 0's "" |
d01_ |
Every named . entry in table order, file symbols too |
d01_ |
recover_'s instructions, e.g. ["mov eax,0x1fa4","ret"]; [] if it's absent |
d02_ |
elf for the ELF; pe for Mastery set 2's MinGW . |
| d02 config | The HCFG record's fields (read by the Homebase agent's config analyser) |
d03_ |
PE_: generic container + managed runtime (PE32_ rejected) |
d03_ |
i386 (PE machine 0x14c) |
d03_ |
The BSJB root's version string, v4. (not net8.) |
| d03 config | The HCFG record in Program.'s FieldRVA data |
Time to solve
The Single Solve was completed on 2026-10-09 at 15:30:21 UTC, 2 d 0 h 14 min after it started: 17 of 24 answers took 2 min 7 s, and the last one, d02_analysis_format, took two days. The Time Trial went through first time (11.3 s on the Homebase agent's clock, about one second on the server's); the first Mastery attempt stopped before submitting and expired, and the Mastery was finished at 17:28:31 UTC as untimed practice, so it is unranked.
Takeaways
- A .NET binary is two formats at once. The PE header gives the machine (
i386for "Any CPU"); the CLI metadata gives everything else. The format answer named both:PE_CLI. - The metadata version isn't the .NET version.
v4.0.30319sits in every modern assembly;net8.0is in theruntimeconfig.json. - "Stashed in the metadata" meant a field's initial data. A constant byte array becomes a FieldRVA blob; the decompiler shows the field, the metadata table shows where its bytes are.
- Generic beats specific in this checker's vocabulary.
PE32_CLIwas wrong andPE_CLIright;ELF64andELFwere wrong andelfright; laterpe. When a question says "the generic container label", take it literally. - Compounding days reward a reusable toolkit. The d01 and d02 files were byte-identical to earlier days, so accepted answers and analysers carried straight over.
- A timed run can stop safely and still be finished. Mastery's first attempt stopped on a file type the config analyser didn't know; after the fix, practice mode finished it untimed.
- One blocked field can hold a challenge open for days. 17 answers took 2 minutes; the last one took two days and outside probing.