Install this skill
npx skills add mukul975/Anthropic-Cybersecurity-SkillsFramework mappings
MITRE ATT&CK
T1046 on the official MITRE ATT&CK siteT1055 on the official MITRE ATT&CK siteT1055.001 on the official MITRE ATT&CK siteT1055.002 on the official MITRE ATT&CK siteT1055.003 on the official MITRE ATT&CK siteT1055.004 on the official MITRE ATT&CK siteT1055.005 on the official MITRE ATT&CK siteT1055.008 on the official MITRE ATT&CK siteT1055.009 on the official MITRE ATT&CK siteT1055.011 on the official MITRE ATT&CK siteT1055.012 on the official MITRE ATT&CK siteT1055.013 on the official MITRE ATT&CK siteT1055.014 on the official MITRE ATT&CK siteT1055.015 on the official MITRE ATT&CK siteT1057 on the official MITRE ATT&CK siteT1082 on the official MITRE ATT&CK siteT1083 on the official MITRE ATT&CK site
NIST CSF 2.0
MITRE D3FEND
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
- Understand Hollowing Mechanics: Process hollowing involves creating a legitimate process in suspended state, unmapping its memory, writing malicious code, then resuming execution.
- Monitor Suspended Process Creation: Hunt for processes created with CREATE_SUSPENDED flag followed by memory writes and thread resumption.
- Detect Memory Section Anomalies: Identify processes where the in-memory image differs from the on-disk binary (image mismatch).
- Analyze Parent-Child Process Trees: Flag processes whose behavior does not match their binary name (e.g., svchost.exe making unusual network connections).
- Check Process Integrity: Compare process memory sections against the legitimate binary on disk.
- Correlate with Network Activity: Hollowed processes often establish C2 connections - correlate suspicious process behavior with network logs.
- 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
- Svchost.exe Hollowing: Malware creates svchost.exe suspended, hollows it, injects backdoor code - process appears legitimate but behaves maliciously.
- Explorer.exe Hollowing: Attacker hollows explorer.exe to inherit its network permissions and trusted process context.
- Rundll32 Hollowing: Malicious loader creates rundll32.exe, replaces its memory with implant code for C2 beaconing.
- 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]Source materials
References and resources
Everything below is rendered for inspection. Script files are read-only and never run.
References 3
api-reference.md1.1 KB
API Reference: Detecting Process Hollowing Technique
Process Hollowing API Sequence
| Step | API Call | Purpose |
|---|---|---|
| 1 | CreateProcess(SUSPENDED) | Create target suspended |
| 2 | NtUnmapViewOfSection | Unmap legitimate code |
| 3 | VirtualAllocEx | Allocate for payload |
| 4 | WriteProcessMemory | Write malicious code |
| 5 | SetThreadContext | Redirect execution |
| 6 | ResumeThread | Execute payload |
Commonly Hollowed Processes
| Process | Reason |
|---|---|
| svchost.exe | Trusted, always running |
| explorer.exe | UI process |
| notepad.exe | Simple, rarely monitored |
| dllhost.exe | COM surrogate |
Sysmon Detection Events
| Event ID | Detection |
|---|---|
| 1 | Suspicious parent-child |
| 8 | CreateRemoteThread into hollowed target |
| 10 | Process Access with PROCESS_ALL_ACCESS |
Splunk SPL
index=sysmon EventCode=10
| where TargetImage IN ("*\svchost.exe","*\explorer.exe")
| where GrantedAccess IN ("0x1FFFFF","0x1F3FFF")
| table _time SourceImage TargetImage GrantedAccess ComputerCLI Usage
python agent.py --sysmon-log Sysmon.evtxstandards.md3.1 KB
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 codeDetection 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
# 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 |
workflows.md3.7 KB
Detailed Hunting Workflow - Process Hollowing Detection
Phase 1: Sysmon-Based Detection
Step 1.1 - Process Tampering Events (Sysmon v13+)
index=sysmon EventCode=25
| table _time Computer User Image Type
| sort -_timeStep 1.2 - Suspicious Process Creation Patterns
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 CommandLineStep 1.3 - KQL for MDE ProcessTampering
DeviceEvents
| where ActionType == "ProcessTampering"
| project Timestamp, DeviceName, FileName, ProcessCommandLine,
InitiatingProcessFileName, InitiatingProcessCommandLine, AdditionalFields
| order by Timestamp descPhase 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
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 CommandLinePhase 3: Memory Analysis
Step 3.1 - pe-sieve Scanning
# 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
# Run Hollows Hunter for automated detection
hollows_hunter64.exe /loop /json /dir C:\hunt_outputStep 3.3 - Volatility Malfind
# Detect injected/modified process memory
python vol.py -f memory.raw windows.malfind
# Dump suspicious processes
python vol.py -f memory.raw windows.pslist --dumpPhase 4: Behavioral Analysis
Step 4.1 - Process Behavior Mismatches
Look for processes whose network/file behavior contradicts their identity:
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 ComputerStep 4.2 - Hollowed Process C2 Indicators
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 > 20Phase 5: API Call Monitoring
Step 5.1 - Critical API Sequences
Monitor for this specific API call chain:
CreateProcessW/CreateProcessAwithCREATE_SUSPENDED(0x00000004)NtUnmapViewOfSection/ZwUnmapViewOfSectionVirtualAllocExwithPAGE_EXECUTE_READWRITEWriteProcessMemorySetThreadContext/NtSetContextThreadResumeThread/NtResumeThread
Step 5.2 - ETW Process Hollowing Detection
# 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 5Scripts 2
agent.py4.5 KB
Display-only source. This catalog never executes bundled scripts.
#!/usr/bin/env python3
"""Process hollowing (T1055.012) detection agent.
Detects hollowed processes by analyzing Sysmon events for suspended process
creation, memory allocation in remote processes, and thread hijacking.
"""
import argparse
import json
import re
from datetime import datetime
try:
import Evtx.Evtx as evtx
except ImportError:
evtx = None
COMMONLY_HOLLOWED = {
"svchost.exe", "explorer.exe", "notepad.exe", "calc.exe",
"dllhost.exe", "regsvr32.exe", "RuntimeBroker.exe",
}
SUSPICIOUS_PARENT_CHILD = [
("cmd.exe", "svchost.exe"), ("powershell.exe", "svchost.exe"),
("wscript.exe", "svchost.exe"), ("mshta.exe", "svchost.exe"),
("winword.exe", "svchost.exe"), ("excel.exe", "svchost.exe"),
]
def detect_hollowing_sysmon(filepath):
if evtx is None:
return {"error": "python-evtx not installed: pip install python-evtx"}
findings = []
with evtx.Evtx(filepath) as log:
for record in log.records():
xml = record.xml()
eid_match = re.search(r'<EventID[^>]*>(\d+)</EventID>', xml)
if not eid_match:
continue
eid = int(eid_match.group(1))
time_match = re.search(r'SystemTime="([^"]+)"', xml)
ts = time_match.group(1) if time_match else ""
if eid == 1:
image = re.search(r'<Data Name="Image">([^<]+)', xml)
parent = re.search(r'<Data Name="ParentImage">([^<]+)', xml)
cmdline = re.search(r'<Data Name="CommandLine">([^<]+)', xml)
if image and parent:
img = image.group(1).rsplit("\\", 1)[-1].lower()
par = parent.group(1).rsplit("\\", 1)[-1].lower()
for sp, sc in SUSPICIOUS_PARENT_CHILD:
if par == sp.lower() and img == sc.lower():
findings.append({
"event_id": 1, "timestamp": ts,
"type": "suspicious_parent_child",
"parent": parent.group(1), "image": image.group(1),
"cmdline": cmdline.group(1)[:200] if cmdline else "",
"severity": "HIGH", "mitre": "T1055.012",
})
if eid == 8:
source = re.search(r'<Data Name="SourceImage">([^<]+)', xml)
target = re.search(r'<Data Name="TargetImage">([^<]+)', xml)
if target:
tgt = target.group(1).rsplit("\\", 1)[-1].lower()
if tgt in COMMONLY_HOLLOWED:
findings.append({
"event_id": 8, "timestamp": ts,
"type": "remote_thread_hollowed_target",
"source": source.group(1) if source else "",
"target": target.group(1),
"severity": "CRITICAL", "mitre": "T1055.012",
})
if eid == 10:
source = re.search(r'<Data Name="SourceImage">([^<]+)', xml)
target = re.search(r'<Data Name="TargetImage">([^<]+)', xml)
access = re.search(r'<Data Name="GrantedAccess">([^<]+)', xml)
if target and access:
tgt = target.group(1).rsplit("\\", 1)[-1].lower()
mask = access.group(1).lower()
if tgt in COMMONLY_HOLLOWED and mask in ("0x1fffff", "0x1f3fff"):
findings.append({
"event_id": 10, "timestamp": ts,
"type": "full_access_hollowed_target",
"source": source.group(1) if source else "",
"target": target.group(1), "access": mask,
"severity": "CRITICAL", "mitre": "T1055.012",
})
return findings
def main():
parser = argparse.ArgumentParser(description="Process Hollowing Detector")
parser.add_argument("--sysmon-log", required=True, help="Sysmon EVTX file")
args = parser.parse_args()
findings = detect_hollowing_sysmon(args.sysmon_log)
if isinstance(findings, dict) and "error" in findings:
results = findings
else:
results = {
"timestamp": datetime.utcnow().isoformat() + "Z",
"findings": findings, "total_findings": len(findings),
}
print(json.dumps(results, indent=2))
if __name__ == "__main__":
main()
process.py11.9 KB
Display-only source. This catalog never executes bundled scripts.
#!/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()
Assets 1
template.mdtext/markdown · 0.9 KBKeep exploring