Malware Triage
First phase for every sample. You run static tools on the host, interpret the output, classify the sample, and decide the next phase. Target: 5-15 minutes per sample.
Execution Model
- You run the commands. You have Bash. Execute every command in this skill yourself and interpret the output. Do not ask the user to run tools or paste results unless a tool is missing and cannot be installed.
- Locate skill files. Scripts and reference files ship in this skill's directory. Set
R="${CLAUDE_PLUGIN_ROOT:-<dir containing this SKILL.md>}"once (when installed as a plugin$CLAUDE_PLUGIN_ROOTis set; otherwise it is this skill folder). Your working directory is the user's analysis workspace, so prefix every script path below with$R, e.g.python3 "$R"/scripts/ioc_extract.py. - Never execute the sample. Only static tools touch it:
file,strings,pe_info.py,upx -d,7z l, hashing. Nochmod +x, nowine, no./sample. - Never upload the sample to any service without explicit consent. Hash lookups are fine; file uploads are not.
- Script paths are relative to this skill directory. From the repo/skill root:
malware-triage/scripts/pe_info.py,malware-triage/scripts/hash_calculator.py,scripts/ioc_extract.py. Resolve them from wherever this SKILL.md lives. - Tool check first. Run once per session; note what is missing and degrade rather than stop:
command -v file strings upx 7z yara; python3 -c "import pefile" 2>&1 | tail -1 # pefile missing -> pip install pefile (needed by pe_info.py) - Big outputs: never
cata strings dump. Save to a file,wc -l, thengrep/head. - Ask the user only for: the sample path, context (where it came from, incident ID, anything already known), and reputation results if no MCP server is available.
Workflow
Step 1 — Identify and hash
S=/path/to/sample
file "$S"; ls -l "$S"
python3 malware-triage/scripts/hash_calculator.py "$S"
Record MD5/SHA1/SHA256, size, file output. Then route on the file output:
file says |
Action |
|---|---|
Mono/.Net assembly |
Record hashes, hand back to orchestrator → specialized-file-analyzer (check this before PE) |
Microsoft Office, PDF document, ELF, MS Windows shortcut, Zip/RAR/7-zip, ISO 9660, Microsoft Disk Image, HTML document, script/text |
Record hashes, hand back to orchestrator → specialized-file-analyzer |
PE32 / PE32+ (not .NET) |
Continue below |
data / unrecognized |
Continue at Unknown Files |
Step 2 — Reputation
- MCP available (VirusTotal, Threat Intel / MalwareBazaar): look up the SHA256. Record detection ratio, family labels, first-seen date, tags. Look up any imphash and section hashes too.
- No MCP: give the user the SHA256 and ask them to check VirusTotal / MalwareBazaar while you continue. Do not block on it. Zero detections on a fresh hash means nothing; 0 detections on an old hash is informative.
- Reputation is a hint, not the verdict. Static findings override AV labels.
Step 3 — PE static summary
python3 malware-triage/scripts/pe_info.py "$S" # add --json for structured output
Read every section of the output. What matters:
| Output | Interpretation |
|---|---|
.NET: YES |
Stop, route to specialized-file-analyzer |
| Compile timestamp in the future / before 2000 | Forged; ignore for dating. Rich header linker info is harder to fake |
Overall entropy > 7.0, or executable section > 6.8, or ! markers |
Packed/encrypted — see Packed Samples |
Entry point outside .text/CODE, or in last section |
Packer stub or appended code |
Import count < 10, or only LoadLibrary/GetProcAddress/VirtualAlloc |
Runtime API resolution — packed or deliberately hiding imports |
| Suspicious import categories (injection, keylogging, network, anti-debug, crypto, persistence) | Map each to a capability; cross-check with references/indicators.md |
| Overlay present | Appended data: config, second-stage payload, or installer archive. Carve it: python3 -c "..." using the reported offset, then file the result |
| Signature present | Verify claimed signer; revoked/self-signed/mismatched = red flag. Unsigned is neutral |
| PDB path | Project/user names, build environment — high-value attribution string |
| Resources with high entropy or large size | Embedded payload. Note type/size; extract in deep analysis |
| TLS callbacks | Code runs before entry point — anti-debug or early unpacking |
| Version info mimicking Microsoft/Adobe/etc. while unsigned | Masquerading |
Step 4 — Strings and IOCs
W=triage_$(basename "$S") # work dir
mkdir -p "$W"
strings -a -n 6 "$S" > "$W/strings_ascii.txt"
strings -a -n 6 -e l "$S" > "$W/strings_wide.txt" # UTF-16LE, essential for Windows binaries
wc -l "$W"/strings_*.txt
python3 scripts/ioc_extract.py "$W"/strings_*.txt # defanged IPs, domains, URLs, emails, hashes, reg keys, paths
Then targeted greps (adjust after reading the ioc_extract output):
grep -ihE 'HKEY_|HKLM|HKCU|CurrentVersion\\Run|schtasks|sc create|netsh|vssadmin|bcdedit|wmic' "$W"/strings_*.txt | sort -u | head -40
grep -ihE 'user-agent|mozilla/|POST |GET |Content-Type|\.php|\.onion|/gate|/panel' "$W"/strings_*.txt | sort -u | head -40
grep -ihE 'mutex|Global\\|Local\\|\\pipe' "$W"/strings_*.txt | sort -u | head -20
grep -ihE 'IsDebuggerPresent|VMware|VBox|vbox|QEMU|Xen|sandbox|SbieDll|wireshark|procmon|ollydbg|x64dbg' "$W"/strings_*.txt | sort -u
grep -ihE 'bitcoin|monero|\.onion|decrypt|ransom|your files|README' "$W"/strings_*.txt | sort -u | head -20
grep -ihE '\.exe|\.dll|\.bat|\.ps1|\.vbs|\.tmp' "$W"/strings_*.txt | grep -viE 'kernel32|ntdll|user32|advapi32|msvcr|api-ms-win' | sort -u | head -40
Note what is absent too: a binary with network imports but no URL/IP strings is decrypting its config at runtime.
Suspicious-API and string categories: references/indicators.md.
Packed Samples
Triggered by Step 3 entropy/import findings.
| Packer | Indicators | Unpack on host? |
|---|---|---|
| UPX | UPX0/UPX1 sections, UPX! in overlay |
Yes: upx -d "$S" -o "$W/unpacked.exe" |
| Themida / WinLicense | .themida, .winlicense sections, huge protected section |
No — dynamic analysis |
| VMProtect | .vmp0/.vmp1 |
No — dynamic analysis |
| ASPack / MPRESS / Petite / Enigma / Upack | named sections (.aspack, .MPRESS1, .petite, .enigma1, .Upack) |
Rarely — dynamic analysis |
| Custom / unknown | high entropy, few imports, no known section names | No — dynamic analysis |
If UPX unpacking succeeds: hash the unpacked file, re-run Steps 3-4 on it, record both hashes (packed = delivery artifact, unpacked = what matches families). If upx -d fails, the UPX header was tampered; treat as custom.
Set sophistication: known packer → at least Moderate; commercial protector → Advanced; custom packer → Advanced/APT. Add "requires dynamic analysis to reveal imports/strings" to next steps.
Step 5 — Classify
Combine imports, strings, reputation, and context. Consult references/indicators.md for the patterns.
| Type | Typical evidence |
|---|---|
| Trojan / RAT / Backdoor | socket or WinHTTP + command dispatch strings, screen/keyboard APIs, persistence |
| Ransomware | Crypt* APIs, file enumeration, vssadmin delete shadows, ransom-note strings, wallet addresses |
| Infostealer | browser profile paths (Login Data, cookies.sqlite), wallet dirs, CryptUnprotectData, exfil URL |
| Dropper / Loader | small binary, high-entropy resource or overlay, URLDownloadToFile, WriteFile + CreateProcess, few other capabilities |
| Cryptominer | pool URLs (stratum+tcp://), xmrig, high CPU-related config strings |
| Worm | SMB/RPC APIs, network share enumeration, removable-drive checks |
Threat level: Critical (destructive, ransomware, APT) / High (data theft, full compromise) / Medium (limited capability, targeted) / Low (adware, PUP, commodity). Sophistication: Simple / Moderate / Advanced / APT-level, driven by protection, evasion, and custom code. Verdict: Malicious / Suspicious / Benign. Benign requires positive evidence (valid signature from a plausible vendor, expected imports for its stated purpose, reputation clean over time), not just absence of red flags.
Step 6 — Predict behavior
Turn static evidence into a watch list for the dynamic phase. Each prediction names the evidence:
- Process: child processes or injection expected? Which targets (
CreateRemoteThread+OpenProcess→ injection;CreateProcess+cmd/powershellstrings → LOLBin chain)? - Files: paths from strings; temp/AppData drops if
GetTempPath/SHGetFolderPathimported. - Registry: Run keys, service creation, IFEO from strings/imports.
- Network: protocol (WinHTTP/WinINet → HTTP(S);
socket/connect→ raw TCP;DnsQueryonly → DNS tunnelling/DGA), hard-coded C2 vs runtime-decrypted, likely beacon. - Persistence: mechanism and location.
- Evasion: anti-VM/anti-debug strings → plan for bypass (
malware-dynamic-analysis/references/anti_analysis_bypass.md).
These predictions become the Procmon/Sysmon filters and the observation plan for malware-dynamic-analysis.
Step 7 — Priority and next phase
| Priority | When |
|---|---|
| Immediate | Unknown/undetected, incident-related, destructive capability, APT indicators, matches current threat intel |
| Standard | Known family variant, commodity malware with existing coverage |
| Low | Well-documented sample, adware/PUP, old, likely false positive |
Next phase: malware-dynamic-analysis for PE files that are packed, have runtime-resolved config, or need behavioral confirmation; detection-engineer + malware-report-writer directly when the sample is a known variant and static IOCs suffice; Benign closes the sample.
Unknown Files
file returned data or something unhelpful:
xxd "$S" | head -20 # magic bytes, structure
strings -a -n 8 "$S" | head -50
python3 - "$S" <<'EOF' # single-byte XOR brute force for a hidden PE
import sys; b=open(sys.argv[1],'rb').read()
for k in range(1,256):
d=bytes(x^k for x in b[:4096])
if d[:2]==b'MZ' or b'This program cannot' in d: print(f"XOR key 0x{k:02x} reveals PE header")
EOF
Also check: base64 blob ([A-Za-z0-9+/]{100,}), gzip/zlib magic (1f 8b, 78 9c), reversed PE (ZM at end), shellcode (starts with fc e8 / e8 00 00 00 00 / 55 8b ec). Decode what you find, file the result, and re-route.
Batch Triage
For N samples, do a quick pass on all before deep-diving any:
for f in samples/*; do
echo "== $f"; file "$f"; sha256sum "$f"
python3 malware-triage/scripts/pe_info.py "$f" 2>/dev/null | grep -E '^\.NET:|packed|imported functions'
done
Produce a ranking table (sample, type, packed, detections, priority, one-line reason), then run the full workflow on Immediate samples first.
Output
- Print the triage report (template below, filled in — no placeholders left).
- Append to
analysis_state.mdin the user's working directory using the orchestrator's structure: hashes, type, priority, classification, threat level, triage findings, IOCs (defanged), next-phase recommendation. - Recommend the next phase with reasoning, then wait for the user.
## Triage Report — [filename]
**Hashes:** MD5 … / SHA1 … / SHA256 … **Size:** … bytes **Type:** … **Packed:** No | Yes (packer)
**Reputation:** VT x/y, family label(s), first seen … | not checked
### Static Findings
- Compile time / linker / signature / PDB
- Sections + entropy (flag anomalies)
- Suspicious imports by category → capability
- Notable strings: URLs, IPs, paths, registry, mutex, UA, commands
- Resources / overlay / TLS callbacks
### Classification
**Verdict:** Malicious | Suspicious | Benign **Type:** … **Threat:** Critical | High | Medium | Low **Sophistication:** …
**Capabilities:** …
### Predicted Behavior
Process / Files / Registry / Network / Persistence / Evasion — each with the evidence behind it
### Initial IOCs (defanged)
File: hashes, imphash, dropped names · Network: … · Host: registry, paths, mutex
### Recommendation
**Priority:** … **Next phase:** … **Why:** …
Quality Gate
Before handing back: all three hashes; file type; packing status with evidence; imports categorized; strings run in both ASCII and UTF-16; IOCs extracted and defanged; classification with reasoning; predictions tied to evidence; state file updated.
References
references/indicators.md— suspicious API imports, string patterns, behavioral indicators, packer signatures, classification patternsreferences/triage_checklist.md— full checklist, decision tree, dynamic go/no-go criteria, tool referencescripts/pe_info.py— PE header/section/import/resource/overlay/signature summary (needspefile)scripts/hash_calculator.py— MD5/SHA1/SHA256../scripts/ioc_extract.py— IOC extraction and defanging from any text