Asi performing-malware-persistence-investigation
Systematically investigate all persistence mechanisms on Windows and Linux systems to identify how malware survives reboots and maintains access.
git clone https://github.com/plurigrid/asi
T=$(mktemp -d) && git clone --depth=1 https://github.com/plurigrid/asi "$T" && mkdir -p ~/.claude/skills && cp -r "$T/plugins/asi/skills/performing-malware-persistence-investigation" ~/.claude/skills/plurigrid-asi-performing-malware-persistence-investigation && rm -rf "$T"
plugins/asi/skills/performing-malware-persistence-investigation/SKILL.mdPerforming Malware Persistence Investigation
When to Use
- When investigating how malware maintains presence on a compromised system after reboots
- During incident response to identify all persistence mechanisms for complete remediation
- For threat hunting to discover unauthorized autostart entries across endpoints
- When analyzing malware behavior to understand its persistence strategy
- For verifying that all persistence has been removed after incident remediation
Prerequisites
- Forensic image or live system access with administrative privileges
- Autoruns (Sysinternals) for Windows persistence enumeration
- RegRipper for offline registry analysis
- Understanding of Windows and Linux persistence mechanisms
- YARA rules for scanning persistence locations
- Baseline of known-good autorun entries for comparison
Workflow
Step 1: Enumerate Windows Registry Persistence
# Extract registry hives from forensic image mount -o ro,loop,offset=$((2048*512)) /cases/case-2024-001/images/evidence.dd /mnt/evidence # Key registry persistence locations python3 << 'PYEOF' from Registry import Registry import json results = {'registry_persistence': []} # SYSTEM hive analysis system_reg = Registry.Registry("/cases/case-2024-001/registry/SYSTEM") select = system_reg.open("Select") current = select.value("Current").value() cs = f"ControlSet{current:03d}" # Services (very common persistence) services = system_reg.open(f"{cs}\\Services") for svc in services.subkeys(): try: start_type = svc.value("Start").value() image_path = "" try: image_path = svc.value("ImagePath").value() except: pass # Start types: 0=Boot, 1=System, 2=Auto, 3=Manual, 4=Disabled if start_type in (0, 1, 2) and image_path: svc_type = svc.value("Type").value() if svc.values() else 0 results['registry_persistence'].append({ 'location': f'HKLM\\SYSTEM\\{cs}\\Services\\{svc.name()}', 'type': 'Service', 'value': image_path, 'start_type': start_type, 'timestamp': str(svc.timestamp()) }) except Exception: pass # SOFTWARE hive analysis sw_reg = Registry.Registry("/cases/case-2024-001/registry/SOFTWARE") # Machine Run keys run_keys = [ "Microsoft\\Windows\\CurrentVersion\\Run", "Microsoft\\Windows\\CurrentVersion\\RunOnce", "Microsoft\\Windows\\CurrentVersion\\RunServices", "Microsoft\\Windows\\CurrentVersion\\RunServicesOnce", "Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer\\Run", "Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Run", "Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\RunOnce", ] for key_path in run_keys: try: key = sw_reg.open(key_path) for value in key.values(): results['registry_persistence'].append({ 'location': f'HKLM\\SOFTWARE\\{key_path}', 'type': 'Run Key', 'name': value.name(), 'value': str(value.value()), 'timestamp': str(key.timestamp()) }) except Exception: pass # NTUSER.DAT analysis import glob for ntuser in glob.glob("/cases/case-2024-001/registry/NTUSER*.DAT"): try: user_reg = Registry.Registry(ntuser) user_run_keys = [ "Software\\Microsoft\\Windows\\CurrentVersion\\Run", "Software\\Microsoft\\Windows\\CurrentVersion\\RunOnce", "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders\\Startup", ] for key_path in user_run_keys: try: key = user_reg.open(key_path) for value in key.values(): results['registry_persistence'].append({ 'location': f'HKCU\\{key_path}', 'type': 'User Run Key', 'name': value.name(), 'value': str(value.value()), 'timestamp': str(key.timestamp()), 'hive': ntuser }) except Exception: pass except Exception: pass print(f"Total registry persistence entries: {len(results['registry_persistence'])}") for entry in results['registry_persistence']: print(f" [{entry['type']}] {entry.get('name', '')} -> {entry.get('value', '')[:100]}") with open('/cases/case-2024-001/analysis/registry_persistence.json', 'w') as f: json.dump(results, f, indent=2) PYEOF
Step 2: Check Scheduled Tasks and WMI Persistence
# Extract scheduled tasks from forensic image mkdir -p /cases/case-2024-001/persistence/tasks/ cp -r /mnt/evidence/Windows/System32/Tasks/* /cases/case-2024-001/persistence/tasks/ 2>/dev/null # Parse scheduled task XML files python3 << 'PYEOF' import os, xml.etree.ElementTree as ET tasks_dir = '/cases/case-2024-001/persistence/tasks/' suspicious_tasks = [] for root_dir, dirs, files in os.walk(tasks_dir): for fname in files: fpath = os.path.join(root_dir, fname) try: tree = ET.parse(fpath) root = tree.getroot() ns = {'t': 'http://schemas.microsoft.com/windows/2004/02/mit/task'} actions = root.findall('.//t:Exec', ns) for action in actions: command = action.find('t:Command', ns) args = action.find('t:Arguments', ns) cmd_text = command.text if command is not None else '' args_text = args.text if args is not None else '' # Flag suspicious commands suspicious_indicators = [ 'powershell', 'cmd.exe', 'wscript', 'cscript', 'mshta', 'regsvr32', 'rundll32', 'certutil', 'bitsadmin', '/c ', '-enc', '-e ', 'hidden', 'bypass', 'downloadstring', 'invoke-', 'iex', '/tmp/', 'appdata', 'programdata', 'temp\\', '.ps1', '.vbs', '.hta', 'base64' ] is_suspicious = any(s in (cmd_text + ' ' + args_text).lower() for s in suspicious_indicators) task_info = { 'name': fname, 'path': fpath.replace(tasks_dir, ''), 'command': cmd_text, 'arguments': args_text, 'suspicious': is_suspicious } if is_suspicious: suspicious_tasks.append(task_info) print(f"SUSPICIOUS TASK: {fname}") print(f" Command: {cmd_text}") print(f" Arguments: {args_text}") print() except Exception as e: pass print(f"\nTotal suspicious scheduled tasks: {len(suspicious_tasks)}") PYEOF # Check WMI event subscriptions (common APT persistence) # WMI repository: C:\Windows\System32\wbem\Repository\ cp -r /mnt/evidence/Windows/System32/wbem/Repository/ /cases/case-2024-001/persistence/wmi/ 2>/dev/null # Parse WMI persistence using PyWMIPersistenceFinder python3 << 'PYEOF' import os, re # Search WMI OBJECTS.DATA for event subscriptions wmi_db = '/cases/case-2024-001/persistence/wmi/OBJECTS.DATA' if os.path.exists(wmi_db): with open(wmi_db, 'rb') as f: data = f.read() # Search for EventFilter strings filters = re.findall(b'__EventFilter.*?(?=\x00\x00)', data) consumers = re.findall(b'CommandLineEventConsumer.*?(?=\x00\x00)', data) bindings = re.findall(b'__FilterToConsumerBinding.*?(?=\x00\x00)', data) print("=== WMI PERSISTENCE ===") print(f"Event Filters: {len(filters)}") print(f"Command Consumers: {len(consumers)}") print(f"Filter-Consumer Bindings: {len(bindings)}") for consumer in consumers: decoded = consumer.decode('utf-8', errors='ignore') print(f" Consumer: {decoded[:200]}") else: print("WMI repository not found") PYEOF
Step 3: Check File System and Boot Persistence
# Startup folders echo "=== STARTUP FOLDER CONTENTS ===" > /cases/case-2024-001/analysis/startup_items.txt ls -la "/mnt/evidence/ProgramData/Microsoft/Windows/Start Menu/Programs/Startup/" \ >> /cases/case-2024-001/analysis/startup_items.txt 2>/dev/null for userdir in /mnt/evidence/Users/*/; do username=$(basename "$userdir") echo "--- User: $username ---" >> /cases/case-2024-001/analysis/startup_items.txt ls -la "$userdir/AppData/Roaming/Microsoft/Windows/Start Menu/Programs/Startup/" \ >> /cases/case-2024-001/analysis/startup_items.txt 2>/dev/null done # Check DLL search order hijacking locations echo "=== DLL HIJACKING CHECK ===" > /cases/case-2024-001/analysis/dll_hijack.txt # Check for DLLs in application directories that should only be in System32 find /mnt/evidence/Program\ Files/ /mnt/evidence/Program\ Files\ \(x86\)/ \ -name "*.dll" -newer /mnt/evidence/Windows/System32/ntdll.dll 2>/dev/null \ >> /cases/case-2024-001/analysis/dll_hijack.txt # Check for COM object hijacking python3 << 'PYEOF' from Registry import Registry reg = Registry.Registry("/cases/case-2024-001/registry/SOFTWARE") # Check for suspicious CLSID entries try: clsid = reg.open("Classes\\CLSID") for key in clsid.subkeys(): try: server = key.subkey("InprocServer32") dll_path = server.value("(default)").value() if any(s in dll_path.lower() for s in ['temp', 'appdata', 'programdata', 'downloads', 'tmp']): print(f"SUSPICIOUS COM: {key.name()} -> {dll_path}") except: pass except: pass PYEOF # Check boot configuration for bootkits # BCD (Boot Configuration Data) ls -la /mnt/evidence/Boot/BCD 2>/dev/null # Check for modified bootmgr or winload.exe sha256sum /mnt/evidence/Windows/System32/winload.exe 2>/dev/null
Step 4: Check Linux Persistence Mechanisms
# If analyzing a Linux system LINUX_ROOT="/mnt/evidence" echo "=== LINUX PERSISTENCE CHECK ===" > /cases/case-2024-001/analysis/linux_persistence.txt # Cron jobs echo "--- Cron Jobs ---" >> /cases/case-2024-001/analysis/linux_persistence.txt cat $LINUX_ROOT/etc/crontab >> /cases/case-2024-001/analysis/linux_persistence.txt 2>/dev/null ls -la $LINUX_ROOT/etc/cron.d/ >> /cases/case-2024-001/analysis/linux_persistence.txt 2>/dev/null cat $LINUX_ROOT/etc/cron.d/* >> /cases/case-2024-001/analysis/linux_persistence.txt 2>/dev/null cat $LINUX_ROOT/var/spool/cron/crontabs/* >> /cases/case-2024-001/analysis/linux_persistence.txt 2>/dev/null # Systemd services echo "--- Custom Systemd Services ---" >> /cases/case-2024-001/analysis/linux_persistence.txt find $LINUX_ROOT/etc/systemd/system/ -name "*.service" -not -type l \ >> /cases/case-2024-001/analysis/linux_persistence.txt 2>/dev/null # SSH authorized keys echo "--- SSH Authorized Keys ---" >> /cases/case-2024-001/analysis/linux_persistence.txt find $LINUX_ROOT/home/ $LINUX_ROOT/root/ -name "authorized_keys" -exec cat {} \; \ >> /cases/case-2024-001/analysis/linux_persistence.txt 2>/dev/null # Init scripts and rc.local echo "--- RC Scripts ---" >> /cases/case-2024-001/analysis/linux_persistence.txt cat $LINUX_ROOT/etc/rc.local >> /cases/case-2024-001/analysis/linux_persistence.txt 2>/dev/null # Shell profile scripts echo "--- Profile Scripts ---" >> /cases/case-2024-001/analysis/linux_persistence.txt cat $LINUX_ROOT/etc/profile.d/*.sh >> /cases/case-2024-001/analysis/linux_persistence.txt 2>/dev/null # LD_PRELOAD echo "--- LD_PRELOAD ---" >> /cases/case-2024-001/analysis/linux_persistence.txt cat $LINUX_ROOT/etc/ld.so.preload >> /cases/case-2024-001/analysis/linux_persistence.txt 2>/dev/null grep -r "LD_PRELOAD" $LINUX_ROOT/etc/ >> /cases/case-2024-001/analysis/linux_persistence.txt 2>/dev/null # Kernel modules echo "--- Loaded Kernel Modules ---" >> /cases/case-2024-001/analysis/linux_persistence.txt cat $LINUX_ROOT/etc/modules-load.d/*.conf >> /cases/case-2024-001/analysis/linux_persistence.txt 2>/dev/null # PAM backdoors echo "--- PAM Configuration ---" >> /cases/case-2024-001/analysis/linux_persistence.txt find $LINUX_ROOT/etc/pam.d/ -exec grep -l "pam_exec\|pam_script" {} \; \ >> /cases/case-2024-001/analysis/linux_persistence.txt 2>/dev/null
Step 5: Compile Persistence Report
# Generate comprehensive persistence report python3 << 'PYEOF' import json with open('/cases/case-2024-001/analysis/registry_persistence.json') as f: reg_data = json.load(f) report = """ MALWARE PERSISTENCE INVESTIGATION REPORT ========================================== PERSISTENCE MECHANISMS FOUND: 1. REGISTRY RUN KEYS: """ run_keys = [e for e in reg_data['registry_persistence'] if 'Run' in e.get('type', '')] for entry in run_keys: report += f" [{entry['timestamp']}] {entry.get('name', 'N/A')} -> {entry.get('value', '')[:100]}\n" services = [e for e in reg_data['registry_persistence'] if e.get('type') == 'Service'] report += f"\n2. SERVICES ({len(services)} auto-start services):\n" for entry in services[:20]: report += f" {entry['location'].split('\\')[-1]}: {entry['value'][:100]}\n" report += """ 3. SCHEDULED TASKS: [See scheduled_tasks analysis] 4. WMI SUBSCRIPTIONS: [See WMI analysis] 5. STARTUP FOLDER: [See startup_items.txt] 6. COM HIJACKING: [See COM analysis] SUSPICIOUS ENTRIES REQUIRING INVESTIGATION: """ # Flag suspicious entries for entry in reg_data['registry_persistence']: value = str(entry.get('value', '')).lower() suspicious_indicators = ['powershell', 'cmd /c', 'wscript', 'certutil', 'programdata', 'appdata\\local\\temp', 'base64', '.ps1', '.vbs', '.hta', '/tmp/', 'hidden'] if any(s in value for s in suspicious_indicators): report += f" SUSPICIOUS: {entry.get('name', 'N/A')} -> {entry.get('value', '')[:100]}\n" with open('/cases/case-2024-001/analysis/persistence_report.txt', 'w') as f: f.write(report) print(report) PYEOF
Key Concepts
| Concept | Description |
|---|---|
| Run keys | Registry keys executing programs at user logon (HKLM and HKCU) |
| Scheduled tasks | Windows Task Scheduler entries that execute on triggers (time, event, logon) |
| WMI event subscriptions | Persistent WMI queries that trigger actions (stealthy persistence) |
| COM hijacking | Redirecting COM object loading to execute malicious DLLs |
| DLL search order hijacking | Placing malicious DLLs in directories searched before System32 |
| Service persistence | Installing Windows services that auto-start with the system |
| Boot-level persistence | Modifying boot configuration or MBR/VBR for pre-OS execution |
| Living-off-the-land | Using legitimate system tools (PowerShell, WMI, certutil) for persistence |
Tools & Systems
| Tool | Purpose |
|---|---|
| Autoruns | Sysinternals comprehensive autostart enumeration tool |
| RegRipper | Automated registry persistence artifact extraction |
| KAPE | Automated persistence artifact collection and analysis |
| Velociraptor | Endpoint agent with persistence hunting artifacts |
| OSQuery | SQL-based system querying for persistence enumeration |
| PersistenceSniper | PowerShell tool for Windows persistence detection |
| RECmd | Eric Zimmerman registry command-line analysis tool |
| Volatility | Memory forensics for in-memory only persistence |
Common Scenarios
Scenario 1: APT Persistence After Initial Compromise Check all registry Run keys, enumerate scheduled tasks for encoded PowerShell commands, examine WMI event subscriptions for event-triggered execution, check COM object registrations for hijacked CLSIDs, review services for recently installed entries with suspicious image paths.
Scenario 2: Ransomware Pre-Encryption Persistence Identify how the ransomware maintains access for re-encryption or monitoring, check for scheduled tasks that would re-launch encryption, examine services installed by the ransomware operator, verify no additional backdoor persistence exists before declaring remediation complete.
Scenario 3: Fileless Malware Persistence Focus on registry-based persistence storing payload in registry values, check WMI subscriptions executing PowerShell from event triggers, examine scheduled tasks using encoded command arguments, check for mshta/rundll32 based persistence loading remote content.
Scenario 4: Post-Remediation Verification Run Autoruns comparison against known-good baseline, verify all identified persistence mechanisms have been removed, check for additional persistence that may have been missed, confirm services, tasks, and registry entries are clean, monitor for re-infection indicators.
Output Format
Persistence Investigation Summary: System: DESKTOP-ABC123 (Windows 10 Pro) Analysis Date: 2024-01-20 Persistence Mechanisms Found: Registry Run Keys (HKLM): 5 entries (1 SUSPICIOUS) Registry Run Keys (HKCU): 3 entries (1 SUSPICIOUS) Services (Auto-Start): 142 entries (2 SUSPICIOUS) Scheduled Tasks: 67 entries (3 SUSPICIOUS) WMI Subscriptions: 1 entry (SUSPICIOUS) Startup Folder: 4 items (1 SUSPICIOUS) COM Objects: 0 hijacked entries DLL Hijacking: 0 detected Suspicious Entries: 1. HKCU\Run\WindowsUpdate -> powershell -ep bypass -e <base64> Timestamp: 2024-01-15 14:35:00 Action: Encoded PowerShell download cradle 2. Service: WinDefenderUpdate -> C:\ProgramData\svc\update.exe Timestamp: 2024-01-15 14:40:00 Action: Unknown executable in ProgramData 3. Task: \Microsoft\Windows\Maintenance\SecurityUpdate Command: cmd.exe /c powershell -w hidden -e <base64> Trigger: On system startup 4. WMI: __EventFilter "ProcessStart" -> CommandLineEventConsumer Action: Execute C:\Windows\Temp\svc.exe on WMI event Remediation Required: 4 persistence mechanisms to remove Report: /cases/case-2024-001/analysis/persistence_report.txt