The challenge
Go binaries are chatty: they ship with build info, function names, and file paths baked right in. Fish them out and map what this one was built to do.
A compounding challenge. One AES-encrypted evidence.zip (password infected) holds Days 1–4: the Day 1 C ELF, the Day 2 C++ ELF, the Day 3 .NET DLL with its runtimeconfig.json, and the new Day 4 Go ELF. 32 questions, 8 per day. Every question uses exact normalisation, and exact-text answers carry the usual line: "use the literal none only when the requested field is absent."
The 24 questions for Days 1–3 are the same IDs as Table Manners (Day 3). The 8 new ones:
answer_id |
Question | Format |
|---|---|---|
d04_ |
The Go binary's SHA-256 | Exact text |
d04_ |
"exact analysis architecture value" | Exact text |
d04_ |
"exact analysis format value" | Exact text |
d04_ |
Config campaign | Exact text |
d04_ |
Config domain | Exact text |
d04_ |
Config mutex | Exact text |
d04_ |
Config URL | Exact text |
d04_ |
Config CRC32 | Lowercase hex with 0x |
The questions are generated from the field names ("What is the exact analysis format value for field d04_analysis_format…?"), so they give no hint about spelling. That became the whole story of this challenge.
Reading the prompt: what it said we'd need
| Clue | What it pointed to | What I did about it |
|---|---|---|
| "Go binaries are chatty" | Go keeps a lot of metadata even when stripped | Expect . to be gone and look in the Go-specific sections instead |
| "build info" | The . section: Go version, module path, build settings |
Read it as plain strings: go1., GOOS=linux, GOARCH=amd64, and the rest |
| "function names" | The Go runtime's . table, which survives stripping |
Wrote a small pclntab reader to list every function and its address (2,200 of them) |
| "file paths baked right in" | Source paths in the pclntab file table | Found _: the program is one main package |
| "map what this one was built to do" | The main. functions are the program; the rest is runtime |
main. and main.; disassembled main. to see what it reads |
Field names config_ / domain / mutex / url / crc32_ |
An embedded config, like Table Manners' .NET one | main. decodes a record; matched its layout to Day 3's and built a checked parser |
architecture and format, no vocabulary given |
The checker's own spellings | Test each one alone, after the certain answers are in |
Safety
Static analysis only. No binary was ever executed. The evidence was downloaded and unpacked on our Kali VM (and, for the Homebase agent's runs, on the Homebase), never on the Windows host. The Go binary was read as bytes: section tables, strings, the pclntab, and a disassembly of one function. The config was decoded from the file's own bytes, not by running the code.
What was in the files
Single Solve delivery: evidence.zip, 852,723 bytes, SHA-256 7c99f149…9fdf (it matched the start reply), AES-encrypted, zip comment huntress:day04-single.
- Days 1–3: byte-identical to Table Manners' files (SHA-256 checked: d01
a0d3aedd…7b9b, d02e9ca3054…d0d7, d03a98b993f…073dandd6109426…6cf3). So Table Manners' accepted answers carried straight over. - Day 4:
d04--variant-000.elf, 1,867,924 bytes, SHA-256d5aa218b…182f.
The Go binary, from its headers and sections:
- ELF64, x86-64, ET_EXEC, statically linked, stripped of .symtab, but with .gopclntab and .go.buildinfo intact.
- Build info (plain strings): go1.25.1, path _/work/variant-000, -buildmode=exe, -trimpath=true, CGO_ENABLED=0, GOARCH=amd64, GOOS=linux, GOAMD64=v1.
- Functions from .gopclntab: main.campaign at 0x4c7380 and main.main at 0x4c74c0. main.evaluate was inlined, so its name appears only in the inline tree.
- Other telling strings: HUNTRESS_HOST_MODEL, huntress.binary.dynamic_trace.v1, file_and_directory_discovery.
What main.campaign does (from its disassembly): it reads a 379-byte global byte slice, takes a one-byte XOR key and a length from the slice's header, un-XORs the rest, and JSON-decodes it. The same layout as Table Manners' .NET config.
The config record, at a high level. It's an HCFG record: the magic HCFG, a version byte (1), a one-byte XOR key (0x51 here), a 2-byte length (367), the CRC32 of the plaintext JSON (0x2b747600), then the XOR'd JSON. The CRC is what the config_crc32_hex question asks for, and it doubles as a check that the decode is right. The decoded config (defanged):
campaign BR04-000
domain node-000.day04.example[.]invalid
ipv4 198.51.100.80
url https://node-000.day04.example[.]invalid/api/BR04-000
mutex Global\HuntressBR04_000
service HuntressLab-Day04-000
host_model workstation
(plus a windows_path and a linux_path)
The timed variants used the same template. The Time Trial delivered one evidence set of the same shape (32 answers), and the core solver answered it with the same rules. Mastery had two sets: set 1 was the same shape, but set 2 swapped two files for native Windows PE .exe files: d02 became a MinGW C++ PE, and d04 became a Go PE (still GOARCH=amd64, machine 0x8664). (The per-variant config values from the timed runs aren't quoted here.)
Single Solve: CORRECT (on 2026-10-09, after a two-day wording hunt)
Started 2026-10-07 16:06:04 UTC, no deadline. The certain answers went in first, then each uncertain one alone:
| # | What was sent | Result |
|---|---|---|
| 1 | Table Manners' 17 accepted answers + the d04 SHA-256 (18) | accepted, 14 left |
| 2 | campaign, domain, mutex, url, CRC32 0x2b747600 |
accepted, 9 left |
| 3 | architecture = amd64 (the build info's GOARCH) |
incorrect |
| 4 | format = ELF_ (following d03's PE_ pattern) |
incorrect |
| 5 | architecture = x86- (Day 1's accepted machine spelling) |
accepted, 8 left |
| 6 | format = elf64- (the BFD name) |
incorrect |
Lesson from #3 and #5: "architecture" wants the ELF machine spelling (x86-64), not Go's own GOARCH (amd64), even in a challenge about Go build info.
That left 24 of 32. The other 8: d04_analysis_format, and the seven Day 2 fields that were still open in Table Manners (six config fields plus d02_analysis_format).
The Day 2 config fields came from the Homebase agent. Claude's safety filter had stopped one step of the analysis on the Day 2 config work, and we didn't work around it, so Claude didn't attempt them here. On 2026-10-07 at 17:13 UTC the Homebase agent's config analyser sent all six as one batch: accepted, 30 of 32.
The format saga
Two fields left, both format: d02_analysis_format and d04_analysis_format. The same open question sat in every ELF and JAR challenge from Day 2 to Day 8. The only accepted precedent was Day 3's PE_CLI, whose question spelled out "a container_runtime token". That precedent pointed every early guess in the wrong direction.
| When (UTC) | Who | d04_analysis_format sent |
Result |
|---|---|---|---|
| 10-07 16:07 | Claude (our Kali VM) | ELF_, elf64- |
incorrect ×2 |
| 10-08 20:49 | Our Kali VM | none |
incorrect |
| 10-08 | Homebase agent (in Routine Check's copy of the same d04 field) | ELF64_ |
incorrect |
| 10-09 13:25–15:26 | Homebase agent | ELF_, ELF/, go, ELF64_, ELF64-, Go, ELF64_, GO_, ELF64_, dynamic_, dynamic_, GO_ |
incorrect ×12 (plus one HTTP 503 temporarily_ at 13:32, retried) |
The break came from Mangled. In the same persistent run, the Homebase agent tried lowercase elf for Mangled's cpp_format and it completed that challenge. In its own words, elf is "the lowercase ELF family token used in objdump -f output"; the uppercase ELF had been rejected there earlier. It then applied the same token everywhere:
| When (UTC) | Field | Sent | Result |
|---|---|---|---|
| 2026-10-09 15:30:59 | d02_ |
elf |
accepted, 1 left |
| 2026-10-09 15:34:02 | d04_ |
elf |
✅ CORRECT |
Single Solve CORRECT (Homebase agent). Flag 44df698c…1e01. 15 spellings were rejected for the Go format before the plainest one won. The confirmed rule: lowercase elf for every ELF, jar for every JAR, and Day 3's .NET stays PE_CLI.
The solver
The timed modes were run by our core binary solver (by Claude) plus the Homebase agent's add-ons. In plain terms:
- An archive reader opens the AES evidence zip (and nested archives) in memory.
- The core solver maps each dNN--variant-N.* member to its day, hashes every file for the sha256_K fields, reads headers for architecture (x86-64), and returns elf / jar for format.
- An HCFG reader finds an HCFG record anywhere in any file and accepts it only when its CRC32 matches, so a stray "HCFG" can't produce an answer. It answers every day's config fields (it also reproduces Day 3's).
- A pclntab reader lists a Go ELF's functions from .gopclntab (analysis aid; the answers don't need it).
- A set matcher pairs Mastery's sets with their own downloads; the Homebase agent's Day 2 config analyser answers the Day 2 config fields; a native-PE add-on (Claude, 2026-10-09) answers the native-PE format and architecture once confirmed.
Time Trial: CORRECT in 2.1 s
Run by the Homebase agent, with our solvers as they stood after the format fix. One submit, 32 answers, first try. Claude later re-ran the fixed solvers offline against the saved delivery: same answers.
mode time_trial (32 answer_ids)
started_at 2026-10-09T16:58:19.592Z deadline 2026-10-09T16:58:26.592Z (7 s window)
start 508 ms · artifact 574 ms · solve 260 ms · submit 770 ms · TOTAL 2112 ms
result CORRECT flag a49531d0…a690
Mastery: CORRECT as untimed practice (from our Kali VM)
Attempt 1 (Homebase agent, 16:58:28 UTC): stopped before submitting, nothing sent. Two sets of the 32 questions; the solver stopped on these gaps:
- Set 1's six Day 2 config fields: the Day 2 config analyser ignored ids with a setN. prefix.
- Set 2's d02 and d04 were native PE files (.exe). set2.d02_analysis_format, set2.d04_analysis_format, and set2.d04_analysis_architecture had no confirmed PE value, and the analyser skipped .exe for the six d02 config fields.
Fixes (Claude, same afternoon): the config analyser handles set ids and .exe; the new native-PE add-on holds the PE answers but stays off until confirmed. The candidates were pe (Day 3's question calls PE "the generic container label") and x86-64 (the accepted ELF spelling, ahead of amd64).
The PE values were confirmed on another challenge. At 17:27 UTC Nimble's Mastery (Day 9, whose sets 2 and 4 are PE sets) came back CORRECT with format = pe and architecture = x86-64. So the Go PE's architecture is x86-64 too, not its GOARCH=amd64, the same rule as the Go ELF.
Attempt 2 (our Kali VM): the retry was refused because the variants had run out, so the run fell back to practice mode: no clock and no Hall of Fame entry.
retry 2026-10-09T17:28:57Z HTTP 409 rotation_exhausted (unique_attempts_remaining 0)
practice mode mastery started_at 2026-10-09T17:28:57.999Z (no deadline, practice)
64 answers (set 1: ELF set; set 2: d02 + d04 as native PE, format pe, d04 architecture x86-64)
TOTAL 2613 ms
result CORRECT flag daa37974…0ac4
(The run log for this practice run recorded only the total, not the per-phase timings.)
Time to solve
The Single Solve was completed on 2026-10-09 at 15:34:02 UTC, 47 h 28 min after it started: 24 of 32 answers were in within 2 minutes, the Day 2 config fields came from the Homebase agent about an hour later, and the two format fields took two days. The Time Trial went through first time in 2.1 s; the first Mastery attempt stopped before submitting, and the Mastery was finished at 17:28:59 UTC as untimed practice, so it is unranked.
Takeaways
- A stripped Go binary still talks.
.gopclntabgives every function name and address, and.go.buildinfogives the toolchain, module path, and target.main.*is the program; the rest is runtime. - Find the config by reading the one function that uses it.
main.campaignshowed the record's layout, which matched Day 3's. One parser then covered both days. - Make a decoder prove itself. Accepting a config only when its CRC32 matches means a wrong decode can't produce an answer, and the CRC is itself one of the answers.
- "architecture" follows the checker, not the language.
x86-64won over Go'samd64, for the ELF and for the Go PE alike. - Try the plainest spelling early. Fifteen compound spellings failed before lowercase
elf. One precedent (PE_CLI, from a question that spelled out its own format) steered every guess wrong for two days. - Compounding pays forward. Byte-identical files (SHA-256 checked) let 17 earlier answers carry over in one submit. One confirmed rule (
elfon Mangled) closed the same field in seven challenges. - Mastery can change the container. Set 2 swapped ELFs for PE files, so a solver has to branch on the file's bytes, not on the day or the extension.
- An expired Mastery isn't lost. After
rotation_exhausted, practice mode still scores the challenge (no clock, no Hall of Fame).