reverse-skills-max: initial public release (restored, redacted, hardened)
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
Apache License 2.0 — see the repository root LICENSE file for the full text.
|
||||
|
||||
Copyright the AnalystAIPack contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
@@ -0,0 +1,92 @@
|
||||
---
|
||||
name: detecting-process-injection-in-memory
|
||||
description: 'Detects process injection in a memory image by identifying private executable regions
|
||||
with no file backing, RWX protections, and modified entry points using Volatility 3 malfind-style
|
||||
analysis. Activates for requests to detect process injection, find injected code in memory, or
|
||||
triage suspicious executable regions in a memory image.'
|
||||
domain: cybersecurity
|
||||
subdomain: malware-analysis
|
||||
tags:
|
||||
- malware-analysis
|
||||
- memory-forensics
|
||||
- process-injection
|
||||
- volatility
|
||||
- malfind
|
||||
version: 1.0.0
|
||||
author: analyst-ai-pack
|
||||
license: Apache-2.0
|
||||
mitre_attack:
|
||||
- T1055
|
||||
- T1055.001
|
||||
- T1055.012
|
||||
d3fend:
|
||||
- D3-PSA
|
||||
- D3-SDA
|
||||
car:
|
||||
- CAR-2014-04-003
|
||||
references:
|
||||
- 'MITRE ATT&CK T1055 Process Injection — https://attack.mitre.org/techniques/T1055/'
|
||||
- 'Volatility 3 windows.malfind — https://volatility3.readthedocs.io/'
|
||||
---
|
||||
|
||||
# Detecting Process Injection in Memory
|
||||
|
||||
## When to Use
|
||||
|
||||
- You have a Windows memory image and want to find injected/hollowed code.
|
||||
- You are triaging private RWX regions, unbacked executable memory, and PE headers in private
|
||||
allocations (classic injection/hollowing signs).
|
||||
|
||||
**Do not use** this on a live production host without authorization — work from an acquired image.
|
||||
This skill interprets analysis output; run Volatility separately on the image.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- A memory image and Volatility 3 (`windows.malfind`, `windows.vadinfo`) installed.
|
||||
|
||||
## Safety & Handling
|
||||
|
||||
- Treat carved injected regions as live malicious code; store them password-protected.
|
||||
|
||||
## Workflow
|
||||
|
||||
### Step 1: Run malfind and capture output
|
||||
|
||||
```bash
|
||||
vol -f memory.raw windows.malfind > malfind.txt
|
||||
```
|
||||
|
||||
### Step 2: Score injection candidates
|
||||
|
||||
```bash
|
||||
python scripts/analyst.py score malfind.txt
|
||||
```
|
||||
|
||||
Flags regions with `PAGE_EXECUTE_READWRITE`, MZ/PE headers in private memory, and shellcode-like
|
||||
disassembly hints, ranking processes by suspicion.
|
||||
|
||||
### Step 3: Corroborate
|
||||
|
||||
Cross-check flagged PIDs with `windows.vadinfo` (private, executable, no mapped file) and the
|
||||
process tree for anomalous parents.
|
||||
|
||||
### Step 4: Carve and route
|
||||
|
||||
Carve the injected region for follow-on shellcode/PE recovery and analysis.
|
||||
|
||||
## Validation
|
||||
|
||||
- Flagged regions are private + executable, not legitimate mapped images.
|
||||
- PE-in-private-memory findings are confirmed by an MZ/`This program` signature.
|
||||
- High-scoring PIDs corroborate with VAD protections and process-tree anomalies.
|
||||
|
||||
## Pitfalls
|
||||
|
||||
- JIT engines (.NET, Java, browsers) legitimately create RWX regions — corroborate before alerting.
|
||||
- Relying on malfind alone; pair with vadinfo and ldrmodules.
|
||||
- Missing hollowing where the region is RX (not RWX) after protection change.
|
||||
|
||||
## References
|
||||
|
||||
- See [`references/api-reference.md`](references/api-reference.md) for the scorer.
|
||||
- ATT&CK T1055 and Volatility 3 docs (linked in frontmatter).
|
||||
@@ -0,0 +1,39 @@
|
||||
# API Reference: Detecting Process Injection in Memory
|
||||
|
||||
## Overview
|
||||
|
||||
`scripts/analyst.py` parses Volatility 3 `windows.malfind` text output and ranks processes by
|
||||
injection indicators: RWX private regions, PE/MZ headers and DOS stubs in private memory, and
|
||||
shellcode prologue bytes in the hexdump. It parses text only and executes nothing.
|
||||
|
||||
## Dependencies
|
||||
|
||||
| Package | Version | Purpose |
|
||||
|---------|---------|---------|
|
||||
| (stdlib) | — | regex, JSON, file IO |
|
||||
| volatility3 | latest | produces the `windows.malfind` input (run separately) |
|
||||
|
||||
## Core functions
|
||||
|
||||
### `score(path)`
|
||||
Returns ranked injection candidates with PID, cumulative score, and contributing reasons.
|
||||
|
||||
## Scoring weights
|
||||
|
||||
- RWX region: +3
|
||||
- PE/MZ in private memory: +3
|
||||
- DOS stub ("This program cannot be run"): +2
|
||||
- Shellcode prologue bytes: +1
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
vol -f memory.raw windows.malfind > malfind.txt
|
||||
python scripts/analyst.py score malfind.txt
|
||||
```
|
||||
|
||||
## Sources
|
||||
|
||||
- MITRE ATT&CK T1055 Process Injection — https://attack.mitre.org/techniques/T1055/
|
||||
- Volatility 3 documentation (windows.malfind, windows.vadinfo) — https://volatility3.readthedocs.io/
|
||||
- malfind RWX / private-memory PE heuristics from published memory-forensics practice
|
||||
@@ -0,0 +1,68 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Score process-injection candidates from Volatility 3 windows.malfind text output.
|
||||
|
||||
Parses malfind's per-region report and ranks processes by injection indicators: RWX protection,
|
||||
MZ/PE header in private memory, and common shellcode prologue bytes in the hexdump. Parses text
|
||||
only; executes nothing.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import re
|
||||
import sys
|
||||
from collections import defaultdict
|
||||
from pathlib import Path
|
||||
|
||||
RE_PID = re.compile(r"\b(?:PID|Pid)\s*[:]?\s*(\d+)", re.I)
|
||||
RE_PROC = re.compile(r"Process[:]?\s+([^\s]+)", re.I)
|
||||
RWX = "PAGE_EXECUTE_READWRITE"
|
||||
SHELLCODE_HINTS = ["55 8b ec", "fc 48 83", "e8 00 00 00 00", "60 89 e5", "31 c0", "eb"]
|
||||
|
||||
|
||||
def score(path: str) -> dict:
|
||||
text = Path(path).read_text(encoding="utf-8-sig", errors="replace")
|
||||
lines = text.splitlines()
|
||||
proc_scores: dict[str, dict] = defaultdict(lambda: {"score": 0, "reasons": set(), "pid": None})
|
||||
current = None
|
||||
for line in lines:
|
||||
mproc = RE_PROC.search(line)
|
||||
mpid = RE_PID.search(line)
|
||||
if mproc:
|
||||
current = mproc.group(1)
|
||||
proc_scores[current]["pid"] = mpid.group(1) if mpid else proc_scores[current]["pid"]
|
||||
if current is None:
|
||||
continue
|
||||
low = line.lower()
|
||||
if RWX.lower() in low:
|
||||
proc_scores[current]["score"] += 3
|
||||
proc_scores[current]["reasons"].add("rwx_region")
|
||||
if "4d 5a" in low or "mz" in low and "this program" in low:
|
||||
proc_scores[current]["score"] += 3
|
||||
proc_scores[current]["reasons"].add("pe_in_private_memory")
|
||||
if "this program cannot be run" in low:
|
||||
proc_scores[current]["score"] += 2
|
||||
proc_scores[current]["reasons"].add("dos_stub")
|
||||
if any(h in low for h in SHELLCODE_HINTS):
|
||||
proc_scores[current]["score"] += 1
|
||||
proc_scores[current]["reasons"].add("shellcode_prologue")
|
||||
ranked = sorted(
|
||||
({"process": k, "pid": v["pid"], "score": v["score"],
|
||||
"reasons": sorted(v["reasons"])} for k, v in proc_scores.items() if v["score"] > 0),
|
||||
key=lambda d: d["score"], reverse=True,
|
||||
)
|
||||
return {"candidates": ranked, "total_flagged": len(ranked)}
|
||||
|
||||
|
||||
def main(argv: list[str] | None = None) -> int:
|
||||
parser = argparse.ArgumentParser(description="Score malfind injection candidates")
|
||||
sub = parser.add_subparsers(dest="cmd", required=True)
|
||||
p = sub.add_parser("score")
|
||||
p.add_argument("malfind_txt")
|
||||
args = parser.parse_args(argv)
|
||||
print(json.dumps(score(args.malfind_txt), indent=2))
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user