malware analysis

Analyzing Malware Persistence with Autoruns

Use Sysinternals Autoruns to systematically identify and analyze malware persistence mechanisms across registry keys, scheduled tasks, services, drivers, and startup locations on Windows systems.

autorunsincident-responsemalware-analysispersistenceregistrystartupsysinternalswindows
Install this skill
npx skills add mukul975/Anthropic-Cybersecurity-Skills
Framework mappings

Overview

Sysinternals Autoruns extracts data from hundreds of Auto-Start Extensibility Points (ASEPs) on Windows, scanning 18+ categories including Run/RunOnce keys, services, scheduled tasks, drivers, Winlogon entries, LSA providers, print monitors, WMI subscriptions, and AppInit DLLs. Digital signature verification filters Microsoft-signed entries. The compare function identifies newly added persistence via baseline diffing. VirusTotal integration checks hash reputation. Offline analysis via -z flag enables forensic disk image examination.

When to Use

  • When investigating security incidents that require analyzing malware persistence with autoruns
  • When building detection rules or threat hunting queries for this domain
  • When SOC analysts need structured procedures for this analysis type
  • When validating security monitoring coverage for related attack techniques

Prerequisites

  • Sysinternals Autoruns (GUI) and Autorunsc (CLI)
  • Administrative privileges on target system
  • Python 3.9+ for automated analysis
  • VirusTotal API key for reputation checks
  • Clean baseline export for comparison

Workflow

Step 1: Automated Persistence Scanning

#!/usr/bin/env python3
"""Automate Autoruns-based persistence analysis."""
import subprocess
import csv
import json
import sys
 
 
def scan_and_analyze(autorunsc_path="autorunsc64.exe", csv_path="scan.csv"):
    cmd = [autorunsc_path, "-a", "*", "-c", "-h", "-s", "-nobanner", "*"]
    result = subprocess.run(cmd, capture_output=True, text=True, timeout=600)
    with open(csv_path, 'w') as f:
        f.write(result.stdout)
    return parse_and_flag(csv_path)
 
 
def parse_and_flag(csv_path):
    suspicious = []
    with open(csv_path, 'r', errors='replace') as f:
        for row in csv.DictReader(f):
            reasons = []
            signer = row.get("Signer", "")
            if not signer or signer == "(Not verified)":
                reasons.append("Unsigned binary")
            if not row.get("Description") and not row.get("Company"):
                reasons.append("Missing metadata")
            path = row.get("Image Path", "").lower()
            for sp in ["\temp\\", "\appdata\local\temp", "\users\public\\"]:
                if sp in path:
                    reasons.append(f"Suspicious path")
            launch = row.get("Launch String", "").lower()
            for kw in ["powershell", "cmd /c", "wscript", "mshta", "regsvr32"]:
                if kw in launch:
                    reasons.append(f"LOLBin: {kw}")
            if reasons:
                row["reasons"] = reasons
                suspicious.append(row)
    return suspicious
 
 
if __name__ == "__main__":
    if len(sys.argv) > 1:
        results = parse_and_flag(sys.argv[1])
        print(f"[!] {len(results)} suspicious entries")
        for r in results:
            print(f"  {r.get('Entry','')} - {r.get('Image Path','')}")
            for reason in r.get('reasons', []):
                print(f"    - {reason}")

Validation Criteria

  • All ASEP categories scanned and cataloged
  • Unsigned entries flagged for investigation
  • Suspicious paths and LOLBin launch strings highlighted
  • Baseline comparison identifies new persistence mechanisms

References

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: Autoruns Persistence Analysis

Autoruns CLI (autorunsc.exe)

autorunsc.exe -a * -c -h -s -v -vt -o autoruns.csv
Flag Description
-a * All autostart categories
-c CSV output
-h Show file hashes
-s Verify digital signatures
-v Verify signatures against catalog
-vt Check VirusTotal
-o Output file

CSV Columns

Column Description
Time Entry timestamp
Entry Location Registry key or path
Entry Entry name
Enabled enabled/disabled
Category Autoruns category
Description File description
Company Publisher name
Image Path Full binary path
Launch String Complete command line
MD5 / SHA-1 / SHA-256 File hashes
Signer Code signing status
VT detection VirusTotal ratio (e.g., "5/72")

Autostart Categories

Category Examples
Logon Run/RunOnce keys, Startup folder
Services Windows services
Drivers Kernel drivers
Scheduled Tasks Task Scheduler entries
Winlogon Shell, Userinit, Notify
WMI Event subscriptions
AppInit AppInit_DLLs
Boot Execute BootExecute values
Image Hijacks IFEO debugger entries
LSA Providers Authentication packages

Suspicious Indicators

Indicator Significance
VT detection > 0 Known malware
Unsigned binary Potential unsigned malware
LOLBin in launch string Living-off-the-land
Path in %TEMP% or %PUBLIC% Staging location
Missing company info Suspicious unsigned entry

MITRE ATT&CK Persistence

  • T1547.001 - Registry Run Keys / Startup Folder
  • T1053.005 - Scheduled Task
  • T1543.003 - Windows Service
  • T1546.003 - WMI Event Subscription
standards.md0.3 KB

Standards Reference - analyzing-malware-persistence-with-autoruns

Applicable Standards

  • MITRE ATT&CK Framework
  • NIST SP 800-83 Guide to Malware Incident Prevention
  • NIST SP 800-86 Guide to Integrating Forensic Techniques

Related MITRE ATT&CK Techniques

See SKILL.md for specific technique mappings.

workflows.md0.5 KB

Analysis Workflows - analyzing-malware-persistence-with-autoruns

Primary Workflow

[Sample Collection] --> [Static Analysis] --> [Dynamic Analysis] --> [IOC Extraction]
                                                                          |
                                                                          v
                                                                 [Report Generation]

See SKILL.md for detailed step-by-step procedures.

Scripts 1

agent.py5.7 KB
Display-only source. This catalog never executes bundled scripts.
#!/usr/bin/env python3
"""Autoruns Persistence Analysis Agent - Analyzes Windows autostart entries for malware persistence."""

import json
import csv
import re
import logging
import argparse
from datetime import datetime
from collections import Counter

logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s")
logger = logging.getLogger(__name__)

SUSPICIOUS_PATHS = [
    r"\\temp\\", r"\\tmp\\", r"\\appdata\\local\\temp",
    r"\\public\\", r"\\programdata\\", r"\\users\\default",
    r"\\recycler\\", r"\\windows\\debug",
]

SUSPICIOUS_COMMANDS = [
    "powershell", "cmd.exe /c", "wscript", "cscript", "mshta",
    "regsvr32", "rundll32", "certutil", "bitsadmin",
    "schtasks", "msiexec /q", "forfiles",
]

KNOWN_PERSISTENCE_LOCATIONS = [
    "HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run",
    "HKCU\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run",
    "HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce",
    "HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon",
    "HKLM\\SYSTEM\\CurrentControlSet\\Services",
    "Task Scheduler",
    "Startup Folder",
    "WMI",
]


def parse_autoruns_csv(csv_file):
    """Parse Autoruns CSV export file."""
    entries = []
    with open(csv_file, "r", encoding="utf-8-sig", errors="ignore") as f:
        reader = csv.DictReader(f, delimiter=",")
        for row in reader:
            entries.append({
                "time": row.get("Time", ""),
                "entry_location": row.get("Entry Location", ""),
                "entry": row.get("Entry", ""),
                "enabled": row.get("Enabled", ""),
                "category": row.get("Category", ""),
                "profile": row.get("Profile", ""),
                "description": row.get("Description", ""),
                "company": row.get("Company", ""),
                "image_path": row.get("Image Path", ""),
                "version": row.get("Version", ""),
                "launch_string": row.get("Launch String", ""),
                "md5": row.get("MD5", ""),
                "sha1": row.get("SHA-1", ""),
                "sha256": row.get("SHA-256", ""),
                "signer": row.get("Signer", ""),
                "vt_detection": row.get("VT detection", ""),
            })
    logger.info("Parsed %d autoruns entries from %s", len(entries), csv_file)
    return entries


def analyze_entry(entry):
    """Analyze a single autoruns entry for suspicious indicators."""
    findings = []
    image_path = (entry.get("image_path") or "").lower()
    launch_string = (entry.get("launch_string") or "").lower()
    signer = entry.get("signer") or ""
    vt = entry.get("vt_detection") or ""
    company = entry.get("company") or ""

    for pattern in SUSPICIOUS_PATHS:
        if re.search(pattern, image_path, re.IGNORECASE):
            findings.append({"type": "Suspicious file path", "severity": "high", "detail": image_path})
            break

    for cmd in SUSPICIOUS_COMMANDS:
        if cmd.lower() in launch_string:
            findings.append({"type": "LOLBin in launch string", "severity": "high", "detail": cmd})
            break

    if signer in ("(Not verified)", "") or "(Not verified)" in signer:
        findings.append({"type": "Unsigned binary", "severity": "medium", "detail": signer})

    if vt and "/" in vt:
        try:
            detections, total = vt.split("/")
            if int(detections.strip()) > 0:
                findings.append({"type": "VirusTotal detections", "severity": "critical", "detail": vt})
        except (ValueError, AttributeError):
            pass

    if not company and entry.get("enabled") == "enabled":
        findings.append({"type": "No company info", "severity": "low", "detail": "Enabled entry without publisher"})

    return findings


def analyze_all_entries(entries):
    """Analyze all autoruns entries and generate findings."""
    all_findings = []
    for entry in entries:
        entry_findings = analyze_entry(entry)
        if entry_findings:
            all_findings.append({
                "entry": entry.get("entry"),
                "location": entry.get("entry_location"),
                "category": entry.get("category"),
                "image_path": entry.get("image_path"),
                "findings": entry_findings,
                "max_severity": max((f["severity"] for f in entry_findings), key=lambda s: {"critical": 4, "high": 3, "medium": 2, "low": 1}.get(s, 0)),
            })
    return all_findings


def generate_report(entries, findings):
    """Generate persistence analysis report."""
    categories = Counter(e.get("category", "Unknown") for e in entries)
    critical = [f for f in findings if f["max_severity"] == "critical"]
    report = {
        "timestamp": datetime.utcnow().isoformat(),
        "total_entries": len(entries),
        "enabled_entries": len([e for e in entries if e.get("enabled") == "enabled"]),
        "suspicious_entries": len(findings),
        "critical_entries": len(critical),
        "category_breakdown": dict(categories.most_common()),
        "findings": findings,
    }
    print(f"AUTORUNS REPORT: {len(entries)} entries, {len(findings)} suspicious, {len(critical)} critical")
    return report


def main():
    parser = argparse.ArgumentParser(description="Autoruns Persistence Analysis Agent")
    parser.add_argument("--csv-file", required=True, help="Autoruns CSV export file")
    parser.add_argument("--output", default="autoruns_report.json")
    args = parser.parse_args()

    entries = parse_autoruns_csv(args.csv_file)
    findings = analyze_all_entries(entries)
    report = generate_report(entries, findings)
    with open(args.output, "w") as f:
        json.dump(report, f, indent=2)
    logger.info("Report saved to %s", args.output)


if __name__ == "__main__":
    main()

Assets 1

template.mdtext/markdown · 0.4 KB
Keep exploring