mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-07-20 14:30:59 +03:00
Initial commit - 611 cybersecurity skills across all subdomains
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
---
|
||||
name: detecting-process-hollowing-technique
|
||||
description: Detect process hollowing (T1055.012) by analyzing memory-mapped sections, hollowed process indicators, and parent-child process anomalies in EDR telemetry.
|
||||
domain: cybersecurity
|
||||
subdomain: threat-hunting
|
||||
tags: [threat-hunting, mitre-attack, process-hollowing, process-injection, edr, t1055, proactive-detection]
|
||||
version: "1.0"
|
||||
author: mahipal
|
||||
license: MIT
|
||||
---
|
||||
|
||||
# Detecting Process Hollowing Technique
|
||||
|
||||
## When to Use
|
||||
|
||||
- When investigating suspected fileless malware or in-memory threats
|
||||
- After EDR alerts on process injection or suspicious memory operations
|
||||
- When hunting for defense evasion techniques in a compromised environment
|
||||
- When threat intel reports indicate process hollowing in active campaigns
|
||||
- During purple team exercises validating T1055.012 detection coverage
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- EDR with memory protection monitoring (CrowdStrike, MDE, SentinelOne)
|
||||
- Sysmon with Event IDs 1 (Process Create), 8 (CreateRemoteThread), 25 (ProcessTampering)
|
||||
- Windows ETW providers for process hollowing (Microsoft-Windows-Kernel-Process)
|
||||
- Memory forensics capabilities (Volatility, WinDbg)
|
||||
- Process integrity monitoring tools
|
||||
|
||||
## Workflow
|
||||
|
||||
1. **Understand Hollowing Mechanics**: Process hollowing involves creating a legitimate process in suspended state, unmapping its memory, writing malicious code, then resuming execution.
|
||||
2. **Monitor Suspended Process Creation**: Hunt for processes created with CREATE_SUSPENDED flag followed by memory writes and thread resumption.
|
||||
3. **Detect Memory Section Anomalies**: Identify processes where the in-memory image differs from the on-disk binary (image mismatch).
|
||||
4. **Analyze Parent-Child Process Trees**: Flag processes whose behavior does not match their binary name (e.g., svchost.exe making unusual network connections).
|
||||
5. **Check Process Integrity**: Compare process memory sections against the legitimate binary on disk.
|
||||
6. **Correlate with Network Activity**: Hollowed processes often establish C2 connections - correlate suspicious process behavior with network logs.
|
||||
7. **Document and Contain**: Report findings, isolate affected endpoints, and update detection rules.
|
||||
|
||||
## Key Concepts
|
||||
|
||||
| Concept | Description |
|
||||
|---------|-------------|
|
||||
| T1055.012 | Process Injection: Process Hollowing |
|
||||
| T1055 | Process Injection (parent technique) |
|
||||
| T1055.001 | DLL Injection |
|
||||
| T1055.003 | Thread Execution Hijacking |
|
||||
| T1055.004 | Asynchronous Procedure Call |
|
||||
| CREATE_SUSPENDED | Windows flag to create a process in suspended state |
|
||||
| NtUnmapViewOfSection | API to unmap process memory sections |
|
||||
| WriteProcessMemory | API to write into another process's memory |
|
||||
| ResumeThread | API to resume a suspended thread |
|
||||
| Image Mismatch | Process memory content differs from on-disk binary |
|
||||
| Process Doppelganging | Related technique using NTFS transactions (T1055.013) |
|
||||
|
||||
## Tools & Systems
|
||||
|
||||
| Tool | Purpose |
|
||||
|------|---------|
|
||||
| CrowdStrike Falcon | Memory protection and hollowing detection |
|
||||
| Microsoft Defender for Endpoint | ProcessTampering alerts |
|
||||
| Sysmon v13+ | Event ID 25 ProcessTampering detection |
|
||||
| Volatility | Memory forensics - malfind plugin |
|
||||
| pe-sieve | Process memory scanner for hollowed processes |
|
||||
| Hollows Hunter | Automated hollowed process detection |
|
||||
| Process Hacker | Live process memory inspection |
|
||||
| API Monitor | Monitor NtUnmapViewOfSection calls |
|
||||
|
||||
## Common Scenarios
|
||||
|
||||
1. **Svchost.exe Hollowing**: Malware creates svchost.exe suspended, hollows it, injects backdoor code - process appears legitimate but behaves maliciously.
|
||||
2. **Explorer.exe Hollowing**: Attacker hollows explorer.exe to inherit its network permissions and trusted process context.
|
||||
3. **Rundll32 Hollowing**: Malicious loader creates rundll32.exe, replaces its memory with implant code for C2 beaconing.
|
||||
4. **Multi-Stage Hollowing**: Loader uses process hollowing as first stage, then performs additional injection into services.
|
||||
|
||||
## Output Format
|
||||
|
||||
```
|
||||
Hunt ID: TH-HOLLOW-[DATE]-[SEQ]
|
||||
Technique: T1055.012
|
||||
Hollowed Process: [Process name and PID]
|
||||
Original Binary: [Expected on-disk path]
|
||||
Parent Process: [Parent name and PID]
|
||||
Memory Mismatch: [Yes/No]
|
||||
Suspicious APIs: [NtUnmapViewOfSection, WriteProcessMemory, etc.]
|
||||
Network Activity: [C2 connections if any]
|
||||
Host: [Hostname]
|
||||
User: [Account context]
|
||||
Risk Level: [Critical/High/Medium/Low]
|
||||
```
|
||||
@@ -0,0 +1,29 @@
|
||||
# Process Hollowing Hunt Template
|
||||
|
||||
## Hunt Metadata
|
||||
| Field | Value |
|
||||
|-------|-------|
|
||||
| Hunt ID | TH-HOLLOW-YYYY-MM-DD-NNN |
|
||||
| Analyst | |
|
||||
| Date | |
|
||||
| Status | [ ] In Progress / [ ] Complete |
|
||||
|
||||
## Hypothesis
|
||||
> [e.g., "Adversaries have used process hollowing to inject malicious code into svchost.exe instances to evade detection."]
|
||||
|
||||
## Findings
|
||||
|
||||
| # | Host | Process | Parent | Expected Parent | Network Activity | Risk | Verdict |
|
||||
|---|------|---------|--------|----------------|-----------------|------|---------|
|
||||
| 1 | | | | | | | |
|
||||
|
||||
## Memory Analysis Results
|
||||
| Process (PID) | Image Mismatch | Injected Code | VAD Anomaly | Verdict |
|
||||
|--------------|----------------|---------------|-------------|---------|
|
||||
| | | | | |
|
||||
|
||||
## Recommendations
|
||||
1. **Memory Dump**: [Collect memory from affected hosts]
|
||||
2. **Containment**: [Isolate compromised endpoints]
|
||||
3. **Detection**: [Deploy Sysmon v13+ with Event ID 25]
|
||||
4. **Prevention**: [Enable Attack Surface Reduction rules]
|
||||
@@ -0,0 +1,79 @@
|
||||
# Standards and References - Process Hollowing Detection
|
||||
|
||||
## MITRE ATT&CK Mappings
|
||||
|
||||
### T1055.012 - Process Injection: Process Hollowing
|
||||
- **Tactic**: Defense Evasion (TA0005), Privilege Escalation (TA0004)
|
||||
- **Platforms**: Windows
|
||||
- **Data Sources**: Process modification, OS API execution, Process access
|
||||
|
||||
### Related Process Injection Sub-Techniques
|
||||
| Sub-Technique | Name |
|
||||
|---------------|------|
|
||||
| T1055.001 | Dynamic-link Library Injection |
|
||||
| T1055.002 | Portable Executable Injection |
|
||||
| T1055.003 | Thread Execution Hijacking |
|
||||
| T1055.004 | Asynchronous Procedure Call |
|
||||
| T1055.005 | Thread Local Storage |
|
||||
| T1055.008 | Ptrace System Calls |
|
||||
| T1055.009 | Proc Memory |
|
||||
| T1055.011 | Extra Window Memory Injection |
|
||||
| T1055.012 | Process Hollowing |
|
||||
| T1055.013 | Process Doppelganging |
|
||||
| T1055.014 | VDSO Hijacking |
|
||||
| T1055.015 | ListPlanting |
|
||||
|
||||
## Process Hollowing API Call Sequence
|
||||
|
||||
```
|
||||
1. CreateProcess(CREATE_SUSPENDED) -> Create target in suspended state
|
||||
2. NtQueryInformationProcess -> Get PEB address
|
||||
3. ReadProcessMemory(PEB) -> Read image base from PEB
|
||||
4. NtUnmapViewOfSection(ImageBase) -> Unmap original image
|
||||
5. VirtualAllocEx(ImageBase, size) -> Allocate memory at same base
|
||||
6. WriteProcessMemory(PE headers) -> Write malicious PE headers
|
||||
7. WriteProcessMemory(PE sections) -> Write malicious code sections
|
||||
8. SetThreadContext(EntryPoint) -> Set new entry point
|
||||
9. ResumeThread -> Resume execution with malicious code
|
||||
```
|
||||
|
||||
## Detection Data Sources
|
||||
|
||||
| Source | Event/Indicator | Description |
|
||||
|--------|----------------|-------------|
|
||||
| Sysmon Event 1 | Process Create | Process created with suspicious parent |
|
||||
| Sysmon Event 8 | CreateRemoteThread | Remote thread in target process |
|
||||
| Sysmon Event 25 | ProcessTampering | Image file replaced (Sysmon v13+) |
|
||||
| ETW | Microsoft-Windows-Kernel-Process | Kernel-level process events |
|
||||
| MDE | ProcessTampering | AlertType for hollowing detection |
|
||||
| Memory | Malfind | Volatility plugin for injected code |
|
||||
| Memory | VAD analysis | Virtual Address Descriptor anomalies |
|
||||
|
||||
## Volatility Forensic Commands
|
||||
|
||||
```bash
|
||||
# Detect injected/hollowed processes
|
||||
volatility -f memory.dmp --profile=Win10x64 malfind
|
||||
|
||||
# Compare process memory to disk image
|
||||
volatility -f memory.dmp --profile=Win10x64 procdump -p <PID> -D ./dump/
|
||||
|
||||
# Analyze process memory sections
|
||||
volatility -f memory.dmp --profile=Win10x64 vadinfo -p <PID>
|
||||
|
||||
# Check process image path vs loaded modules
|
||||
volatility -f memory.dmp --profile=Win10x64 dlllist -p <PID>
|
||||
```
|
||||
|
||||
## Known Malware Using Process Hollowing
|
||||
|
||||
| Malware | Target Process | Notes |
|
||||
|---------|---------------|-------|
|
||||
| Emotet | Multiple | Uses hollowing for persistence |
|
||||
| TrickBot | svchost.exe | Hollows svchost for C2 |
|
||||
| Dridex | explorer.exe | Financial trojan |
|
||||
| FormBook | Various | Infostealer using hollowing |
|
||||
| AgentTesla | RegAsm.exe, MSBuild.exe | Targets .NET processes |
|
||||
| Remcos | Common utilities | RAT using hollowing |
|
||||
| NanoCore | Various | RAT with hollowing capability |
|
||||
| AsyncRAT | Various .NET processes | Open-source RAT |
|
||||
@@ -0,0 +1,121 @@
|
||||
# Detailed Hunting Workflow - Process Hollowing Detection
|
||||
|
||||
## Phase 1: Sysmon-Based Detection
|
||||
|
||||
### Step 1.1 - Process Tampering Events (Sysmon v13+)
|
||||
```spl
|
||||
index=sysmon EventCode=25
|
||||
| table _time Computer User Image Type
|
||||
| sort -_time
|
||||
```
|
||||
|
||||
### Step 1.2 - Suspicious Process Creation Patterns
|
||||
```spl
|
||||
index=sysmon EventCode=1
|
||||
| where match(Image, "(?i)(svchost|explorer|rundll32|dllhost|conhost|taskhost)\.exe$")
|
||||
| where NOT match(ParentImage, "(?i)(services\.exe|explorer\.exe|svchost\.exe|userinit\.exe|winlogon\.exe)")
|
||||
| table _time Computer User Image ParentImage CommandLine
|
||||
```
|
||||
|
||||
### Step 1.3 - KQL for MDE ProcessTampering
|
||||
```kql
|
||||
DeviceEvents
|
||||
| where ActionType == "ProcessTampering"
|
||||
| project Timestamp, DeviceName, FileName, ProcessCommandLine,
|
||||
InitiatingProcessFileName, InitiatingProcessCommandLine, AdditionalFields
|
||||
| order by Timestamp desc
|
||||
```
|
||||
|
||||
## Phase 2: Parent-Child Process Validation
|
||||
|
||||
### Step 2.1 - Invalid Parent-Child Relationships
|
||||
Known legitimate parent-child pairs:
|
||||
- services.exe -> svchost.exe
|
||||
- explorer.exe -> user applications
|
||||
- winlogon.exe -> userinit.exe
|
||||
- svchost.exe -> specific service children
|
||||
|
||||
```spl
|
||||
index=sysmon EventCode=1
|
||||
| eval expected_parent=case(
|
||||
match(Image,"(?i)svchost\.exe$"), "services.exe",
|
||||
match(Image,"(?i)taskhost\.exe$"), "svchost.exe",
|
||||
match(Image,"(?i)userinit\.exe$"), "winlogon.exe",
|
||||
match(Image,"(?i)smss\.exe$"), "System",
|
||||
1=1, "any"
|
||||
)
|
||||
| eval parent_name=mvindex(split(ParentImage,"\\"),-1)
|
||||
| where expected_parent!="any" AND NOT match(parent_name, expected_parent)
|
||||
| table _time Computer Image ParentImage expected_parent parent_name CommandLine
|
||||
```
|
||||
|
||||
## Phase 3: Memory Analysis
|
||||
|
||||
### Step 3.1 - pe-sieve Scanning
|
||||
```powershell
|
||||
# Scan all processes for hollowing
|
||||
Get-Process | ForEach-Object {
|
||||
$pid = $_.Id
|
||||
& pe-sieve64.exe /pid $pid /shellc /dmode 1 /json
|
||||
}
|
||||
```
|
||||
|
||||
### Step 3.2 - Hollows Hunter Full Scan
|
||||
```powershell
|
||||
# Run Hollows Hunter for automated detection
|
||||
hollows_hunter64.exe /loop /json /dir C:\hunt_output
|
||||
```
|
||||
|
||||
### Step 3.3 - Volatility Malfind
|
||||
```bash
|
||||
# Detect injected/modified process memory
|
||||
python vol.py -f memory.raw windows.malfind
|
||||
|
||||
# Dump suspicious processes
|
||||
python vol.py -f memory.raw windows.pslist --dump
|
||||
```
|
||||
|
||||
## Phase 4: Behavioral Analysis
|
||||
|
||||
### Step 4.1 - Process Behavior Mismatches
|
||||
Look for processes whose network/file behavior contradicts their identity:
|
||||
```spl
|
||||
index=sysmon EventCode=3
|
||||
| where match(Image, "(?i)(svchost|dllhost|taskhost|conhost)\.exe$")
|
||||
| where NOT match(DestinationIp, "^(10\.|172\.(1[6-9]|2[0-9]|3[01])\.|192\.168\.)")
|
||||
| where DestinationPort NOT IN (53, 80, 443, 123)
|
||||
| stats count by Image DestinationIp DestinationPort Computer
|
||||
```
|
||||
|
||||
### Step 4.2 - Hollowed Process C2 Indicators
|
||||
```spl
|
||||
index=sysmon EventCode=3
|
||||
| where match(Image, "(?i)(svchost|explorer|rundll32)\.exe$")
|
||||
| bin _time span=1s
|
||||
| streamstats current=f last(_time) as prev by Image Computer DestinationIp
|
||||
| eval interval=_time-prev
|
||||
| stats count avg(interval) as avg_interval stdev(interval) as sd by Image Computer DestinationIp
|
||||
| eval cv=sd/avg_interval
|
||||
| where cv < 0.3 AND count > 20
|
||||
```
|
||||
|
||||
## Phase 5: API Call Monitoring
|
||||
|
||||
### Step 5.1 - Critical API Sequences
|
||||
Monitor for this specific API call chain:
|
||||
1. `CreateProcessW` / `CreateProcessA` with `CREATE_SUSPENDED` (0x00000004)
|
||||
2. `NtUnmapViewOfSection` / `ZwUnmapViewOfSection`
|
||||
3. `VirtualAllocEx` with `PAGE_EXECUTE_READWRITE`
|
||||
4. `WriteProcessMemory`
|
||||
5. `SetThreadContext` / `NtSetContextThread`
|
||||
6. `ResumeThread` / `NtResumeThread`
|
||||
|
||||
### Step 5.2 - ETW Process Hollowing Detection
|
||||
```powershell
|
||||
# Monitor for suspicious API patterns via ETW
|
||||
# Requires elevated privileges
|
||||
$session = New-EtwTraceSession -Name "ProcessHollowHunt"
|
||||
Add-EtwTraceProvider -SessionName "ProcessHollowHunt" `
|
||||
-Guid "{22FB2CD6-0E7B-422B-A0C7-2FAD1FD0E716}" `
|
||||
-Level 5
|
||||
```
|
||||
@@ -0,0 +1,320 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Process Hollowing Detection Script
|
||||
Analyzes process creation, memory events, and parent-child relationships
|
||||
to detect process hollowing (T1055.012) indicators.
|
||||
"""
|
||||
|
||||
import json
|
||||
import csv
|
||||
import argparse
|
||||
import datetime
|
||||
import re
|
||||
from collections import defaultdict
|
||||
from pathlib import Path
|
||||
|
||||
# Legitimate parent-child process relationships on Windows
|
||||
VALID_PARENT_CHILD = {
|
||||
"smss.exe": {"parents": ["system", "smss.exe"], "user": "NT AUTHORITY\\SYSTEM"},
|
||||
"csrss.exe": {"parents": ["smss.exe"], "user": "NT AUTHORITY\\SYSTEM"},
|
||||
"wininit.exe": {"parents": ["smss.exe"], "user": "NT AUTHORITY\\SYSTEM"},
|
||||
"winlogon.exe": {"parents": ["smss.exe"], "user": "NT AUTHORITY\\SYSTEM"},
|
||||
"services.exe": {"parents": ["wininit.exe"], "user": "NT AUTHORITY\\SYSTEM"},
|
||||
"lsass.exe": {"parents": ["wininit.exe"], "user": "NT AUTHORITY\\SYSTEM"},
|
||||
"svchost.exe": {"parents": ["services.exe", "MsMpEng.exe"], "user": "NT AUTHORITY\\*"},
|
||||
"taskhost.exe": {"parents": ["svchost.exe"], "user": "*"},
|
||||
"taskhostw.exe": {"parents": ["svchost.exe"], "user": "*"},
|
||||
"userinit.exe": {"parents": ["winlogon.exe"], "user": "*"},
|
||||
"explorer.exe": {"parents": ["userinit.exe", "explorer.exe"], "user": "*"},
|
||||
"dllhost.exe": {"parents": ["svchost.exe", "services.exe"], "user": "*"},
|
||||
"conhost.exe": {"parents": ["csrss.exe"], "user": "*"},
|
||||
"RuntimeBroker.exe": {"parents": ["svchost.exe"], "user": "*"},
|
||||
"SearchIndexer.exe": {"parents": ["services.exe"], "user": "NT AUTHORITY\\SYSTEM"},
|
||||
"spoolsv.exe": {"parents": ["services.exe"], "user": "NT AUTHORITY\\SYSTEM"},
|
||||
}
|
||||
|
||||
# Common hollowing target processes
|
||||
HOLLOWING_TARGETS = {
|
||||
"svchost.exe", "explorer.exe", "rundll32.exe", "dllhost.exe",
|
||||
"conhost.exe", "taskhost.exe", "taskhostw.exe", "RuntimeBroker.exe",
|
||||
"RegAsm.exe", "MSBuild.exe", "RegSvcs.exe", "vbc.exe",
|
||||
"AppLaunch.exe", "InstallUtil.exe", "aspnet_compiler.exe",
|
||||
}
|
||||
|
||||
# Process behavior indicators that suggest hollowing
|
||||
ANOMALOUS_BEHAVIORS = {
|
||||
"svchost.exe": {
|
||||
"no_cmdline_flag": True, # svchost should have -k flag
|
||||
"required_arg": "-k",
|
||||
"no_external_network": True, # unusual ports
|
||||
},
|
||||
"explorer.exe": {
|
||||
"no_cmdline_flag": False,
|
||||
"no_external_network": False,
|
||||
"single_instance": True,
|
||||
},
|
||||
"dllhost.exe": {
|
||||
"no_cmdline_flag": True,
|
||||
"required_arg": "/Processid:",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def parse_logs(input_path: str) -> list[dict]:
|
||||
"""Parse log files."""
|
||||
path = Path(input_path)
|
||||
if path.suffix == ".json":
|
||||
with open(path, "r", encoding="utf-8") as f:
|
||||
data = json.load(f)
|
||||
return data if isinstance(data, list) else data.get("events", [])
|
||||
elif path.suffix == ".csv":
|
||||
with open(path, "r", encoding="utf-8-sig") as f:
|
||||
return [dict(row) for row in csv.DictReader(f)]
|
||||
return []
|
||||
|
||||
|
||||
def normalize_event(event: dict) -> dict:
|
||||
"""Normalize process event fields."""
|
||||
field_map = {
|
||||
"event_id": ["EventCode", "EventID", "event_id"],
|
||||
"image": ["Image", "FileName", "image", "process.executable"],
|
||||
"command_line": ["CommandLine", "ProcessCommandLine", "command_line"],
|
||||
"parent_image": ["ParentImage", "InitiatingProcessFileName", "parent_image"],
|
||||
"parent_cmd": ["ParentCommandLine", "InitiatingProcessCommandLine", "parent_command_line"],
|
||||
"user": ["User", "AccountName", "user.name"],
|
||||
"hostname": ["Computer", "DeviceName", "host.name"],
|
||||
"timestamp": ["UtcTime", "Timestamp", "@timestamp"],
|
||||
"pid": ["ProcessId", "ProcessId", "process.pid"],
|
||||
"parent_pid": ["ParentProcessId", "ppid", "process.parent.pid"],
|
||||
"integrity": ["IntegrityLevel", "integrity_level"],
|
||||
"hashes": ["Hashes", "SHA256", "hashes"],
|
||||
"action_type": ["ActionType", "event_type"],
|
||||
"dest_ip": ["DestinationIp", "RemoteIP"],
|
||||
"dest_port": ["DestinationPort", "RemotePort"],
|
||||
}
|
||||
normalized = {}
|
||||
for target, sources in field_map.items():
|
||||
for src in sources:
|
||||
if src in event and event[src]:
|
||||
normalized[target] = str(event[src])
|
||||
break
|
||||
if target not in normalized:
|
||||
normalized[target] = ""
|
||||
return normalized
|
||||
|
||||
|
||||
def get_process_name(path: str) -> str:
|
||||
"""Extract process name from full path."""
|
||||
if not path:
|
||||
return ""
|
||||
return path.split("\\")[-1].split("/")[-1].lower()
|
||||
|
||||
|
||||
def check_parent_child(event: dict) -> dict | None:
|
||||
"""Check for invalid parent-child process relationships."""
|
||||
image = get_process_name(event.get("image", ""))
|
||||
parent = get_process_name(event.get("parent_image", ""))
|
||||
|
||||
if image not in VALID_PARENT_CHILD:
|
||||
return None
|
||||
|
||||
expected = VALID_PARENT_CHILD[image]
|
||||
valid_parents = [p.lower() for p in expected["parents"]]
|
||||
|
||||
if parent and parent not in valid_parents:
|
||||
return {
|
||||
"detection_type": "INVALID_PARENT_CHILD",
|
||||
"technique": "T1055.012",
|
||||
"process": image,
|
||||
"parent": parent,
|
||||
"expected_parents": expected["parents"],
|
||||
"full_image_path": event.get("image", ""),
|
||||
"full_parent_path": event.get("parent_image", ""),
|
||||
"command_line": event.get("command_line", ""),
|
||||
"hostname": event.get("hostname", "unknown"),
|
||||
"user": event.get("user", "unknown"),
|
||||
"timestamp": event.get("timestamp", "unknown"),
|
||||
"risk_score": 70,
|
||||
"risk_level": "HIGH",
|
||||
"indicators": [
|
||||
f"Invalid parent: {parent} (expected: {', '.join(expected['parents'])})"
|
||||
],
|
||||
}
|
||||
return None
|
||||
|
||||
|
||||
def check_process_tampering(event: dict) -> dict | None:
|
||||
"""Check for Sysmon Event ID 25 (ProcessTampering)."""
|
||||
if event.get("event_id") != "25":
|
||||
return None
|
||||
|
||||
return {
|
||||
"detection_type": "PROCESS_TAMPERING",
|
||||
"technique": "T1055.012",
|
||||
"process": get_process_name(event.get("image", "")),
|
||||
"full_image_path": event.get("image", ""),
|
||||
"hostname": event.get("hostname", "unknown"),
|
||||
"user": event.get("user", "unknown"),
|
||||
"timestamp": event.get("timestamp", "unknown"),
|
||||
"risk_score": 90,
|
||||
"risk_level": "CRITICAL",
|
||||
"indicators": ["Sysmon ProcessTampering event detected - image replaced in memory"],
|
||||
}
|
||||
|
||||
|
||||
def check_behavioral_anomaly(event: dict) -> dict | None:
|
||||
"""Check for behavioral mismatches suggesting hollowing."""
|
||||
if event.get("event_id") != "1":
|
||||
return None
|
||||
|
||||
image = get_process_name(event.get("image", ""))
|
||||
cmd = event.get("command_line", "")
|
||||
|
||||
if image not in ANOMALOUS_BEHAVIORS:
|
||||
return None
|
||||
|
||||
behavior = ANOMALOUS_BEHAVIORS[image]
|
||||
indicators = []
|
||||
|
||||
if behavior.get("required_arg") and behavior["required_arg"] not in cmd:
|
||||
indicators.append(f"Missing required argument '{behavior['required_arg']}'")
|
||||
|
||||
if image in HOLLOWING_TARGETS:
|
||||
# Check if process path is from unexpected location
|
||||
expected_paths = ["\\windows\\system32\\", "\\windows\\syswow64\\"]
|
||||
image_path = event.get("image", "").lower()
|
||||
if not any(ep in image_path for ep in expected_paths):
|
||||
indicators.append(f"Process running from unexpected path: {image_path}")
|
||||
|
||||
if not indicators:
|
||||
return None
|
||||
|
||||
return {
|
||||
"detection_type": "BEHAVIORAL_ANOMALY",
|
||||
"technique": "T1055.012",
|
||||
"process": image,
|
||||
"full_image_path": event.get("image", ""),
|
||||
"command_line": cmd,
|
||||
"parent_process": get_process_name(event.get("parent_image", "")),
|
||||
"hostname": event.get("hostname", "unknown"),
|
||||
"user": event.get("user", "unknown"),
|
||||
"timestamp": event.get("timestamp", "unknown"),
|
||||
"risk_score": 50,
|
||||
"risk_level": "MEDIUM",
|
||||
"indicators": indicators,
|
||||
}
|
||||
|
||||
|
||||
def check_hollowing_target_network(event: dict) -> dict | None:
|
||||
"""Detect hollowing targets making unusual network connections."""
|
||||
if event.get("event_id") != "3":
|
||||
return None
|
||||
|
||||
image = get_process_name(event.get("image", ""))
|
||||
if image not in HOLLOWING_TARGETS:
|
||||
return None
|
||||
|
||||
dest_ip = event.get("dest_ip", "")
|
||||
dest_port = event.get("dest_port", "")
|
||||
|
||||
# Check for external connections from commonly hollowed processes
|
||||
if dest_ip and not re.match(r"^(10\.|172\.(1[6-9]|2[0-9]|3[01])\.|192\.168\.|127\.)", dest_ip):
|
||||
suspicious_ports = {"4444", "5555", "6666", "8888", "9090", "1234", "31337", "50050"}
|
||||
risk = 40
|
||||
indicators = [f"Hollowing target {image} connecting externally to {dest_ip}:{dest_port}"]
|
||||
|
||||
if dest_port in suspicious_ports:
|
||||
risk += 20
|
||||
indicators.append(f"Suspicious port: {dest_port}")
|
||||
|
||||
return {
|
||||
"detection_type": "HOLLOWED_PROCESS_NETWORK",
|
||||
"technique": "T1055.012",
|
||||
"process": image,
|
||||
"dest_ip": dest_ip,
|
||||
"dest_port": dest_port,
|
||||
"hostname": event.get("hostname", "unknown"),
|
||||
"timestamp": event.get("timestamp", "unknown"),
|
||||
"risk_score": risk,
|
||||
"risk_level": "HIGH" if risk >= 50 else "MEDIUM",
|
||||
"indicators": indicators,
|
||||
}
|
||||
return None
|
||||
|
||||
|
||||
def run_hunt(input_path: str, output_dir: str) -> None:
|
||||
"""Execute process hollowing hunt."""
|
||||
print(f"[*] Process Hollowing Hunt - {datetime.datetime.now().isoformat()}")
|
||||
|
||||
events = parse_logs(input_path)
|
||||
print(f"[*] Loaded {len(events)} events")
|
||||
|
||||
findings = []
|
||||
stats = defaultdict(int)
|
||||
|
||||
detectors = [
|
||||
check_process_tampering,
|
||||
check_parent_child,
|
||||
check_behavioral_anomaly,
|
||||
check_hollowing_target_network,
|
||||
]
|
||||
|
||||
for raw_event in events:
|
||||
event = normalize_event(raw_event)
|
||||
for detector in detectors:
|
||||
result = detector(event)
|
||||
if result:
|
||||
findings.append(result)
|
||||
stats[result["detection_type"]] += 1
|
||||
|
||||
output_path = Path(output_dir)
|
||||
output_path.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
with open(output_path / "hollowing_findings.json", "w", encoding="utf-8") as f:
|
||||
json.dump({
|
||||
"hunt_id": f"TH-HOLLOW-{datetime.date.today().isoformat()}",
|
||||
"total_events": len(events),
|
||||
"total_findings": len(findings),
|
||||
"statistics": dict(stats),
|
||||
"findings": sorted(findings, key=lambda x: x["risk_score"], reverse=True),
|
||||
}, f, indent=2)
|
||||
|
||||
with open(output_path / "hunt_report.md", "w", encoding="utf-8") as f:
|
||||
f.write(f"# Process Hollowing Hunt Report\n\n")
|
||||
f.write(f"**Date**: {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
|
||||
f.write(f"**Findings**: {len(findings)}\n\n")
|
||||
for finding in sorted(findings, key=lambda x: x["risk_score"], reverse=True)[:20]:
|
||||
f.write(f"### [{finding['risk_level']}] {finding['detection_type']}\n")
|
||||
f.write(f"- **Process**: {finding.get('process', '')}\n")
|
||||
f.write(f"- **Host**: {finding['hostname']}\n")
|
||||
f.write(f"- **Indicators**: {', '.join(finding['indicators'])}\n\n")
|
||||
|
||||
print(f"[+] {len(findings)} findings written to {output_dir}")
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Process Hollowing Detection")
|
||||
subparsers = parser.add_subparsers(dest="command")
|
||||
|
||||
hunt_p = subparsers.add_parser("hunt")
|
||||
hunt_p.add_argument("--input", "-i", required=True)
|
||||
hunt_p.add_argument("--output", "-o", default="./hollowing_output")
|
||||
|
||||
subparsers.add_parser("queries")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.command == "hunt":
|
||||
run_hunt(args.input, args.output)
|
||||
elif args.command == "queries":
|
||||
print("=== Sysmon Queries ===")
|
||||
print("--- Process Tampering ---")
|
||||
print('index=sysmon EventCode=25\n| table _time Computer User Image Type')
|
||||
print("\n--- Invalid Parent-Child ---")
|
||||
print('index=sysmon EventCode=1 Image="*\\\\svchost.exe"\n| where NOT match(ParentImage, "(?i)services\\.exe")\n| table _time Computer Image ParentImage CommandLine')
|
||||
else:
|
||||
parser.print_help()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user