npx skills add mukul975/Anthropic-Cybersecurity-SkillsMITRE ATT&CK
NIST CSF 2.0
MITRE D3FEND
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
- 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).
- Collect Endpoint Data: Use EDR, Sysmon, or Velociraptor to collect current persistence artifacts from endpoints across the environment.
- Baseline Legitimate Persistence: Compare collected data against known-good baselines (Autoruns snapshots, GPO-deployed entries, SCCM configurations).
- Identify Anomalies: Flag new, unsigned, or unknown entries in persistence locations that deviate from the baseline.
- Investigate Suspicious Entries: For each anomaly, examine the binary it points to, its digital signature, file hash, and creation timestamp.
- Correlate with Process Activity: Link persistence entries to process execution, network activity, and user login events.
- 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
- Registry Run Key Backdoor: Malware adds
HKCU\Software\Microsoft\Windows\CurrentVersion\Runentry pointing to payload in%APPDATA%. - WMI Event Subscription: Adversary creates WMI consumer/filter pair that executes PowerShell on system boot.
- Malicious Service: Attacker creates Windows service with
sc createpointing to a backdoor binary. - COM Object Hijack: Legitimate COM CLSID InprocServer32 path replaced with malicious DLL.
- 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]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,wmiccommands 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 huntsCore 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 ComponentsDetection 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 -_timeStep 1.2 - Winlogon Modification
index=sysmon EventCode=13
| where match(TargetObject, "(?i)\\\\Winlogon\\\\(Shell|Userinit|Notify)")
| table _time Computer User TargetObject Details ImageStep 1.3 - IFEO Injection
index=sysmon EventCode=13
| where match(TargetObject, "(?i)Image File Execution Options.*\\\\(Debugger|GlobalFlag)")
| table _time Computer User TargetObject Details ImageStep 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, InitiatingProcessFileNamePhase 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 -_timeStep 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_NamePhase 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 -_timeStep 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 CommandLinePhase 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 ImagePhase 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 -_timePhase 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:
- Check digital signature validity
- Verify file hash against threat intel
- Check VirusTotal reputation
- Analyze with YARA rules
- Submit to sandbox if needed
Scripts 2
agent.py6.2 KB
#!/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
#!/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()