The challenge
"Eight Node.js operation graphs that will never execute. Spot the indirect expansions and token fragmentation, then sort each graph into its role — benign, staging, dropping, downloading, uploading, launching, scheduled, or multi-stage — from the final bytes alone."
469 questions about one AES-encrypted evidence.zip (Time Trial the same; Mastery 938, as two sets). Only 70 of them were new. The rest carried every earlier Script day forward.
| Group | Files | Questions | Format |
|---|---|---|---|
| PowerShell (Day 1) | resource- / collection- / impact- |
15 | enum, T####[., exact text |
| JScript (Day 2) | 9 . |
72 | role, ATT&CK, adapter, campaign, indicator, three style enums |
| Batch (Day 3) | 9 . / . |
72 | as above, plus expansion and control (exact text, no list given) |
| VBScript (Day 4) | 9 . |
72 | as above, plus control (exact text, no list given) |
| Python (Day 5) | 14 *- / source- |
126 | packer, codec, and plan fields |
| Bash (Day 6) | 6 . |
42 | role, ATT&CK, plan fields |
| Advanced Bash (new) | indirect-, token- |
14 | role (advanced_), ATT&CK, feature, primitive, campaign, stage count, steps |
| Node.js (new) | 8 node- |
56 | role (8 values, now with persistence_), ATT&CK or none, adapter, campaign, one indicator (host_, url, file_, schedule_, or service_), stage count, steps joined by > |
Reading the prompt: what it said we'd need
| Clue | What it pointed to | What I did about it |
|---|---|---|
| "Eight Node.js operation graphs" | 8 . files, each rebuilding a plan with ordered steps |
A new Node decoder; steps are the "graph", joined with > |
| "that will never execute" | Static only | Read the string literals as data. node was never run |
| "Spot the indirect expansions and token fragmentation" | The two new advanced-Bash fixtures, named exactly that | Decoded their two-field codec; feature and primitive come straight from the plan |
| "benign, staging, … scheduled, or multi-stage" | The role list, with a new entry for "scheduled" | The question's own list says persistence_ and staged_; roles follow the plan's steps |
| "from the final bytes alone" | Answer from the decoded plan, not from file or adapter names | The persistence script's adapter is systemd_, but the path it writes is /, so Cron (T1053.) |
| Not in the prompt | 399 questions from Days 1–6, including the open Batch and VBScript control labels |
Reused the chain of earlier solvers; the labels took two more days |
Safety
Static only. No script was run: the zip was read into memory on our Kali VM, and our Node decoder pulls each string literal out with a regular expression and parses it as JSON, so no JavaScript is ever evaluated. The JScript part was decoded by the Homebase agent's static analyser. The artifacts stayed in the VM and on the Homebase. Indicators below are defanged.
What was in the files
evidence.zip: 66,464 bytes, SHA-256 5e2cadf1…1038 (checked), 61 files.
The Node fixtures are ES modules with the same shape as the other Script days: a const Material_<id> string, a const Codec_<id> = "<representation>|<transform>|<key>" line, and load_, decode_, recover_, plan_, and inspect_ functions behind a HUNTRESS_INSPECT_ONLY guard. Five representations (base64_chunks, base64_literal, hex_buffer, hex_array, escaped_octets) and four transforms (reverse_bytes, identity, rolling_xor, single_byte_xor).
| File | Role | Adapter | Steps | Indicator | ATT&CK |
|---|---|---|---|---|---|
node- |
benign_ |
console_ |
decode_ |
host_ |
none |
node- |
stager |
http_ |
acquire_ |
hxxps:/ |
T1105 |
node- |
dropper |
typed_ |
decode_ |
/ |
T1140 |
node- |
downloader |
http_ |
acquire_ |
a URL | T1105 |
node- |
uploader |
stream_ |
read_ |
a URL | T1041 |
node- |
launcher |
execfile_ |
locate_ |
a file path | T1059. |
node- |
persistence_ |
systemd_ |
build_ |
/ |
T1053. |
node- |
staged_ |
fetch_ |
5 stages, profile_ |
node- |
T1059. |
The advanced-Bash fixtures decode to one-stage plans, e.g. feature=indirect_expansion|primitive=parameter_indirection and token_fragmentation / array_token_construction, both with steps recognize_syntax>reconstruct_intent.
The older languages brought new codec variants, which broke the earlier solvers until fixed: VBScript add_modulo_256 and base64_literal; Python hex_literal; Bash decimal_colon, hex_colon, reverse_bytes, and repeating_key_xor. Each fix kept the Day 4–6 regression tests passing.
The control slots. Day 7 had the most open control answers of any day at the end (9), and the biggest share of if defined Batch scripts (4). It used every control label except goto_dispatch:
| Language | Script (role) | What's around the rebuild / Decode | Label |
|---|---|---|---|
| Batch | benign, stager, dropper, relay | set "HUNTRESS_ + if defined HUNTRESS_ |
if_ |
| Batch | downloader, chain | nothing (an && or quote-compound decoy elsewhere) |
direct |
| Batch | uploader | for / |
call_ |
| Batch | launcher, impact | ver >nul + if errorlevel 1 exit / |
errorlevel_ |
| VBScript | stager, relay | Select Case 1 |
select_ |
| VBScript | dropper, launcher | For decodePass = 0 To 0 |
for_ |
| VBScript | downloader | While … Wend |
while_ |
| VBScript | uploader | Do … Exit Do … Loop |
do_ |
| VBScript | benign, chain | On Error Resume Next … On Error GoTo 0 … Err. |
error_ |
| VBScript | impact | only an always-true If True Then |
direct |
Two Day 7 details mattered. In the benign and stager scripts the if defined line only empties the accumulator before a FOR rebuild, yet the label is still if_guard: the wrapper, not what it guards, decides. And If True Then is not a control form at all: straight_line and if_true were both rejected for VBScript impact, and the answer was direct.
Time Trial and Mastery used the same templates with new values, as far as the solver could tell: it answered all 469 and all 938 first time and met no new form (it stops on anything it doesn't know).
Single Solve
1. Claude, 2026-10-07 16:18–16:25 UTC: 375 accepted in 12 submits, none wrong.
- Five batches from the earlier solvers (PowerShell 15, Batch 55, VBScript 65, Python 126, Bash 56 including all 14 advanced-Bash answers), then the Node plan fields (40), roles (8), and the five easy ATT&CK IDs.
- The three judgement ATT&CK IDs went alone. Launcher and chain: T1059.007 (Node's own interpreter, the rule from every other language). Persistence: T1053.003. All three were accepted on the first try.
- The two Batch hand-off ATT&CK IDs followed at 16:25 as T1059.003. All 56 Node and all 56 Bash answers right.
- Open: 94 (72 JScript, 15 Batch expansion / control, 7 VBScript control).
2. JScript: the Homebase agent, 2026-10-07 17:58 UTC. Claude's safety filter had stopped one step of the JScript analysis on Day 2, and we didn't work around it, so the JScript part went to the Homebase agent. Its JScript analyser decoded the plans statically and answered all 72 in four batches, all accepted. 447/469.
3. Batch and VBScript wording, the Homebase agent, 2026-10-08 and 2026-10-09 (13 accepted, 12 wrong).
- 2026-10-08 17:42: launcher and impact errorlevel_guard (449).
- 2026-10-09 13:14–15:49: for_to_next and counted_loop were rejected for the dropper's For decodePass = 0 To 0 wrapper. Then call_subroutine, do_once, and while_once on three known forms, and for_once, accepted here first (Day 7 dropper, then the launcher) (454).
- 19:22–19:23: nine descriptive names for the open forms (guarded_fragment_loop, ready_call_set, restore_and_reraise, straight_line, and others). All nine rejected. Guessing had stopped paying.
- 20:00–20:01: for_indirect for the benign and stager expansion (456). The Batch expansion labels (delayed, call_double, for_indirect) follow the fragment-variable family, found by tests across days.
- 20:10: our runner's single-question mode sent 4 new expansion answers (460, plus 5 harmless resends), then if_true for VBScript impact: rejected.
4. The Day 7 review (Claude, read-only). Claude checked the Homebase's run against both request logs: the log matched the agent's own results log value for value, 460 accepted equalled offline coverage, and the 9 open IDs were exactly the 9 the solver couldn't cover: six Batch and three VBScript control. Later Homebase audits found no feature in the files that predicted the missing labels.
5. The last nine, 2026-10-09 evening (ET), from our Kali VM. Zwesty and Claude worked back from the accepted labels across Days 3–9:
- error_guard came from Zwesty mirroring errorlevel_guard on Day 5. Day 7's benign and chain: accepted (00:35 UTC).
- direct came from splitting the open scripts by what was really in the slot: 11 Batch slots were empty. The word is the generator's own (JScript style direct); first accepted on Day 5 and on Day 4's VBScript. Day 7's Batch downloader and chain, and VBScript impact: accepted (00:50 UTC).
- if_guard came last, after 68 rejected names on the 13 readiness scripts (mostly on Day 3's relay, including all 8 known labels). The pattern: every accepted label is a keyword in the code plus one of five second words (subroutine, guard, dispatch, case, once). if_guard was the first one tried from that grid. Day 7's four: the last one CORRECT at 01:37:26 UTC (469/469, flag 4f86b8ba…6b9b).
Decoys (separator lines, & rem HUNTRESS quote compound, neutral_* variables, comments) never decided a label. Support was never needed.
12 wrong answers on this challenge, all control names sent by the Homebase agent while the vocabulary was unknown; none on the Node or Bash parts.
Solvers: our Day 7 solver wraps Shell We Begin's chain back to Power Play and adds the Node and advanced-Bash parts and the confirmed ATT&CK table. Our Node decoder is cross-checked against each file's own load_ / decode_, and a small helper submits a chosen subset. Its 3 tests check that it reproduces every accepted answer, stops on a wrong key, and decodes and checks the key. The JScript, Batch-vocabulary, and VBScript-control parts are the Homebase agent's add-ons, run by our runner.
Time Trial
Run from our Kali VM with our runner. The request log doesn't keep the deadline or lap timings, so this is rebuilt from it plus the client's TOTAL.
started_at 2026-10-10T01:41:05.520Z
answers 469 (one set, one submit)
submitted 01:41:07 UTC
TOTAL 2.58 s (client side)
result CORRECT flag 33709f3d…4dac
Mastery
started_at 2026-10-10T01:41:12.772Z
answers 938 (two sets × 469, one submit)
submitted 01:41:15 UTC
TOTAL 4.44 s (client side)
result CORRECT flag dc703b73…a9ab
Both correct on the first attempt. Day 7's timed sets brought no new encoding (Day 6's Mastery had, the same night).
Time to solve
The Single Solve was completed at 01:37:26 UTC on 2026-10-10, 57 h 19 min after it started: 375 of 469 answers (all the new Node and Bash ones among them) were in within 7 minutes, and the last nine control labels came two and a half days later. The Time Trial (2.58 s) and the Mastery (4.44 s) each went through on the first attempt by 01:41:15 UTC, 57 h 23 min after the first start.
Takeaways
- The new language was the easy part. All 56 Node answers were accepted in 3 minutes with no wrong submit, because the codec shape, the role rule, and the ATT&CK rules all carried over.
- The path decides the technique, not the adapter's name. A
systemd_descriptoradapter that writes to/etc/cron.d/is Cron,T1053.003. - A growing chain needs regression tests. Day 7 brought new codec variants in four older languages; each fix had to keep Days 4–6 passing.
- "Nothing" has a name, and decoration is nothing. An always-true
If True Thenisn't a control form: the answer wasdirect. Anif definedthat only empties a variable is still the readiness wrapper,if_guard. - Reconcile against both logs. The Day 7 review showed accepted = coverage, and the open IDs were exactly the uncovered ones. That turned "something is wrong somewhere" into "nine labels are unknown".
- Patterns beat volume. Twelve descriptive guesses failed here; the labels came from reading the accepted answers' naming (keyword + one of five second words) and the generator's own words.