threat hunting

Hunting for Persistence Mechanisms in Windows

Systematically hunt for adversary persistence mechanisms across Windows endpoints including registry, services, startup folders, and WMI subscriptions.

mitre-attackpersistenceproactive-detectionregistrysiemthreat-huntingwindows
Install this skill
npx skills add mukul975/Anthropic-Cybersecurity-Skills
Framework mappings

MITRE ATT&CK

T1046 on the official MITRE ATT&CK siteT1053 on the official MITRE ATT&CK siteT1053.002 on the official MITRE ATT&CK siteT1053.003 on the official MITRE ATT&CK siteT1053.005 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 siteT1543 on the official MITRE ATT&CK siteT1543.003 on the official MITRE ATT&CK siteT1543.004 on the official MITRE ATT&CK siteT1546 on the official MITRE ATT&CK siteT1546.001 on the official MITRE ATT&CK siteT1546.002 on the official MITRE ATT&CK siteT1546.003 on the official MITRE ATT&CK siteT1546.004 on the official MITRE ATT&CK siteT1546.007 on the official MITRE ATT&CK siteT1546.008 on the official MITRE ATT&CK siteT1546.010 on the official MITRE ATT&CK siteT1546.011 on the official MITRE ATT&CK siteT1546.012 on the official MITRE ATT&CK siteT1546.013 on the official MITRE ATT&CK siteT1546.015 on the official MITRE ATT&CK siteT1546.016 on the official MITRE ATT&CK siteT1547 on the official MITRE ATT&CK siteT1547.001 on the official MITRE ATT&CK siteT1547.002 on the official MITRE ATT&CK siteT1547.003 on the official MITRE ATT&CK siteT1547.004 on the official MITRE ATT&CK siteT1547.005 on the official MITRE ATT&CK siteT1547.006 on the official MITRE ATT&CK siteT1547.009 on the official MITRE ATT&CK siteT1547.010 on the official MITRE ATT&CK siteT1547.012 on the official MITRE ATT&CK siteT1547.014 on the official MITRE ATT&CK siteT1547.015 on the official MITRE ATT&CK siteT1574 on the official MITRE ATT&CK siteT1574.001 on the official MITRE ATT&CK siteT1574.002 on the official MITRE ATT&CK siteT1574.006 on the official MITRE ATT&CK siteT1574.008 on the official MITRE ATT&CK siteT1574.009 on the official MITRE ATT&CK siteT1574.011 on the official MITRE ATT&CK siteT1574.012 on the official MITRE ATT&CK site

When to Use

  • During periodic proactive threat hunts for dormant backdoors
  • After an incident to identify all persistence mechanisms an attacker planted
  • When investigating unusual services, scheduled tasks, or startup entries
  • When threat intel reports describe new persistence techniques in the wild
  • During security posture assessments to identify unauthorized persistent software

Prerequisites

  • Sysmon deployed with Event IDs 12/13/14 (Registry), 19/20/21 (WMI), 1 (Process Creation)
  • Windows Security Event forwarding for 4697 (Service Install), 4698 (Scheduled Task)
  • EDR with registry and file monitoring capabilities
  • PowerShell script block logging enabled (Event ID 4104)
  • Autoruns or equivalent baseline of legitimate persistent entries

Workflow

  1. Enumerate Known Persistence Locations: Build a comprehensive list of Windows persistence points (Run keys, services, scheduled tasks, WMI, startup folder, DLL search order, COM hijacks, AppInit DLLs, Image File Execution Options).
  2. Collect Endpoint Data: Use EDR, Sysmon, or Velociraptor to collect current persistence artifacts from endpoints across the environment.
  3. Baseline Legitimate Persistence: Compare collected data against known-good baselines (Autoruns snapshots, GPO-deployed entries, SCCM configurations).
  4. Identify Anomalies: Flag new, unsigned, or unknown entries in persistence locations that deviate from the baseline.
  5. Investigate Suspicious Entries: For each anomaly, examine the binary it points to, its digital signature, file hash, and creation timestamp.
  6. Correlate with Process Activity: Link persistence entries to process execution, network activity, and user login events.
  7. Document and Remediate: Record findings, remove malicious persistence, and update detection rules.

Key Concepts

Concept Description
T1547.001 Registry Run Keys / Startup Folder
T1543.003 Windows Service (Create or Modify)
T1053.005 Scheduled Task
T1546.003 WMI Event Subscription
T1546.015 Component Object Model (COM) Hijacking
T1546.012 Image File Execution Options Injection
T1546.010 AppInit DLLs
T1547.004 Winlogon Helper DLL
T1547.005 Security Support Provider
T1574.001 DLL Search Order Hijacking
TA0003 Persistence Tactic
Autoruns Sysinternals tool showing persistent entries

Tools & Systems

Tool Purpose
Sysinternals Autoruns Comprehensive persistence enumeration
Velociraptor Endpoint-wide persistence artifact collection
CrowdStrike Falcon Real-time persistence monitoring
Sysmon Registry and WMI event monitoring
OSQuery SQL-based persistence queries
RECmd Registry Explorer for forensic analysis
Splunk SIEM correlation of persistence events

Common Scenarios

  1. Registry Run Key Backdoor: Malware adds HKCU\Software\Microsoft\Windows\CurrentVersion\Run entry pointing to payload in %APPDATA%.
  2. WMI Event Subscription: Adversary creates WMI consumer/filter pair that executes PowerShell on system boot.
  3. Malicious Service: Attacker creates Windows service with sc create pointing to a backdoor binary.
  4. COM Object Hijack: Legitimate COM CLSID InprocServer32 path replaced with malicious DLL.
  5. IFEO Debugger Injection: Image File Execution Options key set with debugger pointing to implant for common utilities.

Output Format

Hunt ID: TH-PERSIST-[DATE]-[SEQ]
Persistence Type: [Registry/Service/Task/WMI/COM/Other]
MITRE Technique: T1547.xxx / T1543.xxx / T1053.xxx
Location: [Full registry key / service name / task path]
Value: [Binary path / command line]
Host(s): [Affected endpoints]
Signed: [Yes/No]
Hash: [SHA256]
Creation Time: [Timestamp]
Risk Level: [Critical/High/Medium/Low]
Verdict: [Malicious/Suspicious/Benign]
Source materials

References and resources

Everything below is rendered for inspection. Script files are read-only and never run.

References 3

api-reference.md2.0 KB

API Reference — Hunting for Persistence Mechanisms in Windows

Libraries Used

  • subprocess: Execute reg query, schtasks, wmic commands to enumerate persistence
  • csv: Parse schtasks CSV output for scheduled task analysis
  • re: Pattern matching for suspicious command-line indicators

CLI Interface

python agent.py registry    # Enumerate registry Run keys
python agent.py tasks       # Enumerate scheduled tasks
python agent.py services    # Enumerate suspicious services
python agent.py all         # Run all persistence hunts

Core Functions

enumerate_registry_persistence()

Queries 11 common registry persistence locations using reg query and flags entries matching suspicious indicators.

Returns: dict with total_entries, suspicious_entries, and findings list (each with key, name, type, value, suspicious).

enumerate_scheduled_tasks()

Runs schtasks /query /fo CSV /v and flags tasks with suspicious actions or non-Microsoft authors.

Returns: dict with total_tasks, suspicious_tasks, and findings list.

enumerate_services()

Uses wmic service get to list services and flags those running from unusual filesystem paths.

Returns: dict with total_services, suspicious_services, and filtered findings.

parse_reg_output(output, parent_key)

Parses reg query text output into structured entries with key, name, type, value fields.

Registry Keys Checked

Key Path Persistence Type
HKLM\...\CurrentVersion\Run Auto-start programs
HKLM\...\Winlogon Logon scripts, shell replacement
HKLM\...\Active Setup Per-user component execution
HKLM\...\Services Service binary paths
HKLM\...\Image File Execution Options Debugger hijacking

Suspicious Indicators

Patterns flagging entries: \\temp\\, powershell.*-enc, mshta.exe, rundll32.exe, base64, downloadstring, \\users\\public\\

Dependencies

No external packages required — uses only Python standard library and Windows built-in commands.

standards.md4.3 KB

Standards and References - Windows Persistence Hunting

MITRE ATT&CK Persistence Techniques (TA0003)

Boot or Logon Autostart Execution (T1547)

Sub-Technique Name Registry/Location
T1547.001 Registry Run Keys / Startup Folder HKLM/HKCU Run, RunOnce, Startup
T1547.002 Authentication Package HKLM\SYSTEM\CurrentControlSet\Control\Lsa
T1547.003 Time Providers HKLM\System\CurrentControlSet\Services\W32Time\TimeProviders
T1547.004 Winlogon Helper DLL HKLM\Software\Microsoft\Windows NT\CurrentVersion\Winlogon
T1547.005 Security Support Provider HKLM\SYSTEM\CurrentControlSet\Control\Lsa\Security Packages
T1547.006 Kernel Modules and Extensions Driver loading
T1547.009 Shortcut Modification .lnk files in Startup
T1547.010 Port Monitors HKLM\SYSTEM\CurrentControlSet\Control\Print\Monitors
T1547.012 Print Processors HKLM\SYSTEM\CurrentControlSet\Control\Print\Environments
T1547.014 Active Setup HKLM\SOFTWARE\Microsoft\Active Setup\Installed Components
T1547.015 Login Items (macOS)

Create or Modify System Process (T1543)

Sub-Technique Name
T1543.003 Windows Service
T1543.004 Launch Daemon (macOS/Linux)

Scheduled Task/Job (T1053)

Sub-Technique Name
T1053.005 Scheduled Task
T1053.003 Cron
T1053.002 At

Event Triggered Execution (T1546)

Sub-Technique Name
T1546.001 Change Default File Association
T1546.002 Screensaver
T1546.003 WMI Event Subscription
T1546.004 Unix Shell Configuration Modification
T1546.007 Netsh Helper DLL
T1546.008 Accessibility Features (sethc, utilman, narrator)
T1546.010 AppInit DLLs
T1546.011 Application Shimming
T1546.012 Image File Execution Options Injection
T1546.013 PowerShell Profile
T1546.015 COM Hijacking
T1546.016 Installer Packages

Hijack Execution Flow (T1574)

Sub-Technique Name
T1574.001 DLL Search Order Hijacking
T1574.002 DLL Side-Loading
T1574.006 Dynamic Linker Hijacking
T1574.008 Path Interception by Search Order Hijacking
T1574.009 Path Interception by Unquoted Service Path
T1574.011 Services Registry Permissions Weakness
T1574.012 COR_PROFILER

Key Registry Persistence Locations

HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Run
HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce
HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Run
HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce
HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\RunServices
HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Explorer\Run
HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Explorer\Run
HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\Shell
HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\Userinit
HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\Notify
HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\BootExecute
HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\SharedTaskScheduler
HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\ShellServiceObjectDelayLoad
HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options
HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SilentProcessExit
HKLM\SOFTWARE\Classes\CLSID\{GUID}\InprocServer32
HKLM\SYSTEM\CurrentControlSet\Control\Lsa\Security Packages
HKLM\SYSTEM\CurrentControlSet\Control\Lsa\Authentication Packages
HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Custom
HKLM\SOFTWARE\Microsoft\Active Setup\Installed Components

Detection Event IDs

Source Event ID Meaning
Sysmon 12 Registry object created/deleted
Sysmon 13 Registry value set
Sysmon 14 Registry object renamed
Sysmon 19 WMI EventFilter created
Sysmon 20 WMI EventConsumer created
Sysmon 21 WMI ConsumerToFilter binding
Windows Security 4697 Service installed
Windows Security 4698 Scheduled task created
Windows Security 4699 Scheduled task deleted
Windows Security 7045 New service installed
Task Scheduler 106 Task registered
Task Scheduler 140 Task updated
workflows.md3.3 KB

Detailed Hunting Workflow - Windows Persistence

Phase 1: Registry Persistence Hunting

Step 1.1 - Run Key Monitoring

index=sysmon (EventCode=12 OR EventCode=13)
| where match(TargetObject, "(?i)\\\\CurrentVersion\\\\(Run|RunOnce|Policies\\\\Explorer\\\\Run)")
| table _time Computer User EventType TargetObject Details Image
| sort -_time

Step 1.2 - Winlogon Modification

index=sysmon EventCode=13
| where match(TargetObject, "(?i)\\\\Winlogon\\\\(Shell|Userinit|Notify)")
| table _time Computer User TargetObject Details Image

Step 1.3 - IFEO Injection

index=sysmon EventCode=13
| where match(TargetObject, "(?i)Image File Execution Options.*\\\\(Debugger|GlobalFlag)")
| table _time Computer User TargetObject Details Image

Step 1.4 - KQL for Registry Persistence

DeviceRegistryEvents
| where Timestamp > ago(7d)
| where RegistryKey has_any ("CurrentVersion\\Run","Winlogon\\Shell","Image File Execution Options")
| where ActionType in ("RegistryValueSet","RegistryKeyCreated")
| project Timestamp, DeviceName, RegistryKey, RegistryValueName, RegistryValueData, InitiatingProcessFileName

Phase 2: Service Persistence Hunting

Step 2.1 - New Service Installation

index=wineventlog (EventCode=7045 OR EventCode=4697)
| where NOT match(Service_File_Name, "(?i)(windows|program files|system32)")
| table _time Computer Service_Name Service_File_Name Service_Start_Type Service_Account
| sort -_time

Step 2.2 - Service Binary Path Anomalies

index=wineventlog EventCode=7045
| where match(Service_File_Name, "(?i)(temp|appdata|public|programdata|users)")
    OR match(Service_File_Name, "(?i)(powershell|cmd\.exe|wscript|cscript|mshta)")
| table _time Computer Service_Name Service_File_Name

Phase 3: WMI Persistence Hunting

Step 3.1 - WMI Event Subscription

index=sysmon (EventCode=19 OR EventCode=20 OR EventCode=21)
| table _time Computer User EventType Operation Destination Consumer Filter
| sort -_time

Step 3.2 - PowerShell WMI Creation

index=sysmon EventCode=1 Image="*\\powershell.exe"
| where match(CommandLine, "(?i)(Register-WmiEvent|Set-WmiInstance|__EventFilter|CommandLineEventConsumer)")
| table _time Computer User CommandLine

Phase 4: COM Hijacking

Step 4.1 - InprocServer32 Modifications

index=sysmon EventCode=13
| where match(TargetObject, "(?i)\\\\InprocServer32\\\\$")
| where NOT match(Details, "(?i)(system32|syswow64|program files|windows)")
| table _time Computer User TargetObject Details Image

Phase 5: Scheduled Task Persistence

Step 5.1 - New Scheduled Tasks

index=wineventlog (EventCode=4698 OR source="Microsoft-Windows-TaskScheduler/Operational" EventCode=106)
| table _time Computer User Task_Name Task_Content
| sort -_time

Phase 6: Cross-Reference and Validate

Step 6.1 - Autoruns Comparison

  • Export Autoruns data from reference system: autorunsc.exe -a * -c -h -s -v -vt > autoruns_baseline.csv
  • Export from suspect system: autorunsc.exe -a * -c -h -s -v -vt > autoruns_current.csv
  • Diff the two outputs to find new entries

Step 6.2 - Verify Binary Signatures

For each suspicious persistence entry:

  1. Check digital signature validity
  2. Verify file hash against threat intel
  3. Check VirusTotal reputation
  4. Analyze with YARA rules
  5. Submit to sandbox if needed

Scripts 2

agent.py6.2 KB
Display-only source. This catalog never executes bundled scripts.
#!/usr/bin/env python3
"""Agent for hunting Windows persistence mechanisms across registry, services, and scheduled tasks."""

import json
import argparse
import subprocess
import re
from datetime import datetime

REGISTRY_PERSISTENCE_KEYS = [
    r"HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Run",
    r"HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce",
    r"HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Run",
    r"HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce",
    r"HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon",
    r"HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Explorer\Run",
    r"HKLM\SOFTWARE\Microsoft\Active Setup\Installed Components",
    r"HKLM\SYSTEM\CurrentControlSet\Services",
    r"HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options",
    r"HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders",
    r"HKCU\Environment",
]

SUSPICIOUS_INDICATORS = [
    r"\\temp\\", r"\\tmp\\", r"\\appdata\\local\\temp\\",
    r"powershell.*-enc", r"cmd\.exe.*/c\s+",
    r"\\users\\public\\", r"\\programdata\\",
    r"mshta\.exe", r"rundll32\.exe", r"regsvr32\.exe",
    r"wscript\.exe", r"cscript\.exe",
    r"base64", r"iex\s*\(", r"downloadstring",
]


def enumerate_registry_persistence():
    """Enumerate common Windows registry persistence locations using reg query."""
    findings = []
    for key in REGISTRY_PERSISTENCE_KEYS:
        try:
            result = subprocess.run(
                ["reg", "query", key], capture_output=True, text=True, timeout=10
            )
            if result.returncode == 0:
                entries = parse_reg_output(result.stdout, key)
                for entry in entries:
                    entry["suspicious"] = any(
                        re.search(p, entry.get("value", ""), re.I)
                        for p in SUSPICIOUS_INDICATORS
                    )
                    findings.append(entry)
        except (subprocess.TimeoutExpired, FileNotFoundError):
            continue
    return {
        "timestamp": datetime.utcnow().isoformat(),
        "total_entries": len(findings),
        "suspicious_entries": sum(1 for f in findings if f.get("suspicious")),
        "findings": findings,
    }


def parse_reg_output(output, parent_key):
    """Parse reg query output into structured entries."""
    entries = []
    current_key = parent_key
    for line in output.strip().split("\n"):
        line = line.strip()
        if not line:
            continue
        if line.startswith("HK"):
            current_key = line
            continue
        parts = re.split(r"\s{2,}", line, maxsplit=2)
        if len(parts) >= 3:
            entries.append({
                "key": current_key,
                "name": parts[0],
                "type": parts[1],
                "value": parts[2],
            })
    return entries


def enumerate_scheduled_tasks():
    """List scheduled tasks and flag suspicious ones."""
    try:
        result = subprocess.run(
            ["schtasks", "/query", "/fo", "CSV", "/v"],
            capture_output=True, text=True, timeout=30
        )
    except (subprocess.TimeoutExpired, FileNotFoundError):
        return {"error": "schtasks not available"}
    findings = []
    import csv
    from io import StringIO
    reader = csv.DictReader(StringIO(result.stdout))
    for row in reader:
        task_name = row.get("TaskName", "")
        action = row.get("Task To Run", "")
        author = row.get("Author", "")
        suspicious = any(re.search(p, action, re.I) for p in SUSPICIOUS_INDICATORS)
        if suspicious or "\\Microsoft\\" not in task_name:
            findings.append({
                "task_name": task_name,
                "action": action[:500],
                "author": author,
                "status": row.get("Status", ""),
                "next_run": row.get("Next Run Time", ""),
                "suspicious": suspicious,
            })
    return {
        "total_tasks": len(findings),
        "suspicious_tasks": sum(1 for f in findings if f["suspicious"]),
        "findings": findings,
    }


def enumerate_services():
    """List Windows services and flag those running from unusual paths."""
    try:
        result = subprocess.run(
            ["wmic", "service", "get", "Name,PathName,StartMode,State", "/format:csv"],
            capture_output=True, text=True, timeout=30
        )
    except (subprocess.TimeoutExpired, FileNotFoundError):
        return {"error": "wmic not available"}
    findings = []
    for line in result.stdout.strip().split("\n")[1:]:
        parts = line.strip().split(",")
        if len(parts) >= 5:
            path = parts[3]
            suspicious = any(re.search(p, path, re.I) for p in SUSPICIOUS_INDICATORS)
            findings.append({
                "name": parts[1], "path": path,
                "start_mode": parts[4] if len(parts) > 4 else "",
                "state": parts[2], "suspicious": suspicious,
            })
    return {
        "total_services": len(findings),
        "suspicious_services": sum(1 for f in findings if f["suspicious"]),
        "findings": [f for f in findings if f["suspicious"]],
    }


def main():
    parser = argparse.ArgumentParser(description="Hunt for Windows persistence mechanisms")
    sub = parser.add_subparsers(dest="command")
    sub.add_parser("registry", help="Enumerate registry persistence keys")
    sub.add_parser("tasks", help="Enumerate scheduled tasks")
    sub.add_parser("services", help="Enumerate suspicious services")
    sub.add_parser("all", help="Run all persistence hunts")
    args = parser.parse_args()
    if args.command == "registry":
        result = enumerate_registry_persistence()
    elif args.command == "tasks":
        result = enumerate_scheduled_tasks()
    elif args.command == "services":
        result = enumerate_services()
    elif args.command == "all":
        result = {
            "registry": enumerate_registry_persistence(),
            "scheduled_tasks": enumerate_scheduled_tasks(),
            "services": enumerate_services(),
            "timestamp": datetime.utcnow().isoformat(),
        }
    else:
        parser.print_help()
        return
    print(json.dumps(result, indent=2, default=str))


if __name__ == "__main__":
    main()
process.py14.0 KB
Display-only source. This catalog never executes bundled scripts.
#!/usr/bin/env python3
"""
Windows Persistence Mechanism Hunter
Analyzes logs for registry, service, scheduled task, WMI, and COM persistence.
"""

import json
import csv
import argparse
import datetime
import re
from collections import defaultdict
from pathlib import Path

# Registry persistence locations to monitor
REGISTRY_PERSISTENCE_KEYS = {
    "run_keys": {
        "patterns": [
            r"\\CurrentVersion\\Run($|\\)",
            r"\\CurrentVersion\\RunOnce($|\\)",
            r"\\CurrentVersion\\RunServices($|\\)",
            r"\\Policies\\Explorer\\Run($|\\)",
        ],
        "technique": "T1547.001",
        "risk_base": 40,
    },
    "winlogon": {
        "patterns": [
            r"\\Winlogon\\(Shell|Userinit|Notify|VmApplet|AppSetup)",
        ],
        "technique": "T1547.004",
        "risk_base": 60,
    },
    "ifeo": {
        "patterns": [
            r"\\Image File Execution Options\\.*\\Debugger",
            r"\\SilentProcessExit\\.*\\MonitorProcess",
        ],
        "technique": "T1546.012",
        "risk_base": 70,
    },
    "lsa_packages": {
        "patterns": [
            r"\\Control\\Lsa\\(Security Packages|Authentication Packages)",
        ],
        "technique": "T1547.005",
        "risk_base": 80,
    },
    "appinit_dlls": {
        "patterns": [
            r"\\Windows\\CurrentVersion\\Windows\\AppInit_DLLs",
            r"\\Windows\\CurrentVersion\\Windows\\LoadAppInit_DLLs",
        ],
        "technique": "T1546.010",
        "risk_base": 70,
    },
    "com_hijack": {
        "patterns": [
            r"\\InprocServer32\\?$",
            r"\\InprocServer32\\\\$",
        ],
        "technique": "T1546.015",
        "risk_base": 50,
    },
    "active_setup": {
        "patterns": [
            r"\\Active Setup\\Installed Components\\.*\\StubPath",
        ],
        "technique": "T1547.014",
        "risk_base": 60,
    },
    "screensaver": {
        "patterns": [
            r"\\Control Panel\\Desktop\\SCRNSAVE\.EXE",
        ],
        "technique": "T1546.002",
        "risk_base": 50,
    },
    "netsh_helper": {
        "patterns": [
            r"\\Netsh\\.*\\(DLL|HelperDll)",
        ],
        "technique": "T1546.007",
        "risk_base": 60,
    },
    "print_monitor": {
        "patterns": [
            r"\\Control\\Print\\Monitors\\.*\\Driver",
        ],
        "technique": "T1547.010",
        "risk_base": 70,
    },
    "boot_execute": {
        "patterns": [
            r"\\Session Manager\\BootExecute",
        ],
        "technique": "T1547.001",
        "risk_base": 80,
    },
}

# Suspicious service binary paths
SUSPICIOUS_SERVICE_PATHS = [
    r"\\temp\\", r"\\tmp\\", r"\\appdata\\", r"\\programdata\\",
    r"\\public\\", r"\\downloads\\", r"\\users\\.*\\desktop\\",
    r"powershell", r"cmd\.exe.*\/c", r"wscript", r"cscript",
    r"mshta", r"rundll32", r"regsvr32",
]

# Legitimate system paths for services
LEGITIMATE_SERVICE_PATHS = [
    r"^\"?C:\\Windows\\",
    r"^\"?C:\\Program Files",
    r"^\"?C:\\ProgramData\\Microsoft",
    r"^\"?\"?svchost\.exe",
]


def parse_logs(input_path: str) -> list[dict]:
    """Parse input 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 event fields."""
    field_map = {
        "event_id": ["EventCode", "EventID", "event_id", "event.code"],
        "registry_key": ["TargetObject", "RegistryKey", "registry.key", "registry_key"],
        "registry_value": ["Details", "RegistryValueData", "registry.data", "registry_value"],
        "image": ["Image", "image", "process.executable", "InitiatingProcessFileName"],
        "command_line": ["CommandLine", "command_line", "ProcessCommandLine"],
        "user": ["User", "user", "AccountName", "user.name"],
        "hostname": ["Computer", "hostname", "DeviceName", "host.name"],
        "timestamp": ["UtcTime", "timestamp", "Timestamp", "@timestamp"],
        "service_name": ["Service_Name", "ServiceName", "service.name"],
        "service_path": ["Service_File_Name", "ServiceFileName", "ImagePath"],
        "task_name": ["Task_Name", "TaskName"],
        "task_content": ["Task_Content", "TaskContent"],
        "event_type": ["EventType", "ActionType", "event_type"],
        "wmi_operation": ["Operation", "wmi_operation"],
        "wmi_destination": ["Destination", "Consumer", "wmi_destination"],
    }
    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 analyze_registry_persistence(event: dict) -> dict | None:
    """Check registry events for persistence indicators."""
    event_id = event.get("event_id", "")
    if event_id not in ("12", "13", "14"):
        return None

    reg_key = event.get("registry_key", "")
    if not reg_key:
        return None

    for category, info in REGISTRY_PERSISTENCE_KEYS.items():
        for pattern in info["patterns"]:
            if re.search(pattern, reg_key, re.IGNORECASE):
                value = event.get("registry_value", "")
                risk = info["risk_base"]

                # Increase risk for suspicious paths in value
                if value:
                    for susp in SUSPICIOUS_SERVICE_PATHS:
                        if re.search(susp, value, re.IGNORECASE):
                            risk += 20
                            break

                    # Decrease risk for legitimate paths
                    for legit in LEGITIMATE_SERVICE_PATHS:
                        if re.search(legit, value, re.IGNORECASE):
                            risk -= 20
                            break

                risk_level = (
                    "CRITICAL" if risk >= 80 else
                    "HIGH" if risk >= 60 else
                    "MEDIUM" if risk >= 40 else "LOW"
                )

                return {
                    "persistence_type": "REGISTRY",
                    "category": category,
                    "technique": info["technique"],
                    "registry_key": reg_key,
                    "value": value,
                    "modifying_process": event.get("image", ""),
                    "hostname": event.get("hostname", "unknown"),
                    "user": event.get("user", "unknown"),
                    "timestamp": event.get("timestamp", "unknown"),
                    "risk_score": risk,
                    "risk_level": risk_level,
                }
    return None


def analyze_service_persistence(event: dict) -> dict | None:
    """Check for suspicious service installations."""
    event_id = event.get("event_id", "")
    if event_id not in ("7045", "4697"):
        return None

    service_path = event.get("service_path", "")
    service_name = event.get("service_name", "")
    if not service_path:
        return None

    risk = 30
    indicators = []

    for pattern in SUSPICIOUS_SERVICE_PATHS:
        if re.search(pattern, service_path, re.IGNORECASE):
            risk += 25
            indicators.append(f"Suspicious service path: {pattern}")

    is_legitimate = False
    for pattern in LEGITIMATE_SERVICE_PATHS:
        if re.search(pattern, service_path, re.IGNORECASE):
            is_legitimate = True
            break

    if not is_legitimate:
        risk += 15
        indicators.append("Service binary outside standard paths")

    if not indicators:
        return None

    risk_level = (
        "CRITICAL" if risk >= 80 else
        "HIGH" if risk >= 60 else
        "MEDIUM" if risk >= 40 else "LOW"
    )

    return {
        "persistence_type": "SERVICE",
        "technique": "T1543.003",
        "service_name": service_name,
        "service_path": service_path,
        "hostname": event.get("hostname", "unknown"),
        "user": event.get("user", "unknown"),
        "timestamp": event.get("timestamp", "unknown"),
        "risk_score": risk,
        "risk_level": risk_level,
        "indicators": indicators,
    }


def analyze_wmi_persistence(event: dict) -> dict | None:
    """Check for WMI event subscription persistence."""
    event_id = event.get("event_id", "")
    if event_id not in ("19", "20", "21"):
        return None

    operation = event.get("wmi_operation", "")
    destination = event.get("wmi_destination", "")

    wmi_type = {
        "19": "EventFilter",
        "20": "EventConsumer",
        "21": "ConsumerToFilter",
    }.get(event_id, "Unknown")

    return {
        "persistence_type": "WMI_EVENT_SUBSCRIPTION",
        "technique": "T1546.003",
        "wmi_type": wmi_type,
        "operation": operation,
        "destination": destination,
        "hostname": event.get("hostname", "unknown"),
        "user": event.get("user", "unknown"),
        "timestamp": event.get("timestamp", "unknown"),
        "risk_score": 70,
        "risk_level": "HIGH",
        "indicators": [f"WMI {wmi_type} created"],
    }


def analyze_scheduled_task(event: dict) -> dict | None:
    """Check for scheduled task persistence."""
    event_id = event.get("event_id", "")
    if event_id not in ("4698", "106"):
        return None

    task_name = event.get("task_name", "")
    task_content = event.get("task_content", "")

    risk = 40
    indicators = []

    suspicious_task_patterns = [
        r"powershell", r"cmd\.exe", r"wscript", r"cscript",
        r"mshta", r"http[s]?://", r"-enc\s", r"iex\s",
        r"downloadstring", r"\\temp\\", r"\\appdata\\",
    ]

    for pattern in suspicious_task_patterns:
        if re.search(pattern, task_content, re.IGNORECASE):
            risk += 15
            indicators.append(f"Suspicious content: {pattern}")

    if not indicators:
        return None

    risk_level = (
        "CRITICAL" if risk >= 80 else
        "HIGH" if risk >= 60 else
        "MEDIUM" if risk >= 40 else "LOW"
    )

    return {
        "persistence_type": "SCHEDULED_TASK",
        "technique": "T1053.005",
        "task_name": task_name,
        "task_content": task_content[:500],
        "hostname": event.get("hostname", "unknown"),
        "user": event.get("user", "unknown"),
        "timestamp": event.get("timestamp", "unknown"),
        "risk_score": risk,
        "risk_level": risk_level,
        "indicators": indicators,
    }


def run_hunt(input_path: str, output_dir: str) -> None:
    """Execute persistence mechanism hunt."""
    print(f"[*] Windows Persistence Hunt - {datetime.datetime.now().isoformat()}")

    events = parse_logs(input_path)
    print(f"[*] Loaded {len(events)} events")

    findings = []
    stats = defaultdict(int)

    analyzers = [
        analyze_registry_persistence,
        analyze_service_persistence,
        analyze_wmi_persistence,
        analyze_scheduled_task,
    ]

    for raw_event in events:
        event = normalize_event(raw_event)
        for analyzer in analyzers:
            result = analyzer(event)
            if result:
                findings.append(result)
                stats[result["persistence_type"]] += 1
                stats[result["risk_level"]] += 1

    output_path = Path(output_dir)
    output_path.mkdir(parents=True, exist_ok=True)

    with open(output_path / "persistence_findings.json", "w", encoding="utf-8") as f:
        json.dump({
            "hunt_id": f"TH-PERSIST-{datetime.date.today().isoformat()}",
            "total_events": len(events),
            "total_findings": len(findings),
            "statistics": dict(stats),
            "findings": findings,
        }, f, indent=2)

    with open(output_path / "hunt_report.md", "w", encoding="utf-8") as f:
        f.write(f"# Windows Persistence 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.get("risk_score", 0), reverse=True)[:30]:
            f.write(f"### [{finding['risk_level']}] {finding['persistence_type']} - {finding.get('technique','')}\n")
            f.write(f"- **Host**: {finding['hostname']}\n")
            if finding.get("registry_key"):
                f.write(f"- **Key**: `{finding['registry_key']}`\n")
            if finding.get("service_name"):
                f.write(f"- **Service**: {finding['service_name']}\n")
            if finding.get("task_name"):
                f.write(f"- **Task**: {finding['task_name']}\n")
            f.write("\n")

    print(f"[+] {len(findings)} findings written to {output_dir}")


def main():
    parser = argparse.ArgumentParser(description="Windows Persistence Mechanism Hunter")
    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="./persistence_output")

    query_p = subparsers.add_parser("queries")
    query_p.add_argument("--platform", "-p", choices=["splunk", "kql", "all"], default="all")

    args = parser.parse_args()

    if args.command == "hunt":
        run_hunt(args.input, args.output)
    elif args.command == "queries":
        print("=== Registry Persistence ===")
        print("""index=sysmon (EventCode=12 OR EventCode=13)
| where match(TargetObject, "(?i)\\\\CurrentVersion\\\\(Run|RunOnce)")
| table _time Computer User TargetObject Details Image""")
        print("\n=== Service Persistence ===")
        print("""index=wineventlog (EventCode=7045 OR EventCode=4697)
| table _time Computer Service_Name Service_File_Name""")
        print("\n=== WMI Persistence ===")
        print("""index=sysmon (EventCode=19 OR EventCode=20 OR EventCode=21)
| table _time Computer User EventType Operation Destination""")
    else:
        parser.print_help()


if __name__ == "__main__":
    main()

Assets 1

template.mdtext/markdown · 2.0 KB
Keep exploring