mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-07-14 11:51:16 +03:00
175 lines
6.7 KiB
Python
175 lines
6.7 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
WMI Subscription Persistence Detection Script
|
|
Analyzes Sysmon Events 19/20/21 and process creation logs to detect
|
|
malicious WMI permanent event subscriptions used for persistence.
|
|
"""
|
|
|
|
import json
|
|
import csv
|
|
import argparse
|
|
import datetime
|
|
import re
|
|
from pathlib import Path
|
|
|
|
DANGEROUS_CONSUMER_TYPES = {"ActiveScriptEventConsumer", "CommandLineEventConsumer"}
|
|
|
|
SUSPICIOUS_FILTER_PATTERNS = [
|
|
(r"__InstanceCreationEvent.*Win32_Process", "process_start_trigger"),
|
|
(r"__InstanceModificationEvent.*Win32_PerfFormattedData", "system_startup_trigger"),
|
|
(r"__TimerEvent", "timer_based_trigger"),
|
|
(r"__InstanceCreationEvent.*Win32_LogonSession", "user_logon_trigger"),
|
|
(r"Win32_ProcessStartTrace", "process_trace_trigger"),
|
|
]
|
|
|
|
SUSPICIOUS_CONSUMER_PATTERNS = [
|
|
(r"(?i)powershell", "powershell_execution"),
|
|
(r"(?i)(cmd\.exe|cmd\s/c)", "command_execution"),
|
|
(r"(?i)(http|https)://", "network_callback"),
|
|
(r"(?i)(wscript|cscript)", "script_execution"),
|
|
(r"(?i)(base64|encodedcommand|-enc\s)", "encoded_content"),
|
|
(r"(?i)(invoke-expression|iex|downloadstring)", "download_cradle"),
|
|
]
|
|
|
|
|
|
def parse_events(input_path: str) -> list[dict]:
|
|
"""Parse Sysmon WMI event logs."""
|
|
path = Path(input_path)
|
|
events = []
|
|
if path.suffix == ".json":
|
|
with open(path, "r", encoding="utf-8") as f:
|
|
data = json.load(f)
|
|
events = data if isinstance(data, list) else data.get("events", [])
|
|
elif path.suffix == ".csv":
|
|
with open(path, "r", encoding="utf-8-sig") as f:
|
|
events = [dict(row) for row in csv.DictReader(f)]
|
|
return events
|
|
|
|
|
|
def detect_wmi_persistence(events: list[dict]) -> list[dict]:
|
|
"""Detect malicious WMI event subscriptions."""
|
|
findings = []
|
|
filters = {}
|
|
consumers = {}
|
|
|
|
for event in events:
|
|
event_code = str(event.get("EventCode", event.get("EventID", "")))
|
|
computer = event.get("Computer", event.get("host", ""))
|
|
timestamp = event.get("UtcTime", event.get("_time", ""))
|
|
user = event.get("User", event.get("user", ""))
|
|
|
|
if event_code == "19": # EventFilter
|
|
name = event.get("Name", event.get("name", ""))
|
|
query = event.get("Query", event.get("query", ""))
|
|
filters[name] = {"query": query, "computer": computer, "timestamp": timestamp}
|
|
|
|
for pattern, trigger_type in SUSPICIOUS_FILTER_PATTERNS:
|
|
if re.search(pattern, query, re.IGNORECASE):
|
|
findings.append({
|
|
"timestamp": timestamp,
|
|
"computer": computer,
|
|
"user": user,
|
|
"event_type": "EventFilter",
|
|
"name": name,
|
|
"query": query,
|
|
"trigger_type": trigger_type,
|
|
"severity": "HIGH",
|
|
"technique": "T1546.003",
|
|
})
|
|
break
|
|
|
|
elif event_code == "20": # EventConsumer
|
|
name = event.get("Name", event.get("name", ""))
|
|
consumer_type = event.get("Type", event.get("Destination", ""))
|
|
destination = event.get("Destination", event.get("destination", ""))
|
|
consumers[name] = {"type": consumer_type, "destination": destination}
|
|
|
|
severity = "CRITICAL" if any(dt in str(consumer_type) for dt in DANGEROUS_CONSUMER_TYPES) else "MEDIUM"
|
|
|
|
indicators = []
|
|
for pattern, indicator in SUSPICIOUS_CONSUMER_PATTERNS:
|
|
if re.search(pattern, str(destination)):
|
|
indicators.append(indicator)
|
|
severity = "CRITICAL"
|
|
|
|
findings.append({
|
|
"timestamp": timestamp,
|
|
"computer": computer,
|
|
"user": user,
|
|
"event_type": "EventConsumer",
|
|
"name": name,
|
|
"consumer_type": str(consumer_type),
|
|
"destination": destination,
|
|
"indicators": indicators,
|
|
"severity": severity,
|
|
"technique": "T1546.003",
|
|
})
|
|
|
|
elif event_code == "21": # Binding
|
|
consumer = event.get("Consumer", event.get("consumer", ""))
|
|
filter_ref = event.get("Filter", event.get("filter", ""))
|
|
findings.append({
|
|
"timestamp": timestamp,
|
|
"computer": computer,
|
|
"user": user,
|
|
"event_type": "FilterToConsumerBinding",
|
|
"consumer": consumer,
|
|
"filter": filter_ref,
|
|
"severity": "HIGH",
|
|
"technique": "T1546.003",
|
|
})
|
|
|
|
elif event_code == "1": # Process creation
|
|
parent = event.get("ParentImage", "")
|
|
image = event.get("Image", "")
|
|
cmdline = event.get("CommandLine", "")
|
|
if "wmiprvse.exe" in parent.lower():
|
|
image_name = image.split("\\")[-1].lower()
|
|
if image_name in {"cmd.exe", "powershell.exe", "wscript.exe", "cscript.exe", "mshta.exe"}:
|
|
findings.append({
|
|
"timestamp": timestamp,
|
|
"computer": computer,
|
|
"user": user,
|
|
"event_type": "WmiPrvSe_Child_Process",
|
|
"child_process": image,
|
|
"command_line": cmdline,
|
|
"severity": "HIGH",
|
|
"technique": "T1546.003",
|
|
})
|
|
|
|
return sorted(findings, key=lambda x: {"CRITICAL": 0, "HIGH": 1, "MEDIUM": 2}.get(x["severity"], 3))
|
|
|
|
|
|
def run_hunt(input_path: str, output_dir: str) -> None:
|
|
"""Execute WMI subscription persistence hunt."""
|
|
print(f"[*] WMI Subscription Persistence Hunt - {datetime.datetime.now().isoformat()}")
|
|
events = parse_events(input_path)
|
|
print(f"[*] Loaded {len(events)} events")
|
|
|
|
findings = detect_wmi_persistence(events)
|
|
print(f"[!] WMI persistence detections: {len(findings)}")
|
|
|
|
output_path = Path(output_dir)
|
|
output_path.mkdir(parents=True, exist_ok=True)
|
|
|
|
with open(output_path / "wmi_persistence_findings.json", "w", encoding="utf-8") as f:
|
|
json.dump({
|
|
"hunt_id": f"TH-WMI-{datetime.date.today().isoformat()}",
|
|
"total_events": len(events),
|
|
"findings_count": len(findings),
|
|
"findings": findings,
|
|
}, f, indent=2)
|
|
print(f"[+] Results written to {output_dir}")
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description="WMI Subscription Persistence Detection")
|
|
parser.add_argument("--input", "-i", required=True)
|
|
parser.add_argument("--output", "-o", default="./wmi_hunt_output")
|
|
args = parser.parse_args()
|
|
run_hunt(args.input, args.output)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|