The challenge
"Some credential-access-shaped code keeps rummaging through the browser's cupboards, and synthetic store artifacts are sitting there waiting. Connect each code path to the artifact it targets, and find the reference that points at a store that was never there."
1,026 questions about the specimens in one evidence.zip (Day 14 had 971). The lane compounds: every earlier day's specimens come back. Today adds a new polyglot host, Perl: four polyglot-perl-* specimens plus a host-perl-cgi one, 11 questions each = 55 new questions.
| Question family | Example question | Format |
|---|---|---|
Perl role, attack |
"Classify the inert source role."; map the decoded behaviour graph to one technique by the stated precedence | enum; ATT&CK ID |
Perl adapter, campaign, url / file-, stage-, steps |
"Recover the exact url indicator." | exact text, integer, >-joined tokens |
Perl host-, obfuscation, stage-, next- |
"Identify the script host or webshell source container." | exact text |
collection- (since Day 1) |
Treat access to the browser credential store as the central behaviour | ATT&CK ID (T1555.003; the broader T1005 also accepted) |
batch- (since Day 3) |
"Classify only the expansion form used by the descriptor reconstruction statement." | one label; the question now lists percent, delayed, call_, for_ |
Reading the prompt: what it said we'd need
| Clue | What it pointed to | What I did about it |
|---|---|---|
| "credential-access-shaped code", "browser's cupboards" | Credentials from web browsers (T1555.003): the collection specimen's descriptor names the Chromium stores Login Data and Cookies |
Nothing new: collection- = T1555. has been answered since Day 1 |
| "synthetic store artifacts", "a store that was never there" | We expected new questions about store files | None came. No question asked about a store; the question ids and text were the same in all three modes. The prompt's theme didn't match new fields this time |
| "Connect each code path to the artifact it targets" | Per-specimen indicators (URL, file, note) from each decoded descriptor | Our core solver already reads those |
| (unstated) a new host | Perl polyglots, the same template as PHP, Lua, AutoIt, MSBuild | The Day 8 core read the HUNTRESS_ plan in each Perl file without a change |
Safety
- The specimens are inert, malware-styled scripts. They were read as text in memory on our Kali VM; nothing was run, and nothing was copied off the VM.
- This write-up was built from our own records of the run. The only artifact text quoted is two short, defanged lines.
What was in the files
- Single Solve: one password-protected
evidence.zip, 116 members: every lane-to-date specimen (PowerShell, Bash, Python, Node, JScript, VBScript, Batch, and the polyglot hosts) plus the five new Perl files. - The credential-store clue lives in the collection specimen. Its PowerShell decodes a Base64 JSON descriptor holding a URL, a file list (
update-<id>.dat,Login Data,Cookies), a stage zip name, a mutex, and a user agent. The.NETtypes it lists (IO.File,IO.Compression.ZipFile,Net.HttpWebRequest) tell the story: read the store, zip it, send it. - The Perl specimens carry the plan twice: a
# HUNTRESS_ANALYSIS_V1:B64-…header and the same Base64 string in amy $descriptor_<id> = "…";variable, wrapped in abuild_plan_<id>sub. Decoded, it's the familiarkey=value|…plan (language, role, attack, adapter, profiles, steps, campaign,url=hxxps://perl-<id>.example[.]invalid/stage). - Timed variants: the same 1,026 questions with identical text and formats, but 115 of 116 files differ (new values and new forms everywhere). Mastery was 5 sets, 5,130 questions.
- The Batch line that mattered (Time Trial,
host-context.cmd), inside a:HUNTRESS_RECOVERsubroutine called once byfor /l %%Q in (1,1,1) do call :HUNTRESS_RECOVER:set "cfg=%h0%%h1%%h2%%h3%".
Single Solve
Our automated morning solver (a scheduled script on our Kali VM) did most of it; a transport timeout left it unfinished.
| Batch | Answers | Result |
|---|---|---|
| core (Day 8 chain) | 899, the 55 Perl answers among them | accepted (remaining 127) |
| Batch vocabulary, one at a time | 10 | 9 accepted (remaining 118); the 10th timed out |
| JScript plug-ins (plain, styles, roles, attack) | 60 + 27 + 9 + 9 | accepted (helper agent) |
| Batch vocabulary, one at a time | 6 | accepted (helper agent) |
| VBScript control, one at a time | 6 | accepted; the last → CORRECT (helper agent) |
- 13:01:17 UTC: the 10th single submit (
batch-uploader-control=call_subroutine) stalled. curl gave up at 60 s with 74 KB of a 567 KB reply received. The solver dropped the lane on the transport error. - Did it land?
statusshows no per-answer progress, so the Claude helper agent didn't resend. It sent the next batch first: that reply's cumulativeaccepted_idsheldbatch-uploader-control(118 − 1 − 60 = 57 remaining). It had landed, so it wasn't sent again. - Offline coverage before sending: 1,026/1,026, and the core matched every accepted answer. No solver change.
- Single Solve CORRECT at 09:10:50 ET, flag
c6577c2f…0f12. Nothing rejected.
Time Trial
First attempt (ranked): INCORRECT. 50 s clock, 31 unique attempts left. The helper agent stopped there and didn't retry.
started_at 2026-10-15T13:11:29.877Z
deadline 2026-10-15T13:12:19.877Z (50 s, ranked)
answers 1026
start 1035 ms · artifact 608 ms · solve 126 ms · submit 794 ms · TOTAL 2564 ms
stamped +1.84 s after started_at
result incorrect (no detail)
Finding the wrong answer. With no per-answer detail and 115 of 116 files changed, comparing files didn't help. Claude audited offline: every Time Trial label had been accepted on some earlier day; VBScript codecs matched each script's own Codec_* line; Batch control matched the confirmed control-slot rules. Not checkable offline: JScript styles, Batch graphs, adapters.
So we retried untimed and batched: a plain retry on this route has no clock and isn't ranked. Sending plug-in by plug-in, as in a Single Solve, makes the failing batch name the wrong plug-in.
- Core 896 → accepted (remaining 130).
- First plug-in batch rejected:
batch-benign-expansion=delayed. That was the line above: a plain%h0%…rebuild, which our Batch vocabulary maps todelayed. On Day 14 the same line with the same question was accepted asdelayed. - Next candidates in order:
call_double(the h0–h3 fragment-family rule) → incorrect;percent(the literal form, now in the question's own list) → accepted. - Every Batch expansion in this variant wanted the literal form:
delayedwas rejected 7 more times. Thencall_doublewas accepted for the 4call set %%x%%rebuilds andpercentfor the 3%x%ones. The relay needed none. All 13 other plug-in groups were accepted first time.
attempt untimed retry (no deadline, unranked)
started_at 2026-10-15T13:16:26.929Z
submits 40 in this mode (incl. the ranked attempt): 29 accepted, 10 incorrect, 1 CORRECT
result CORRECT 13:20:50Z flag 885e3125…c0d8
Mastery
Untimed and batched too, with the same fallback (literal form first, then delayed).
attempt untimed (no deadline, unranked)
started_at 2026-10-15T13:21:23.086Z
answers 5 sets, 5130 questions, 122 batches
rejected 21 singles, all Batch expansion: 20× delayed, 1× call_double (set 5 chain); each then accepted on the fallback
result CORRECT 13:26:55Z flag 9c5f28ac…9ecb
Time to solve
All three modes were solved by 09:26:55 ET, 25 min 56 s after the Single Solve was started. The Single Solve was finished by hand at 09:10:50 after a transport timeout; the ranked Time Trial was incorrect, so both timed modes were then solved on untimed, unranked retries (Time Trial 09:20:50, Mastery 09:26:55).
Takeaways
- A transport timeout isn't a rejection. The server had replied; we just didn't read it all. The next submit's cumulative
accepted_idsanswered "did it land?" without resending. The solver should check and resume, not stop. - The same line can take a different label on a different variant.
set "cfg=%h0%…"wasdelayedon Day 14 and this morning's Single Solve, andpercenton every timed variant today. A label rule that has always held can still be the wrong one. - To find one wrong answer in 1,026, batch it. An untimed retry sent plug-in by plug-in pointed at the Batch vocabulary in the first rejected batch. Without that the audit had no way to narrow it down.
- A prompt's theme isn't always a new question. "Credential store clues" brought no store questions; the real change was the Perl host, and the existing template solver handled it.
- Cost: both timed modes ended unranked, and this lane's last CORRECT (09:26:55 ET, also the day's finish) came ~25 min later than the solver's usual ~09:02. Open item at the time: our Batch vocabulary still answered
delayedfirst, and Claude was changing it to try the literal form first before the next day's run.