The challenge
"The same indicators show up in every language the campaign speaks, each with its own quoting, escaping, and encoding accent. Normalize them to their exact form and find the one that doesn't match its siblings."
615 questions about one AES-encrypted evidence.zip (password infected). The first 483 are Days 1–7's question set with new values. The other 132 are new: 12 files with 11 questions each.
| Group | Files | Questions | Format |
|---|---|---|---|
| PowerShell (Day 1's samples) | resource- / collection- / impact- |
15: scenario, ATT&CK, three indicators each | enum, T####[., exact text |
| JScript | 9 . |
72: role, ATT&CK, adapter, campaign, one indicator, three style questions | enum, exact text |
| Batch | 9 . / . |
72: role, ATT&CK, adapter, campaign, one indicator, expansion, control, graph |
enum / exact text |
| VBScript | 9 . |
72: role, ATT&CK, adapter, campaign, one indicator, representation, transform, control |
exact text |
| Python | 14 | 126: the Day 5 set (syntax, obfuscation, carrier, chain, and the usual five) | exact text |
| Bash | 6 regular + 4 advanced | 70: role, ATT&CK, campaign, stage count, steps; the advanced ones ask feature and primitive instead of adapter and indicator | exact text, integer, ordered tokens |
| Node | 8 | 56: the Day 7 set | exact text, integer, ordered tokens |
| New: PHP polyglots | 8 polyglot-, one per role |
88 (11 each, below) | see below |
| New: host fixtures | host-, host-, host-, host- |
44 (11 each) | see below |
The 11 questions on each new file:
answer_id suffix |
Question | Format |
|---|---|---|
- |
Classify the inert source role | enum of 11 (benign_ … advanced_) |
- |
Primary ATT&CK technique ID, or none | T####[. or none |
-, - |
Modeled language adapter; synthetic campaign ID | exact text |
- or - |
The file's indicator | exact text |
- |
Modeled stage count | integer |
- |
Ordered behavior graph | tokens joined by > |
- |
Script host or source container | exact text |
- |
"Ordered encoding profile" | exact text |
- |
Multi-stage relationship profile | exact text |
- |
Next script language in the hand-off | exact text |
Reading the prompt: what it said we'd need
| Clue | What it pointed to | What I did about it |
|---|---|---|
| "The same indicators show up in every language the campaign speaks" | Every earlier Script language comes back (PowerShell, JScript, Batch, VBScript, Python, Bash, Node), plus PHP | Ran the whole Day 1–7 solver chain first: 384 of the 483 older answers on the first submit |
| "each with its own quoting, escaping, and encoding accent" | Per-language decoders, and new spellings of old tricks | Fixed four small variants (Node & 255, Node percent_ and unicode_ loaders, Bash reversed_); found the new files' header plan in four encodings |
| "Normalize them to their exact form" | exact_ answers with only trim normalisation |
Took values straight from the decoded plan; turned the plan's comma list into the > form steps asks for |
| "find the one that doesn't match its siblings" | A file that disagrees with itself | No separate question asked this. Our header check still compares the header plan with every plan it can decode in the body, and refuses a mismatch. None disagreed |
| "host-profile", "stage-profile", "next-language" in the questions | Fields of the new files' plan | Read from the plan, not guessed from the code |
Safety
Static only. No script was run. Files were read straight out of the AES zip into memory and parsed as text. The artifacts stayed on our Kali VM and on the Homebase. Indicators below are defanged.
Claude's safety filter stopped one step of the analysis, and we didn't work around it; the Homebase agent took that part on.
What was in the files
- The older 62 files use the same templates as Days 1–7 with new values. A few loaders changed spelling: Node used
& 255where it had used% 256, two new Node loaders (percent_hex,unicode_escapes), and the Bash advanced fixtures usedreversed_base64. - The 12 new files each carry a plan in a header comment,
HUNTRESS_ANALYSIS_V1:<ENC>-<payload>, withENCone ofHEX,B64,HB64(hex of Base64), orFRAG(dot-separated hex). It decodes tov=3|key=value|…, and the body holds the same plan again. The plan names every answer. Forpolyglot-php-stager.phpit held:
| Field | Value |
|---|---|
| role / attack | stager / T1105 |
| adapter / campaign | runtime_ / PHP- |
| url | hxxps:/ |
| stage count / steps | 2 / acquire_ |
| host / obfuscation / stage profile | standalone / base64 / two_ |
| next language | jscript |
- Profiles seen: host
standalonefor all eight polyglots,wsf_mixedandphp_webshellfor the two non-JScript host files. Obfuscationplain_hex,base64,hex_base64,fragmented_hex. Stage profilessingle_descriptor,two_stage_download_execute,three_stage_decode_write_launch,cross_language_handoff. The next-language answers ran acrosspowershell,jscript,batch,vbscript,python,bash,javascript, andphp. - Time Trial and Mastery used the same templates with new values. Mastery took 3,075 answers in one submit, five times the Single Solve's 615. No new encoding turned up in either.
Single Solve
Claude, from our Kali VM (2026-10-08, 20:16–20:18 UTC): 494 of 615, no wrong submits
- Older parts: 384 accepted in six language batches (Batch 54, VBScript 63, PowerShell 15, Python 126, Bash 70, Node 56), once the four loader variants were in. The Bash advanced
feature/primitivepairs (for exampleindirect_expansion/parameter_indirection) came from Day 6's Bash solver. - New files: 110 accepted (10 non-JScript files × 11): 90 plain fields, then 10 roles, then ATT&CK in three batches. The header's own ATT&CK was right, including the parent
T1059for the PHP launcher, the staged chain, and both non-JScript hosts, andT1053for the PHP persistence descriptor.
The JScript part: the Homebase agent (2026-10-08, 20:36–20:38 UTC): 592 of 615
The Homebase agent handled the JScript files throughout this event. Its JScript add-on decoded the plans statically: 72 older JScript answers plus 22 for the two JScript host files, read from their header plans. Its Batch-wording add-on added 4 Batch control answers. All accepted, no wrong submits. 23 left, all Batch/VBScript wording.
Batch and VBScript wording (2026-10-09): 23 down to 4
These answers name a construct, not a value, so the right word had to be found by test: one answer per submit, and a rejected value never resent.
| Time (UTC) | Who | Result on Day 8 |
|---|---|---|
| 14:09–15:50 | Homebase | 9 accepted: VBScript do_ ×4, while_, for_ ×2; Batch call_ ×2 (launcher, impact: for /). 3 rejected: for_ and for_ on the launcher before call_ landed, findstr_ on the stager |
| 19:25 | Homebase | 4 rejected: call_, if_, error_, straight_ |
| 20:19–20:25 | Homebase | Coverage reconciliation from confirmed rules: 8 accepted (Batch expansion answers, goto_ on the stager). 1 rejected: delayed on batch-, a rule that turned out to be too broad |
| 20:48 | Homebase | call_ (impact) and delayed (relay): both accepted, 611/615 |
The Batch expansion rule that came out of those last three tests: a set "x=!a!…" rebuild is delayed. FOR/CALL and direct call set rebuilds follow the fragment variable family: h0–h3 gives call_double, while fragment*, item*, and v_a* give delayed (or for_indirect for the FOR form). Day 8's impact script, with call set "cfg=%%h0%%%%h1%%%%h2%%%%h3%%", was the one that showed the split.
The last four control labels (Zwesty and Claude, from our Kali VM, 2026-10-10 00:35–01:37 UTC)
Late on 2026-10-09 (ET), Zwesty and Claude stopped guessing and worked back from the accepted answers. Batch: the "control slot" sits between :HUNTRESS_SEPARATOR_DONE and echo(, and it holds one of five things. VBScript: the label names the innermost block around the Decode_* call. Separator lines, the & rem HUNTRESS quote compound marker, neutral_* variables, and comments are decoys and never decide the label.
| Slot contents | Label |
|---|---|
Batch call :HUNTRESS_ block |
call_ |
Batch ver >nul + if errorlevel 1 exit / |
errorlevel_ |
Batch goto :HUNTRESS_ + its label |
goto_ |
Batch set "HUNTRESS_ + if defined HUNTRESS_ |
if_ |
VBScript Select Case / Do … Exit Do / While … Wend / For … To 0 |
select_ / do_ / while_ / for_ |
VBScript On Error Resume Next around the decode |
error_ |
| Nothing (Batch or VBScript) | direct |
Day 8's four open questions, each sent alone:
| Question | What was in the slot | Answer | How it was found |
|---|---|---|---|
vbscript- |
On Error Resume Next / recoveredMaterial = Decode_ / On Error GoTo 0 / If savedError <> 0 Then Err. |
error_ ✅ |
Zwesty's idea, mirroring errorlevel_ (first accepted on Day 5), then rolled out |
batch- |
Nothing. A & rem HUNTRESS quote compound decoy, then call set "cfg=%%v_ |
direct ✅ |
Splitting the open scripts by what was really in the slot, then borrowing the generator's own "plain" word from the JScript style field |
vbscript- |
Nothing. v_, then a byte-copy For … To UBound loop that doesn't enclose the decode |
direct ✅ |
Same rollout |
batch- |
set "HUNTRESS_, an if defined HUNTRESS_ line that clears cfg, then for %%G in (0 1 2 3) do call set "cfg=%%cfg%%%%h%%G%%" |
if_ ✅ CORRECT |
Every accepted label is a keyword in the code plus one of five second words (subroutine / guard / dispatch / case / once). After 68 rejected names across the 13 readiness questions, the grid's first new pair (on Day 3) was accepted |
Single Solve CORRECT at 01:37:29 UTC on 2026-10-10 (615/615). Flag 8771c177…cedc. Support was never needed.
Wrong submits on Day 8: 8, all single-answer tests (the 3 morning ones, the 4 at 19:25 and the one too-broad delayed). None came from Claude's own solver.
Solvers: this day's solver became the core of the Script chain. It wraps Node by Node's solver (and through it Days 4–7 and Power Play) for the older questions, and adds the new files through a header decoder. Day 9 was solved with it, and our automated morning solver (a scheduled script on our Kali VM) uses it. The header decoder handles the four header encodings, collects every plan it can decode in the body, and flags a disagreement. The solver also checks each new-file enum answer against the question's accepted list. Its 3 tests cover the four encodings, the header-versus-body check, and "reproduces the 494 accepted answers and nothing else, with no JScript". The Homebase agent's JScript, Batch-wording, and VBScript-control add-ons run alongside it.
Time Trial
Run from our Kali VM by our solver. Timings are client side.
started_at 2026-10-10T01:41:19.308Z (deadline not recorded in the request log)
coverage 615/615 (solver chain + JScript, Batch-wording and VBScript-control add-ons)
submit 615 answers, 01:41:21 UTC
TOTAL 2.55 s (client side)
result CORRECT flag 3e5e3756…c527
Correct on the first run.
Mastery
started_at 2026-10-10T01:41:25.608Z (deadline not recorded in the request log)
submit 3,075 answers, 01:41:29 UTC
TOTAL 5.43 s (client side)
result CORRECT flag a51740fc…ce74
Correct on the first run. On other days, some timed runs were stopped before submitting (unknown readiness wrapper on Days 4–5, a new Bash hex_dash on Day 6). Day 8's ran after both fixes and went through cleanly.
Time to solve
The Single Solve started on 2026-10-08 at 20:16 UTC and went CORRECT on 2026-10-10 at 01:37:29 UTC, 29 h 21 min later; 494 answers were in within two minutes, and nearly all the rest of the time went on the Batch and VBScript wording. The Time Trial (2.55 s) and Mastery (5.43 s) then each went CORRECT on the first run, for 29 h 25 min from the first start to the last CORRECT.
Takeaways
- Compounding pays off. Seven days of solvers answered 384 of the 483 older questions on the first submit. This day's solver became the chain that Day 9 and our morning solver used.
- Trust the sample's own plan, but check it against itself. The header plan named every answer, including ATT&CK parents (
T1059) that a guess would have refined. Comparing the header with the body costs nothing and catches a planted mismatch. - Decoders must read the code, not the template. Each day brought a respelled loader (
& 255,percent_hex,reversed_base64). A check against the script's own decode line found them. - Name the construct, not the decoration. The
controllabels were all "keyword + one of five words". Once the decoys were set aside and the open scripts were split by what was really in the slot, three rules closed every open label on every day. - One answer per test. The 8 wrong submits here were all deliberate single tests, so every rejection taught something and none was repeated.