threat hunting

Hunting For Registry Persistence Mechanisms

Hunt for registry-based persistence mechanisms including Run keys, Winlogon modifications, IFEO injection, and COM hijacking in Windows environments.

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

When to Use

  • When proactively hunting for indicators of hunting for registry persistence mechanisms in the environment
  • After threat intelligence indicates active campaigns using these techniques
  • During incident response to scope compromise related to these techniques
  • When EDR or SIEM alerts trigger on related indicators
  • During periodic security assessments and purple team exercises

Prerequisites

  • EDR platform with process and network telemetry (CrowdStrike, MDE, SentinelOne)
  • SIEM with relevant log data ingested (Splunk, Elastic, Sentinel)
  • Sysmon deployed with comprehensive configuration
  • Windows Security Event Log forwarding enabled
  • Threat intelligence feeds for IOC correlation

Workflow

  1. Formulate Hypothesis: Define a testable hypothesis based on threat intelligence or ATT&CK gap analysis.
  2. Identify Data Sources: Determine which logs and telemetry are needed to validate or refute the hypothesis.
  3. Execute Queries: Run detection queries against SIEM and EDR platforms to collect relevant events.
  4. Analyze Results: Examine query results for anomalies, correlating across multiple data sources.
  5. Validate Findings: Distinguish true positives from false positives through contextual analysis.
  6. Correlate Activity: Link findings to broader attack chains and threat actor TTPs.
  7. Document and Report: Record findings, update detection rules, and recommend response actions.

Key Concepts

Concept Description
T1547.001 Registry Run Keys
T1547.004 Winlogon Helper DLL
T1546.012 IFEO Injection
T1546.015 COM Hijacking

Tools & Systems

Tool Purpose
CrowdStrike Falcon EDR telemetry and threat detection
Microsoft Defender for Endpoint Advanced hunting with KQL
Splunk Enterprise SIEM log analysis with SPL queries
Elastic Security Detection rules and investigation timeline
Sysmon Detailed Windows event monitoring
Velociraptor Endpoint artifact collection and hunting
Sigma Rules Cross-platform detection rule format

Common Scenarios

  1. Scenario 1: Malware adding HKCU Run key for user-level persistence
  2. Scenario 2: Adversary modifying Winlogon Shell for system-level persistence
  3. Scenario 3: IFEO debugger injection for accessibility feature backdoor
  4. Scenario 4: COM object InprocServer32 hijack for DLL loading

Output Format

Hunt ID: TH-HUNTIN-[DATE]-[SEQ]
Technique: T1547.001
Host: [Hostname]
User: [Account context]
Evidence: [Log entries, process trees, network data]
Risk Level: [Critical/High/Medium/Low]
Confidence: [High/Medium/Low]
Recommended Action: [Containment, investigation, monitoring]
Source materials

References and resources

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

References 3

api-reference.md1.8 KB

API Reference — Hunting for Registry Persistence Mechanisms

Libraries Used

  • subprocess: Execute reg query /s to enumerate registry persistence keys
  • re: Pattern matching for suspicious values in registry entries
  • json: Baseline file I/O and structured output

CLI Interface

python agent.py scan [--categories run_keys winlogon ifeo] [--save-baseline out.json]
python agent.py compare --baseline baseline.json

Core Functions

scan_persistence_keys(categories=None)

Enumerates registry persistence keys across 8 categories and flags suspicious entries.

Parameters:

Name Type Description
categories list Optional subset of categories to scan (default: all 8)

Returns: dict with categories map, all_suspicious list, and total_suspicious count.

compare_baseline(baseline_file, current_scan=None)

Compares current registry state against a saved baseline to detect new persistence entries.

Parameters:

Name Type Description
baseline_file str Path to baseline JSON file from previous scan

Returns: dict with baseline_entries count, new_entries count, and findings list.

Registry Categories Scanned

Category Keys MITRE Technique
run_keys Run, RunOnce, RunOnceEx T1547.001
winlogon Winlogon Shell, Userinit T1547.004
ifeo Image File Execution Options T1546.012
appinit AppInit_DLLs T1546.010
shell_extensions ShellExecuteHooks T1546.015
browser_helpers Browser Helper Objects T1176
com_hijack CLSID overrides in HKCU T1546.015
boot_execute BootExecute, Session Manager T1542.003

Dependencies

No external packages required — uses Python standard library and reg.exe.

standards.md1.6 KB

Standards and References - Hunting For Registry Persistence Mechanisms

MITRE ATT&CK Mappings

Technique Name Description
T1547.001 Registry Run Keys See attack.mitre.org/techniques/T1547/001
T1547.004 Winlogon Helper DLL See attack.mitre.org/techniques/T1547/004
T1546.012 IFEO Injection See attack.mitre.org/techniques/T1546/012
T1546.015 COM Hijacking See attack.mitre.org/techniques/T1546/015

Detection Data Sources

Source Event ID Purpose
Sysmon 1 Process creation with command line
Sysmon 3 Network connection initiated
Sysmon 7 Image loaded (DLL)
Sysmon 10 Process access (LSASS)
Sysmon 11 File creation
Sysmon 12/13 Registry create/set
Sysmon 22 DNS query
Sysmon 25 Process tampering
Windows Security 4624 Successful logon
Windows Security 4625 Failed logon
Windows Security 4648 Explicit credential logon
Windows Security 4672 Special privileges assigned
Windows Security 4688 Process creation
Windows Security 4697 Service installed
Windows Security 4698 Scheduled task created
Windows Security 4769 Kerberos TGS requested
Windows Security 5140 Network share accessed

References

workflows.md2.9 KB

Detailed Hunting Workflow - Hunting For Registry Persistence Mechanisms

Phase 1: Data Collection and Querying

Splunk SPL Query

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

KQL Query (Microsoft Defender for Endpoint)

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

Phase 2: Baseline and Anomaly Detection

Step 2.1 - Establish Normal Behavior Baseline

  • Collect 30 days of historical data for the targeted technique
  • Document expected patterns, frequencies, and legitimate use cases
  • Identify known false positive sources and document exceptions
  • Build statistical baseline (mean, standard deviation) for key metrics

Step 2.2 - Identify Anomalies

  • Compare current activity against the 30-day baseline
  • Flag events exceeding 3 standard deviations from normal
  • Prioritize anomalies by risk score and potential business impact
  • Cross-reference with threat intelligence for known IOCs

Phase 3: Investigation and Correlation

Step 3.1 - Deep Dive Analysis

  • For each anomaly, collect full process tree context
  • Correlate with network activity, file operations, and authentication events
  • Check binary signatures, file hashes, and certificate validity
  • Review user account context and access patterns

Step 3.2 - Attack Chain Reconstruction

  • Map findings to MITRE ATT&CK kill chain stages
  • Identify initial access vector if applicable
  • Trace lateral movement and privilege escalation paths
  • Determine data access and potential exfiltration

Phase 4: Validation and Response

Step 4.1 - True/False Positive Determination

  • Verify findings with system owners and IT operations
  • Check change management records for authorized activities
  • Validate user context (authorized actions vs. compromised account)
  • Document determination rationale for each finding

Step 4.2 - Response Actions

  • For confirmed threats: initiate incident response procedures
  • For detection gaps: create or update detection rules
  • For false positives: tune existing rules and update exclusions
  • Update threat hunting playbook with lessons learned

Phase 5: Documentation and Reporting

Step 5.1 - Hunt Report

  • Summarize hypothesis, methodology, and findings
  • Include all queries executed and their results
  • Document IOCs discovered and detection rules created
  • Provide recommendations for security improvements

Step 5.2 - Knowledge Base Update

  • Add findings to threat intelligence platform
  • Update MITRE ATT&CK coverage heatmap
  • Share detection rules via Sigma format
  • Schedule follow-up hunts for related techniques

Scripts 2

agent.py5.7 KB
Display-only source. This catalog never executes bundled scripts.
#!/usr/bin/env python3
"""Agent for hunting registry-based persistence mechanisms on Windows."""

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

PERSISTENCE_KEYS = {
    "run_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\CurrentVersion\RunOnceEx",
    ],
    "winlogon": [
        r"HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon",
    ],
    "ifeo": [
        r"HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options",
    ],
    "appinit": [
        r"HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows",
    ],
    "shell_extensions": [
        r"HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\ShellExecuteHooks",
        r"HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\ShellServiceObjectDelayLoad",
    ],
    "browser_helpers": [
        r"HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Browser Helper Objects",
    ],
    "com_hijack": [
        r"HKCU\SOFTWARE\Classes\CLSID",
    ],
    "boot_execute": [
        r"HKLM\SYSTEM\CurrentControlSet\Control\Session Manager",
    ],
}

SUSPICIOUS_PATTERNS = [
    r"\\temp\\", r"\\tmp\\", r"\\appdata\\local\\temp",
    r"powershell.*-enc", r"powershell.*-nop",
    r"cmd\.exe\s+/c\s+", r"mshta\.exe", r"rundll32\.exe.*javascript",
    r"regsvr32\.exe.*/s\s+/n\s+/u\s+/i:",
    r"\\users\\public\\", r"\\programdata\\[^m]",
    r"certutil.*-decode", r"bitsadmin.*transfer",
    r"base64", r"downloadstring", r"iex\s*\(",
]


def scan_persistence_keys(categories=None):
    """Scan specified registry persistence key categories."""
    if categories is None:
        categories = list(PERSISTENCE_KEYS.keys())
    results = {"timestamp": datetime.utcnow().isoformat(), "categories": {}, "all_suspicious": []}
    for category in categories:
        keys = PERSISTENCE_KEYS.get(category, [])
        category_findings = []
        for key in keys:
            try:
                proc = subprocess.run(
                    ["reg", "query", key, "/s"], capture_output=True, text=True, timeout=10
                )
                if proc.returncode == 0:
                    entries = _parse_reg(proc.stdout, key)
                    for entry in entries:
                        entry["suspicious"] = _check_suspicious(entry.get("value", ""))
                        entry["category"] = category
                        if entry["suspicious"]:
                            results["all_suspicious"].append(entry)
                    category_findings.extend(entries)
            except (subprocess.TimeoutExpired, FileNotFoundError):
                continue
        results["categories"][category] = {
            "total": len(category_findings),
            "suspicious": sum(1 for f in category_findings if f.get("suspicious")),
            "entries": category_findings,
        }
    results["total_suspicious"] = len(results["all_suspicious"])
    return results


def _parse_reg(output, default_key):
    entries = []
    current_key = default_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 _check_suspicious(value):
    return any(re.search(p, value, re.I) for p in SUSPICIOUS_PATTERNS)


def compare_baseline(baseline_file, current_scan=None):
    """Compare current registry state against a known-good baseline."""
    with open(baseline_file, "r") as f:
        baseline = json.load(f)
    if current_scan is None:
        current_scan = scan_persistence_keys()
    baseline_set = set()
    for cat_data in baseline.get("categories", {}).values():
        for entry in cat_data.get("entries", []):
            baseline_set.add((entry.get("key", ""), entry.get("name", ""), entry.get("value", "")))
    new_entries = []
    for cat_name, cat_data in current_scan["categories"].items():
        for entry in cat_data.get("entries", []):
            key_tuple = (entry.get("key", ""), entry.get("name", ""), entry.get("value", ""))
            if key_tuple not in baseline_set:
                entry["status"] = "NEW"
                new_entries.append(entry)
    return {
        "baseline_entries": len(baseline_set),
        "new_entries": len(new_entries),
        "findings": new_entries,
    }


def main():
    parser = argparse.ArgumentParser(description="Hunt for registry persistence mechanisms")
    sub = parser.add_subparsers(dest="command")
    s = sub.add_parser("scan", help="Scan registry persistence keys")
    s.add_argument("--categories", nargs="*", choices=list(PERSISTENCE_KEYS.keys()),
                   help="Categories to scan (default: all)")
    s.add_argument("--save-baseline", help="Save scan as baseline JSON file")
    c = sub.add_parser("compare", help="Compare against baseline")
    c.add_argument("--baseline", required=True, help="Baseline JSON file")
    args = parser.parse_args()
    if args.command == "scan":
        result = scan_persistence_keys(args.categories)
        if args.save_baseline:
            with open(args.save_baseline, "w") as f:
                json.dump(result, f, indent=2)
    elif args.command == "compare":
        result = compare_baseline(args.baseline)
    else:
        parser.print_help()
        return
    print(json.dumps(result, indent=2, default=str))


if __name__ == "__main__":
    main()
process.py3.6 KB
Display-only source. This catalog never executes bundled scripts.
#!/usr/bin/env python3
"""Registry Persistence Detection - Analyzes logs for T1547.001 indicators."""

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

DETECTION_PATTERNS = [
    r'CurrentVersion\\\\Run',
    r'Winlogon\\\\Shell',
    r'Image File Execution Options',
    r'AppInit_DLLs',
    r'InprocServer32',
    r'Active Setup',
]

def parse_logs(path):
    p = Path(path)
    if p.suffix == ".json":
        with open(p, encoding="utf-8") as f:
            data = json.load(f)
            return data if isinstance(data, list) else data.get("events", [])
    elif p.suffix == ".csv":
        with open(p, encoding="utf-8-sig") as f:
            return [dict(r) for r in csv.DictReader(f)]
    return []

def analyze_event(event):
    cmd = event.get("CommandLine", event.get("command_line", event.get("ProcessCommandLine", "")))
    content = event.get("Task_Content", event.get("Parameters", event.get("RawEventData", "")))
    search_text = f"{cmd} {content}"
    risk = 0
    indicators = []
    for pattern in DETECTION_PATTERNS:
        if re.search(pattern, search_text, re.IGNORECASE):
            risk += 25
            indicators.append(f"Pattern match: {pattern}")
    if not indicators:
        return None
    risk = min(risk, 100)
    return {
        "technique": "T1547.001",
        "command_line": cmd[:500] if cmd else content[:500],
        "hostname": event.get("Computer", event.get("DeviceName", event.get("hostname", "unknown"))),
        "user": event.get("User", event.get("AccountName", event.get("UserId", "unknown"))),
        "timestamp": event.get("_time", event.get("timestamp", event.get("UtcTime", event.get("Timestamp", "")))),
        "risk_score": risk,
        "risk_level": "CRITICAL" if risk >= 75 else "HIGH" if risk >= 50 else "MEDIUM" if risk >= 25 else "LOW",
        "indicators": indicators,
    }

def run_hunt(input_path, output_dir):
    print(f"[*] Registry Persistence Hunt - {datetime.datetime.now().isoformat()}")
    events = parse_logs(input_path)
    findings = [f for f in (analyze_event(e) for e in events) if f]
    Path(output_dir).mkdir(parents=True, exist_ok=True)
    slug = "hunting_for_registry"
    with open(Path(output_dir) / f"{slug}_findings.json", "w", encoding="utf-8") as f:
        json.dump({"hunt_id": f"TH-{datetime.date.today()}", "total_events": len(events), "findings": findings}, f, indent=2)
    with open(Path(output_dir) / "hunt_report.md", "w", encoding="utf-8") as f:
        f.write(f"# Registry 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["risk_score"], reverse=True)[:20]:
            f.write(f"### [{finding['risk_level']}] {finding['technique']}\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():
    p = argparse.ArgumentParser(description="Registry Persistence Detection")
    sp = p.add_subparsers(dest="cmd")
    h = sp.add_parser("hunt"); h.add_argument("--input", "-i", required=True); h.add_argument("--output", "-o", default="./hunting_for_reg_output")
    sp.add_parser("queries")
    args = p.parse_args()
    if args.cmd == "hunt": run_hunt(args.input, args.output)
    elif args.cmd == "queries":
        print("=== Detection Queries ===")
        print("See references/workflows.md for platform-specific queries")
    else: p.print_help()

if __name__ == "__main__": main()

Assets 1

template.mdtext/markdown · 2.6 KB
Keep exploring