The challenge
"Nine Python behaviors come wrapped in fourteen disguises: bytecode, wheels, zipapps, frozen executables, and armored runtimes. Peel each carrier back to its intent without letting anything hatch, and identify the package that tells a different story than its payload."
357 questions (Single Solve and Time Trial) about one AES-encrypted evidence.zip (password infected), and 714 in Mastery (two sets). Day 4's 231 questions came back with new values, plus 126 new Python questions.
| Group | Files | Questions | Format |
|---|---|---|---|
| PowerShell (Day 1's samples) | resource- / collection- / impact- |
15: scenario, ATT&CK, three indicators each | enum, T####[., exact text |
| JScript (Day 2's) | 9 . |
72: role, ATT&CK, adapter, campaign, one indicator, function / invocation / property style | role enum, ATT&CK, exact text |
| Batch | 9 (5 ., 4 .) |
72: role, ATT&CK, adapter, campaign, one indicator, expansion, control, graph |
exact text |
| VBScript | 9 . |
72: role, ATT&CK, adapter, campaign, one indicator, representation, transform, control |
exact text |
| Python (new) | 14: 13 *- + source- |
126: role, ATT&CK, adapter, campaign, one indicator, syntax, obfuscation, carrier, windows- |
role enum; ATT&CK; the four new fields are exact text ("Identify the exact source, bytecode, frozen, or protected carrier profile.") |
The Batch expansion and Batch/VBScript control questions give no list of accepted words. They ask only for "the exact Batch control-flow form" or "the exact VBScript control-flow form". That is where all the time went.
Reading the prompt: what it said we'd need
| Clue | What it pointed to | What I did about it |
|---|---|---|
| "Nine Python behaviors" | The role enum: benign utility, stager, dropper, downloader, uploader, launcher, impact, staged chain, relay | Roles taken from each decoded plan's steps |
| "fourteen disguises: bytecode, wheels, zipapps, frozen executables, and armored runtimes" | One file per carrier, and a carrier question for each |
The carrier name is in the plan (hash_, wheel_, zipapp_, pyinstaller_, pyarmor_ …) |
| "Peel each carrier back to its intent" | Each file packs an encoded plan; decode it to get role, adapter, campaign, indicator | Our Python fixture decoder decodes MATERIAL with the file's own CODEC line |
| "without letting anything hatch" | Don't run the . files |
Constants read with ast., which accepts literals only (see Safety) |
| "the package that tells a different story than its payload" | Carriers whose name hints at one thing while the plan says another | Not asked as its own question in this delivery. Example: wheel- is a memory stager, and py2exe- is a five-stage chain with a scheduled-task name and a local proxy. Nothing to submit |
| (not in the prompt) the other 231 questions | Day 4's Dim and Dimmer, with new values | Reused Dim and Dimmer's solver, so the PowerShell, Batch, and VBScript plans came out on the first run |
Safety
Static only. No script was run: not the Python fixtures, not the invoke-python-stage.ps1 hand-off precursor, and not the Batch, VBScript, or JScript files. The zip was read into memory and parsed. For Python, our decoder pulls the two constants out with ast.literal_eval, so nothing in a fixture can execute; a test checks that a non-literal MATERIAL is refused. The artifacts stayed on our Kali VM (and on the Homebase for the JScript part). No indicators are quoted below.
What was in the files
- 45 files in one zip: Dim and Dimmer's 30 scripts with new values, the 14 Python carriers, and
invoke-python-stage.ps1. Nothing asks about that last one directly. It is an inert "PowerShell-to-Python handoff precursor": aStart-Processofpython.exe pyarmor-pyinstaller-recovered.py --handoff-id PSPY-AB93…D558, the same hand-off ID as in that carrier's plan. - Each Python fixture has
MATERIAL_<id>plusCODEC_<id>=<representation>|<transform>|<key>, andload_,decode_,recover_,inspect_,surface_,obfuscation_, andplan_functions, all guarded byHUNTRESS_INSPECT_ONLY. The representations aredecimal_csv,hex_escape,hex_colon,base64_chunks,urlsafe_base64,integer_list, andreversed_integer_list. The transforms arereverse_then_xor,reverse_bytes,single_byte_xor,add_modulo_256,subtract_modulo_256, andidentity. - The decoded plan names
syntax,obfuscation,carrier, andwindows_chainitself, and each script'ssurface_4-tuple says the same; the solver checks both agree.
| File | Role | Carrier | Syntax | Obfuscation | Windows chain |
|---|---|---|---|---|---|
source- |
benign_utility | raw_ |
plain_ |
transform_ |
direct_ |
pyc38- |
dropper | hash_ |
class_ |
string_ |
renamed_ |
current- |
launcher | hash_ |
closure_ |
chr_ |
pythonw_ |
zipapp- |
relay | zipapp_ |
lambda_ |
reversed_ |
cmd_ |
wheel- |
stager | wheel_ |
list_ |
format_ |
powershell_ |
pyinstaller- |
downloader | pyinstaller_ |
generator_ |
fstring_ |
mshta_ |
pyinstaller- |
uploader | pyinstaller_ |
dictionary_ |
lambda_ |
rundll32_ |
py2exe- |
launcher | py2exe_ |
match_ |
closure_ |
regsvr32_ |
py2exe- |
staged_chain | py2exe_ |
walrus_ |
exception_ |
service_ |
cxfreeze- |
impact | cx_ |
decorator_ |
bytes_ |
scheduled_ |
cxfreeze- |
dropper | cx_ |
iterator_ |
reflective_ |
run_ |
pyarmor- |
stager | pyarmor_ |
exception_ |
marshal_ |
wscript_ |
pyarmor- |
staged_chain | pyarmor_ |
dictionary_ |
reflective_ |
powershell_ |
embedded- |
relay | embeddable_ |
class_ |
marshal_ |
renamed_ |
- Hand-offs: zipapp →
pyc38-recovered.py; py2exe-service →resource-sample.ps1; pyarmor-pyinstaller →local-dispatch.bat; embedded-runtime →pyarmor-pyinstaller-recovered.py. - VBScript added forms here: the representations
base64_chunks(fourMidchunks),reversed_hex, andhex_dash, and the transformidentity. Claude changed our VBScript decoder to take the separator, base, and order from the script's own Load code. There was also a new control form,On Error Resume Next … On Error GoTo 0around the decode (three scripts). - Time Trial and Mastery used the same template with new values, but the Batch "control slot" could change between variants. The Single Solve's nine Batch scripts had no
set "HUNTRESS_READY=1"+if definedwrapper. The first Time Trial variant had two, and that wrapper had no known label yet.
Single Solve
Claude, 2026-10-07 16:08–16:25 UTC: 261 of 357
- PowerShell, Batch, and VBScript from Dim and Dimmer's solver (16:10:44–16:10:52): six batches, all accepted: 15 PowerShell, 54 Batch (plans, roles, ATT&CK, two
delayedexpansions), and 64 VBScript (plans, roles, representation, transform, ATT&CK, the relay'sselect_case). 224 left. - Python, all 126, no wrong submit (16:10:53–16:11:51). Three batches of plan fields (98: adapter, campaign, indicator, syntax, obfuscation, carrier, windows-chain), then 12 roles, then the two relays. The relay question lists
relayitself, unlike Batch's. ATT&CK went in two steps: - 8 role-mapped IDs in one batch:none(benign),T1140(droppers),T1105(stagers, downloader),T1041(uploader),T1486(impact). Accepted. - The six launchers, chains, and relays, each alone:T1059.006, all accepted. Day 4's rule held: a hand-off takes its own interpreter's ID (Python), even when the next stage is PowerShell or Batch. - VBScript
On Errorform (16:12):on_error_resume_nextandon_erroronhost-context.vbs, both rejected. Claude stopped there. The other open VBScript forms (Do … Exit Do,While … Wend) had already been tried on Day 4, so they weren't retried. - Batch hand-off ATT&CK (16:25):
T1059.003for chain and relay, accepted. 261 of 357.
Not attempted by Claude: the 72 JScript questions. Claude's safety filter had stopped one step of the JScript analysis on Day 2, and we didn't work around it, so that part went to the Homebase agent.
The Homebase agent, 2026-10-07 to 2026-10-09: 92 more
- JScript (2026-10-07 17:58 UTC): the agent's static JScript analyser decoded the plans and answered all 72 in four batches. 333 of 357.
- Batch and VBScript
control,expansion(2026-10-08 and 2026-10-09): one answer per submit, from the agent's Batch and VBScript wording add-ons. Accepted:errorlevel_guard×3 andcall_subroutine×4 (Batch),do_once×3 andwhile_once×2 (VBScript), the seven open Batch expansions (delayed,call_double,for_indirect, which follow the fragment variable family), andgoto_dispatchonrelay-handoff.cmd. That left 353 of 357. The Batch wording work stayed with the agent partly because Claude's safety filter stopped one step of that analysis, and we didn't work around it. - What didn't work: 21 rejected submits. 15 names on
vbscript-benign-controlalone (resume_next,error_suppression,error_trapping,error_handler,scoped_error_handler,continue_on_error,capture_and_raise…). 4 onvbscript-dropper-control, one of them an accidental resend the agent reported itself. 1 onvbscript-uploader-control. 1 onbatch-launcher-control(h_fragment_for_loop). After that run, our runner was changed to refuse any value already rejected for that question.
The last four, 2026-10-09 evening (ET), Claude and Zwesty from our Kali VM
The open answers: three VBScript scripts with the On Error Resume Next form, and local-dispatch.bat. Zwesty and Claude worked back from the answers that had been accepted.
error_guard(00:34 UTC): accepted. Zwesty's idea: VBScript'sOn Error Resume Nextis the error check, the same idea as Batch'sif errorlevel 1 exit /b 1, which waserrorlevel_guard. So mirror it. It was the first timeerror_guardwas accepted anywhere. It was then sent to dropper and uploader: both accepted, 356 of 357.local-dispatch.bat. Its slot between:HUNTRESS_SEPARATOR_DONEandecho(holds nothing. The rebuild is afor %%G in (0 1 2 3) do call setline, and an& rem HUNTRESS quote compounddecoy sits elsewhere in the file.for_loop(the FOR rebuild names it): rejected.no_wrapper: rejected.- Then Claude sorted every open Batch script by what was really in the slot. There were five kinds: call block, errorlevel, dispatch,
if defined, and empty. The "nothing" words tried earlier (straight_line,linear…) had all gone to anif definedscript, never to an empty-slot one. Candidates came from the generator's own "plain" words in other fields. The best was the JScriptinvocation-stylevaluedirect. direct(00:48:18 UTC): CORRECT, 357/357, flag405fa223…aedc. Day 5 was the first of the seven Script days to finish.directwas then accepted on 14 more questions across Days 3, 4, 6, 7, and 8, and finished Day 4. No support ticket was needed for any of the labels.
25 wrong submits in all on this day: 2 by Claude on 2026-10-07, 21 by the Homebase agent, and 2 by Claude on the last evening. Apart from the one duplicate, each was a deliberate test. The Python and JScript parts had none.
Solvers: our Day 5 solver wraps Dim and Dimmer's and adds the Python part, using our Python fixture decoder; a small helper sends a chosen subset. Its 3 tests check that it reproduces exactly the 259 answers accepted on the first day, that a wrong XOR key in decode_ stops the solver, and that a non-literal MATERIAL is refused. The timed runs used our runner with the solver chain (Power Play → Dim and Dimmer → Hatch Careful → Shell We Begin → Node by Node) plus the JScript, Batch-wording, and VBScript-control add-ons.
Time Trial
Timings below are the client's own, not the server's. The raw console output wasn't kept, so the blocks are built from the request log plus the TOTAL time the run reported.
attempt 1 started_at 2026-10-10T01:13:42.760Z
stopped before submit: batch-impact-control and batch-relay-control carried the
set "HUNTRESS_READY=1" + if defined wrapper, which had no label yet. Expired, nothing sent
retry started_at 2026-10-10T01:39:54.467Z (deadline not in the local logs)
coverage 357/357
submit 01:39:55Z, 357 answers
TOTAL 2.55 s (client side)
result CORRECT flag 195b396f…7e95
The readiness wrapper was named if_guard between the two attempts (on Day 3's challenge), from the pattern "a keyword in the code + one of five second words". 68 names had been rejected before it.
Mastery
started_at 2026-10-10T01:39:59.705Z (deadline not in the local logs)
coverage 714/714 (two sets of 357)
submit 01:40:01Z, 714 answers
TOTAL 2.74 s (client side)
result CORRECT flag edae22a8…59b7
Correct on the first attempt.
Time to solve
The Single Solve was completed at 00:48:18 UTC on 2026-10-10, 56 h 40 min after it started: 261 of 357 answers (including all 126 Python ones) were in on the first afternoon, and the last four control labels came two days later. The first Time Trial attempt stopped before submitting and expired; the retry (2.55 s) and the Mastery (2.74 s, first attempt) were both CORRECT by 01:40:01 UTC, 57 h 31 min after the first start.
Takeaways
- Compounding pays off. Dim and Dimmer's solver plus a new Python decoder gave 259 of 357 in under four minutes, including all 126 Python answers with no wrong submit.
- Read constants with a parser, not an interpreter.
ast.literal_evalpulls outMATERIALandCODECwithout running a line of the carrier. The plan inside then names its own carrier, syntax, and execution chain. - The own-interpreter rule held again. Python launchers, chains, and relays are
T1059.006, even when the next stage is PowerShell or Batch. - Labels are a mechanism plus a purpose.
error_guardcame from mirroring the already-acceptederrorlevel_guard, not from describing whatOn Error Resume Nextdoes. 17 names had failed on that one question. - Sort the open questions by what is really there before guessing. The "nothing" words had all been sent to the wrong kind of script. Once the empty-slot script was on its own, the generator's own word
directworked first time. - Timed variants can bring forms the Single Solve never showed. The first Time Trial met the
if definedwrapper. Stopping before submit cost one attempt and sent no wrong answer.