yara-memory-hunting · git:20260504.2a64374 · 2026-05-04 · sha256 db3f7f5f39068a56
yara-memory-hunting git:20260504.2a64374A
Immutable. This exact content is served forever at /api/v1/blob/db3f7f5f39068a56.
---
name: yara-memory-hunting
description: Use YARA against memory images to locate known malware, C2 frameworks, and custom IoCs. Covers rule selection, scoping (process vs whole image), false-positive control, and pivoting hits into structured findings.
---
# YARA Memory Hunting
Maps to [MITRE ATT&CK T1059](https://attack.mitre.org/techniques/T1059/) (Command and Scripting Interpreter) and [T1027](https://attack.mitre.org/techniques/T1027/) (Obfuscated Files or Information) when classifying samples. For rule sources see Elastic [protections-artifacts](https://github.com/elastic/protections-artifacts), [YARA-Forge](https://yarahq.github.io/), and vendor reports.
## When to Use
- You have IoCs (strings, byte patterns, config layouts) and want to confirm / scope
- Triage surfaced a suspect process and you want to classify the family
- Sweeping an image for commodity C2 frameworks (Cobalt Strike, Sliver, Brute Ratel, Metasploit, etc.)
## Scoping Decisions First
| Goal | Scope | Rule style |
|---|---|---|
| "Is *this* process X?" | Single PID via `pid=N` | Specific, tight strings |
| "Where in the image does X live?" | Whole image | Broader, paired-condition rules |
| "Is there anything bad here at all?" | Whole image | Curated commodity-framework pack |
Whole-image scans are slow and noisy. Use them when you don't yet have a suspect, then narrow to PIDs.
## Workflow
### 1. Pick the rule set
For commodity C2 and known malware, start with community-maintained packs:
- Elastic Protections Artifacts (`protections-artifacts/yara/rules/`)
- YARA-Forge aggregate
- Vendor-published rules attached to report writeups (Mandiant, Volexity, CrowdStrike blogs)
For custom IoCs derived from triage, **write the rule inline** — don't manage a file for a one-shot.
### 2. Run the scan
```
volatility_yara_scan(image, rules_file="/path/to/rules.yar", pid=None)
```
or
```
volatility_yara_scan(image, rules_inline="""
rule MyCustom { strings: $a = "CONFIG:" condition: $a }
""", pid=1234)
```
Exactly one of `rules_file` / `rules_inline`. The `pid` filter dramatically speeds up scans when you have a suspect.
### 3. Interpret hits
Each hit gives you: rule name, PID, process, virtual address, matching string(s). Triage each:
- **High specificity rule (tight strings, multi-condition)** — trust the verdict, move to analysis
- **Generic rule (single common string)** — corroborate before acting
- **Hit on a `csrss`, `services`, `svchost`** — elevated priority; system processes don't normally contain arbitrary strings
- **Hit on a user-mode browser / IDE / chat app** — often benign (strings in page cache, clipboard, logs)
### 4. Pivot
For each confirmed hit:
- `volatility_malfind --pid N` to see if the hit sits inside an injected region
- `volatility_dll_list --pid N` to see what module the offset maps into (if any)
- `volatility_dump_process --pid N --mode vad` and carve around the hit offset for offline triage
- Derive new IoCs (neighboring strings, config blobs, mutex names) and re-scan
## Rule Patterns That Work Well Against Memory
### Config-block pattern (Cobalt Strike / Sliver style)
```yara
rule CS_Beacon_Config {
strings:
$magic = { 2E 2E 2E 2E ?? ?? 2E 2E 2E 2E } // XOR-key surrounded config
$sleep = "Beacon_mask" ascii wide
$post = "post-ex" ascii wide
condition:
2 of them
}
```
### Decoded-in-memory string pattern
Decrypted strings appear in memory even if encrypted on disk. Target what only shows up after unpacking:
```yara
rule FamilyX_Runtime_Strings {
strings:
$cmd1 = "!@#run_payload@#!" ascii wide
$cmd2 = "!@#beacon_check@#!" ascii wide
$err = "FamX: failed to allocate" ascii wide
condition:
any of them
}
```
### API-resolution pattern
Malware that dynamically resolves APIs leaves the names in memory:
```yara
rule Injector_API_Resolution {
strings:
$a1 = "NtAllocateVirtualMemory"
$a2 = "NtWriteVirtualMemory"
$a3 = "NtCreateThreadEx"
$a4 = "RtlAdjustPrivilege"
condition:
3 of them
}
```
Benign software rarely stores these as plaintext strings (they're in import tables, not data).
### Mutex / named-pipe pattern
```yara
rule Beacon_NamedPipe {
strings:
$p1 = "\\\\.\\pipe\\msagent_" ascii wide
$p2 = "\\\\.\\pipe\\postex_" ascii wide
$p3 = "\\\\.\\pipe\\status_" ascii wide
condition:
any of them
}
```
## False-Positive Control
- Never rely on single-string rules unless the string is a hash / GUID / long unique phrase
- Pair "would appear in malware" strings with "would *not* appear in benign software" strings
- Scope by process type: system processes (`lsass`, `services`, `svchost`) have a tiny legitimate string set; hits there are almost always real
- Every hit in a browser, email client, or IDE needs corroboration — those processes scrape the internet into memory and will match almost anything
## Timeouts and Budgets
Whole-image YARA is slow. The MCP defaults to 600s; raise `timeout` for larger images or heavy rule packs. When iterating rules, scope to `pid=N` first to get fast feedback, then widen.
## Common Pitfalls
- Rules with only wide strings miss ASCII-compiled binaries; use `ascii wide` liberally
- Very long strings may not match if the memory range they land in is paged out — prefer shorter signatures with stronger condition logic
- Vol3 yarascan wants a **compiled or source `.yar` file**, not a compiled `.yarc` — pass source