threat hunting

Detecting Process Hollowing Technique

Detect process hollowing (T1055.012) by analyzing memory-mapped sections, hollowed process indicators, and parent-child process anomalies in EDR telemetry.

edrmitre-attackproactive-detectionprocess-hollowingprocess-injectiont1055threat-hunting
Install this skill
npx skills add mukul975/Anthropic-Cybersecurity-Skills
Framework mappings

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]
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 Computer

CLI Usage

python agent.py --sysmon-log Sysmon.evtx
standards.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 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

# 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 -_time

Step 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 CommandLine

Step 1.3 - KQL for MDE ProcessTampering

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
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

# 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_output

Step 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 --dump

Phase 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 Computer

Step 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 > 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

# 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

Scripts 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 KB
Keep exploring