The challenge
Three PowerShell specimens are on the table, and none of them will ever run. That's fine, because you only need to read them. Classify each script's intent, map its primary ATT&CK technique, and pull out the exact indicators sitting in plain sight.
15 questions, five per script (resource-sample.ps1, collection-sample.ps1, and impact-sample.ps1):
| Per script | Answer format |
|---|---|
| Scenario | One of resource_, synthetic_, or synthetic_ |
| Primary ATT&CK technique | T#### or T####. |
| Three exact indicators | Resource: URL, wallet, mutex. Collection: URL, first filename, user agent. Impact: extension, recovery-note name, URL |
The Time Trial had the same 15 questions (the page said "under 8 seconds"). Mastery had 30, as two sets of 15 ("2 tasks in under 7 seconds").
Reading the prompt: what it said we'd need
| Clue | What it pointed to | What I did about it |
|---|---|---|
| "none of them will ever run … you only need to read them" | Static analysis only | Never execute. Kali ships pwsh, and it stayed unused. The scripts were decrypted to memory and read as text |
| "Classify each script's intent" | Identify the objective: mining, theft, ransomware | Look at what the code would touch and use, not the file names |
| "map its primary ATT&CK technique" | One best-fit technique per script, which is a judgement call | Map from evidence, then work out parent vs sub-technique (see below) |
| "exact indicators sitting in plain sight" | IOCs that are present, maybe lightly encoded, and must be copied byte for byte | Decode, then extract programmatically. Never retype |
| "native signals" (in the challenge ID) | The .NET types each script references are the behavioural signal | Base the classification on the $nativeTypes list |
Safety: a locked sample archive
evidence.zip was AES-256 encrypted (zip method 99, WinZip AE-2). The Game Master API gave no password. The archive's unencrypted parts held no hint either: no comments, and only the standard AES extra field.

The CourseStack lesson page had it: "The password for this archive is infected." That's the security industry's standard password for zipped malware samples. The convention keeps antivirus and mail filters from acting on samples in transit.
Handling: decrypted with 7z x -pinfected -so straight to stdout, so no .ps1 ever touched the disk. Nothing was executed. Artifacts stayed in the analysis VM.
What was in the files
Every script, in every mode, was one of three fixed templates. Each defines a single function that:
1. decodes a Base64 JSON "descriptor" holding the indicators,
2. passes it through a $reconstruct block (the identity function in every sample we saw),
3. returns it alongside a $nativeTypes list: the .NET classes a real version would use.
The resource sample, with the Base64 shortened:
# Huntress Script Triage -- inert composite-analysis fixture (windows_powershell_5_1)
function artifact_6ad60457ad {
$encodedMaterial = [Convert]::FromBase64String('eyJhIjoiYTg4YmZkYTcy…')
$reconstruct = {
param([byte[]]$candidateBytes)
return [byte[]]$candidateBytes
}
[byte[]]$plainMaterial = @(& $reconstruct $encodedMaterial)
$decodedText = [Text.Encoding]::UTF8.GetString($plainMaterial)
$descriptor = $decodedText | ConvertFrom-Json
$record = $descriptor
$nativeTypes = @([Diagnostics.Process],[Microsoft.Win32.Registry],[Net.WebClient])
[pscustomobject]@{ A = $record.a; B = $record.b; C = $record.c; D = $record.d; Native = $nativeTypes }
}
The three samples, decoded (Single Solve values, defanged):
| Sample | $nativeTypes |
Descriptor contents |
|---|---|---|
| resource | Diagnostics., Microsoft., Net. |
URL hxxps:/ · wallet wallet_ · file update- · Run key HKCU\Software\HuntressLab\Run\03ce326948 · user agent Mozilla/ · 64 (a thread count) · mutex Global\HuntressLab_ |
| collection | IO., IO., Net. |
URL hxxps:/ · target files ["update- · archive stage- · mutex · user agent Mozilla/ |
| impact | IO., Security., IO. |
extension . · note RECOVERY- · URL hxxps:/ · the same ["update- list · mutex |
Two details that mattered:
- JSON escaping: the descriptor shows Global\\HuntressLab_…, but the real value has one backslash.
- "Composite" fixtures: strings like Login Data and Cookies appear in both the collection and impact samples. I first wrote them off as decoys. They turned out to be the key to the hardest answer.
Was every mode the same style, just with different data?
Yes. I compared all 9 scripts (3 per mode, across 4 zips). With the random function name and the Base64 removed, there were exactly 3 distinct script skeletons. The $nativeTypes list per sample type never changed, and neither did the descriptor's layout: in every resource sample, b is the URL, c the wallet, and h the mutex. Only the values changed: URLs, wallets, mutexes, user-agent versions, and the random hex IDs.
So the scenario and ATT&CK answers were the same in all three modes: T1496, T1555.003, and T1486. That was tested, not assumed: the solver matched each script's type list against the three Game Master-confirmed lists, and would have stopped on any mismatch.
Single Solve: 12 indicators straight away, ATT&CK the hard way
The scenarios and indicators were straightforward:
- The scenarios came from the native types. Process, Registry, and WebClient plus a wallet and mutex mean mining. File, ZipFile, and HttpWebRequest mean gather, pack, and send. File, AES, and DirectoryInfo plus .locked and a recovery note mean ransomware.
- The indicators were picked out of the decoded descriptor by shape: https://, wallet_, Global\, Mozilla/, and so on. "The first filename" was the first entry of the collection target list.
The ATT&CK IDs were the hard part. The Game Master judges all 15 answers together and says only incorrect, with no detail. Two full submits failed (collection as T1005, then T1560).
The breakthrough was a partial submit. I sent only the 12 non-ATT&CK answers, and the reply was accepted, remaining: 3. That proved all 12 indicators and scenarios were right, and that Single Solve keeps partial progress, the same as Mastery. From then on, each ATT&CK answer could be tested on its own.
Each candidate was reasoned in advance, never cycled through blindly. The event forbids brute-forcing:
| Technique | Tried alone | Result | Lesson |
|---|---|---|---|
| impact | T1486 Data Encrypted for Impact |
✅ accepted | Textbook: AES, directory walking, a renamed extension, a note |
| resource | T1496. Compute Hijacking |
❌ | Nothing in the sample proves which resource is hijacked |
| resource | T1496 Resource Hijacking |
✅ accepted | The parent, because the evidence doesn't narrow it |
| collection | T1005, T1560, T1560., T1119, T1074. |
❌ all five | These describe the mechanism (read, zip, stage), not the objective |
| collection | T1555 Credentials from Password Stores |
❌ | The right family, but too vague |
| collection | T1555. Credentials from Web Browsers |
✅ CORRECT | Login Data and Cookies are Chrome's password and cookie stores: the evidence names the browser |
The reframe that cracked collection: the "first filename" answer had been accepted from the list ["update-…dat", "Login Data", "Cookies"]. So that list was the collection script's real target list, not decoys. A script that reads Chrome's credential stores, zips them, and sends them is browser credential theft (T1555.003), even though the challenge labelled the scenario "collection".
12 requests in total: 2 full submits, 1 diagnostic, and 9 single ATT&CK tests. CORRECT, flag 1fb306c1…11a4.
Parent or sub-technique? The rule this taught
Use the most specific technique the evidence actually proves, and no more specific. The test: "If I picked the sub-technique, could I point to the line that proves it?"
Login Data/Cookiesprove "browser", so the sub-techniqueT1555.003wins. The parent was rejected.- A wallet and a mutex prove "mining", but not CPU vs GPU vs cloud, so the parent
T1496wins. The sub-technique was rejected.
Time Trial: 1.49 s

With the judgement calls confirmed, the timed modes were pure extraction. Our solver recognises each script by its $nativeTypes set, using only the three confirmed pairs. It pulls indicators by shape, and stops on an unknown type list, a changed $reconstruct block, or an indicator not found exactly once.
started_at 2026-10-02T20:57:14.179Z deadline 2026-10-02T20:57:44.179Z
artifact evidence.zip 2472 bytes sha256 76980d6a5ad47ff4… (checked)
resource-attack: T1496 collection-attack: T1555.003 impact-attack: T1486
… 12 indicators …
start 455 ms
artifact 322 ms
solve 9 ms (including AES decryption of all three scripts)
submit 700 ms
TOTAL 1485 ms
result CORRECT flag c46b165d…2505
Mastery: 2.15 s, first attempt
The delivery was one outer zip holding evidence-01.zip and evidence-02.zip, each AES-encrypted. That's one of the two delivery shapes the solver was tested against before the first attempt, a lesson carried over from the earlier Day 1 challenges. Each set was answered only from its own zip.
started_at 2026-10-02T20:58:52.474Z deadline 2026-10-02T20:59:22.474Z
artifact script-analysis-day-01-native-signals-mastery.zip 5166 bytes sha256 f96ad10b598a63dc… (checked)
… 30 answers (2 sets × 15) …
start 530 ms
artifact 763 ms
solve 22 ms
submit 829 ms
TOTAL 2145 ms
result CORRECT flag 6fe2d6ec…8a52
Time to solve
All three modes were solved on 2026-10-02 by 20:58:54 UTC, 31 min 45 s after the Single Solve started. The Single Solve took 19 min 49 s and 12 requests, most of them spent pinning down the ATT&CK IDs; the Time Trial (1.49 s) and the Mastery (2.15 s) each went through on the first submit.
Takeaways
- Read the lesson page as well as the API. The archive password was only there.
- When a checker only says "incorrect", use partial submits to find out which answers are wrong. Then test the uncertain ones one at a time.
- Reason every guess in advance. Twelve requests, each with a stated reason, isn't brute force. Cycling through technique IDs would be.
- Map ATT&CK from the objective, not the mechanism. Zipping and uploading is how. Stealing browser credentials is what.
- Pick the most specific technique the evidence proves, and stop there.
- Re-examine your "decoys" once an answer has been accepted. If a validated answer came from them, they're evidence.
- Once the judgement calls are confirmed, timed modes are just extraction. Build the solver around the confirmed answers, and have it stop on anything new.