# 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. Use `R` established by the orchestrator; it points to the package root. 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. No `chmod +x`, no `wine`, 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: ```bash command -v file strings upx 7z yara; python3 -c "import pefile" 2>&1 | tail -1 # pefile missing -> use fallback tooling or ask before installing an approved/pinned package ``` - **Big outputs:** never `cat` a strings dump. Save to a file, `wc -l`, then `grep`/`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 ```bash S=/path/to/sample file "$S"; ls -l "$S" python3 "$R"/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 ```bash python3 "$R"/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 ```bash 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 "$R"/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): ```bash 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`/`powershell` strings → LOLBin chain)? - **Files:** paths from strings; temp/AppData drops if `GetTempPath`/`SHGetFolderPath` imported. - **Registry:** Run keys, service creation, IFEO from strings/imports. - **Network:** protocol (WinHTTP/WinINet → HTTP(S); `socket`/`connect` → raw TCP; `DnsQuery` only → DNS tunnelling/DGA), hard-coded C2 vs runtime-decrypted, likely beacon. - **Persistence:** mechanism and location. - **Evasion:** anti-VM/anti-debug strings → plan for bypass (`$R/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: ```bash 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: ```bash for f in samples/*; do echo "== $f"; file "$f"; sha256sum "$f" python3 "$R"/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 1. **Print the triage report** (template below, filled in — no placeholders left). 2. **Append to `analysis_state.md`** in the user's working directory using the orchestrator's structure: hashes, type, priority, classification, threat level, triage findings, IOCs (defanged), next-phase recommendation. 3. **Recommend the next phase** with reasoning, then wait for the user. ```markdown ## 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 patterns - `references/triage_checklist.md` — full checklist, decision tree, dynamic go/no-go criteria, tool reference - `scripts/pe_info.py` — PE header/section/import/resource/overlay/signature summary (needs `pefile`) - `scripts/hash_calculator.py` — MD5/SHA1/SHA256 - `$R/scripts/ioc_extract.py` — IOC extraction and defanging from any text