incident response

Eradicating Malware from Infected Systems

Systematically remove malware, backdoors, and attacker persistence mechanisms from infected systems while ensuring complete eradication and preventing re-infection.

dfireradicationincident-responsemalware-removalpersistence
Install this skill
npx skills add mukul975/Anthropic-Cybersecurity-Skills
Framework mappings

When to Use

  • Malware infection confirmed and containment is in place
  • Forensic investigation has identified all persistence mechanisms
  • All compromised systems have been identified and scoped
  • Ready to remove attacker artifacts and restore clean state
  • Post-containment phase requires systematic cleanup

Prerequisites

  • Completed forensic analysis identifying all malware artifacts
  • List of all compromised systems and accounts
  • EDR/AV with updated signatures deployed
  • YARA rules for the specific malware family
  • Clean system images or verified backups for restoration
  • Network isolation still in effect during eradication

Workflow

Step 1: Map All Persistence Mechanisms

# Windows - Check all known persistence locations
# Autoruns (Sysinternals) - comprehensive autostart enumeration
autorunsc.exe -accepteula -a * -c -h -s -v > autoruns_report.csv
 
# Registry Run keys
reg query "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Run" /s
reg query "HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Run" /s
reg query "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce" /s
reg query "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Explorer\Run" /s
 
# Scheduled tasks
schtasks /query /fo CSV /v > schtasks_all.csv
 
# WMI event subscriptions
Get-WMIObject -Namespace root\Subscription -Class __EventFilter
Get-WMIObject -Namespace root\Subscription -Class CommandLineEventConsumer
Get-WMIObject -Namespace root\Subscription -Class __FilterToConsumerBinding
 
# Services
Get-Service | Where-Object {$_.Status -eq 'Running'} | Select-Object Name, DisplayName, BinaryPathName
 
# Linux persistence
cat /etc/crontab
ls -la /etc/cron.*/
ls -la /etc/init.d/
systemctl list-unit-files --type=service | grep enabled
cat /etc/rc.local
ls -la ~/.bashrc ~/.profile ~/.bash_profile

Step 2: Identify All Malware Artifacts

# Scan with YARA rules specific to the malware family
yara -r -s malware_rules/specific_family.yar C:\ 2>/dev/null
 
# Scan with multiple AV engines
# ClamAV scan
clamscan -r --infected --remove=no /mnt/infected_disk/
 
# Check for known malicious file hashes
find / -type f -newer /tmp/baseline_timestamp -exec sha256sum {} \; 2>/dev/null | \
  while read hash file; do
    grep -q "$hash" known_malicious_hashes.txt && echo "MALICIOUS: $file ($hash)"
  done
 
# Check for web shells
find /var/www/ -name "*.php" -newer /tmp/baseline -exec grep -l "eval\|base64_decode\|system\|passthru\|shell_exec" {} \;
 
# Check for unauthorized SSH keys
find / -name "authorized_keys" -exec cat {} \; 2>/dev/null

Step 3: Remove Malware Files and Artifacts

# Remove identified malicious files (after forensic imaging)
# Windows
Remove-Item -Path "C:\Windows\Temp\malware.exe" -Force
Remove-Item -Path "C:\Users\Public\backdoor.dll" -Force
 
# Remove malicious scheduled tasks
schtasks /delete /tn "MaliciousTaskName" /f
 
# Remove WMI persistence
Get-WMIObject -Namespace root\Subscription -Class __EventFilter -Filter "Name='MalFilter'" | Remove-WMIObject
Get-WMIObject -Namespace root\Subscription -Class CommandLineEventConsumer -Filter "Name='MalConsumer'" | Remove-WMIObject
 
# Remove malicious registry entries
reg delete "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Run" /v "MalEntry" /f
 
# Remove malicious services
sc stop "MalService" && sc delete "MalService"
 
# Linux - Remove malicious cron entries, binaries, SSH keys
crontab -r  # Remove entire crontab (or edit specific entries)
rm -f /tmp/.hidden_backdoor
sed -i '/malicious_key/d' ~/.ssh/authorized_keys
systemctl disable malicious-service && rm /etc/systemd/system/malicious-service.service

Step 4: Reset Compromised Credentials

# Reset all compromised user passwords
Import-Module ActiveDirectory
Get-ADUser -Filter * -SearchBase "OU=CompromisedUsers,DC=domain,DC=com" |
  Set-ADAccountPassword -Reset -NewPassword (ConvertTo-SecureString "TempP@ss!$(Get-Random)" -AsPlainText -Force)
 
# Reset KRBTGT password (twice, 12+ hours apart for Kerberos golden ticket attack)
Reset-KrbtgtPassword -DomainController DC01
# Wait 12+ hours, then reset again
Reset-KrbtgtPassword -DomainController DC01
 
# Rotate service account passwords
Get-ADServiceAccount -Filter * | ForEach-Object {
  Reset-ADServiceAccountPassword -Identity $_.Name
}
 
# Revoke all Azure AD tokens
Get-AzureADUser -All $true | ForEach-Object {
  Revoke-AzureADUserAllRefreshToken -ObjectId $_.ObjectId
}
 
# Rotate API keys and secrets
# Application-specific credential rotation

Step 5: Patch Vulnerability Used for Initial Access

# Identify and patch the entry point vulnerability
# Windows Update
Install-WindowsUpdate -KBArticleID "KB5001234" -AcceptAll -AutoReboot
 
# Linux patching
apt update && apt upgrade -y  # Debian/Ubuntu
yum update -y                 # RHEL/CentOS
 
# Application-specific patches
# Update web application frameworks, CMS, etc.
 
# Verify patch was applied
Get-HotFix -Id "KB5001234"

Step 6: Validate Eradication

# Full system scan with updated signatures
# CrowdStrike Falcon - On-demand scan
curl -X POST "https://api.crowdstrike.com/scanner/entities/scans/v1" \
  -H "Authorization: Bearer $FALCON_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"ids": ["device_id"]}'
 
# Verify no persistence mechanisms remain
autorunsc.exe -accepteula -a * -c -h -s -v | findstr /i "unknown verified"
 
# Check for any remaining suspicious processes
Get-Process | Where-Object {$_.Path -notlike "C:\Windows\*" -and $_.Path -notlike "C:\Program Files*"}
 
# Verify no unauthorized network connections
Get-NetTCPConnection -State Established |
  Where-Object {$_.RemoteAddress -notlike "10.*" -and $_.RemoteAddress -notlike "172.16.*"} |
  Select-Object LocalPort, RemoteAddress, RemotePort, OwningProcess
 
# Run YARA rules again to confirm no artifacts remain
yara -r malware_rules/specific_family.yar C:\ 2>/dev/null

Key Concepts

Concept Description
Persistence Mechanism Method attacker uses to maintain access across reboots
Root Cause Remediation Fixing the vulnerability that enabled initial compromise
Credential Rotation Resetting all potentially compromised passwords and tokens
KRBTGT Reset Invalidating Kerberos tickets after golden ticket attack
Indicator Sweep Scanning all systems for known malicious artifacts
Validation Scan Confirming eradication was successful before recovery
Re-imaging Rebuilding systems from clean images rather than cleaning

Tools & Systems

Tool Purpose
Sysinternals Autoruns Enumerate all Windows autostart locations
YARA Custom rule-based malware scanning
CrowdStrike/SentinelOne EDR-based scanning and remediation
ClamAV Open-source antivirus scanning
PowerShell Scripted cleanup and validation
Velociraptor Remote artifact collection and remediation

Common Scenarios

  1. RAT with Multiple Persistence: Remote access trojan using registry, scheduled task, and WMI subscription. Must remove all three persistence mechanisms.
  2. Web Shell on IIS/Apache: PHP/ASPX web shell in web root. Remove shell, audit all web files, patch application vulnerability.
  3. Rootkit Infection: Kernel-level rootkit that survives cleanup. Requires full re-image from known-good media.
  4. Fileless Malware: PowerShell-based attack living in memory and registry. Remove registry entries, clear WMI subscriptions, restart system.
  5. Active Directory Compromise: Attacker created backdoor accounts and golden tickets. Reset KRBTGT, remove rogue accounts, audit group memberships.

Output Format

  • Eradication action log with all removed artifacts
  • Credential rotation confirmation report
  • Vulnerability patching verification
  • Post-eradication validation scan results
  • Systems cleared for recovery phase
Source materials

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: Malware Eradication

Windows Process Termination

taskkill

taskkill /F /PID 1234           # Kill by PID
taskkill /F /IM malware.exe     # Kill by name
taskkill /F /T /PID 1234       # Kill process tree

PowerShell

Stop-Process -Id 1234 -Force
Get-Process -Name "malware" | Stop-Process -Force

Windows Persistence Cleanup

Registry Run Keys

reg delete "HKCU\Software\Microsoft\Windows\CurrentVersion\Run" /v MalwareName /f
reg delete "HKLM\Software\Microsoft\Windows\CurrentVersion\Run" /v MalwareName /f

Scheduled Tasks

schtasks /Delete /TN "MalwareTask" /F
schtasks /Query /FO CSV /V /NH

Services

sc stop MalwareService
sc delete MalwareService
sc query type= all state= all

Linux Persistence Cleanup

Crontab

crontab -l -u root         # List root cron
crontab -r -u root         # Remove all cron (use carefully)
ls -la /etc/cron.d/
ls -la /var/spool/cron/

Systemd Services

systemctl list-unit-files --type=service
systemctl disable malware.service
systemctl stop malware.service
rm /etc/systemd/system/malware.service
systemctl daemon-reload

Process Kill

kill -9 <pid>
pkill -f "malware_pattern"

File Quarantine Best Practices

Hash Before Move

sha256sum /path/to/malware > /quarantine/hash.txt

Secure Move

mv /path/to/malware /quarantine/sha256_filename.quarantine
chmod 000 /quarantine/sha256_filename.quarantine

Autoruns (Sysinternals)

Command Line

autorunsc.exe -a * -c -h -s -v -vt

Output Columns

Column Description
Entry Autorun name
Image Path Binary location
Signer Code signing info
VT Detection VirusTotal results

YARA Scanning for Remaining Artifacts

Command

yara -r rules.yar /target/directory

Rule Example

rule Malware_Remnant {
    strings:
        $s1 = "malware_mutex" ascii
        $s2 = {4D 5A 90 00}
    condition:
        any of them
}
standards.md2.8 KB

Standards and Framework References - Malware Eradication

NIST SP 800-61 Rev. 3 - Eradication Alignment

  • Respond (RS.MI-02): Incidents are eradicated
  • Eradication follows containment; remove all attacker artifacts
  • Must address root cause, not just symptoms
  • Verify eradication before moving to recovery

NIST SP 800-83 - Guide to Malware Incident Prevention and Handling

  • Section 4: Handling Malware Incidents
    • 4.3: Containment and Eradication
    • 4.3.1: Identification of infected hosts
    • 4.3.2: Containment options (quarantine, disconnect, block)
    • 4.3.3: Eradication and recovery options
  • Reference: https://csrc.nist.gov/pubs/sp/800/83/r1/final

SANS PICERL - Eradication Phase

  • Phase 4 of SANS incident handling process
  • Remove malware and attacker tools from all affected systems
  • Identify and remediate the root cause
  • Improve defenses based on attack methods used

MITRE ATT&CK - Persistence Techniques to Eradicate

Windows Persistence (T1547)

Sub-technique Location Eradication Method
T1547.001 Registry Run Keys Delete malicious registry values
T1547.004 Winlogon Helper DLL Restore legitimate DLL path
T1547.005 Security Support Provider Remove from registry
T1547.009 Shortcut Modification Restore original shortcuts

Scheduled Task/Job (T1053)

Sub-technique Location Eradication Method
T1053.005 Scheduled Task schtasks /delete
T1053.003 Cron Remove crontab entries
T1053.006 Systemd Timers Disable and remove timer units

Create/Modify System Process (T1543)

Sub-technique Location Eradication Method
T1543.003 Windows Service sc delete ServiceName
T1543.002 Systemd Service systemctl disable + rm unit file
T1543.001 Launch Agent Remove plist file

Other Persistence

Technique Description Eradication
T1546.003 WMI Event Subscription Remove filter, consumer, binding
T1546.015 COM Object Hijacking Restore CLSID registry values
T1098 Account Manipulation Remove backdoor accounts
T1136 Create Account Delete unauthorized accounts
T1556 Modify Authentication Restore authentication mechanisms
T1505.003 Web Shell Remove web shell files

CISA Malware Analysis Reports (MARs)

Microsoft DART Eradication Guidance

  • Recommended approach for Active Directory compromise recovery
  • KRBTGT password reset procedures
  • Tiered administration model implementation
  • Reference: Microsoft Incident Response playbooks
workflows.md3.8 KB

Malware Eradication - Detailed Workflow

Pre-Eradication Checklist

  • Forensic images collected from all compromised systems
  • All IOCs identified and documented
  • All persistence mechanisms mapped
  • Root cause (initial access vector) identified
  • Containment verified and holding
  • Eradication plan approved by Incident Commander
  • Rollback plan prepared in case eradication fails

Eradication Phases

Phase 1: Artifact Inventory

  1. Compile list of all malware files with paths and hashes
  2. Map all persistence mechanisms per system
  3. List all compromised accounts (user, service, admin)
  4. Identify all backdoor access methods
  5. Document network-level indicators (C2 IPs, domains)
  6. Note any configuration changes made by attacker

Phase 2: Coordinated Removal

Execute removal across ALL compromised systems simultaneously to prevent attacker from detecting cleanup on one system and acting on another.

Simultaneous Actions:

  1. Remove malware files from all systems
  2. Delete persistence mechanisms (registry, tasks, services, WMI)
  3. Disable compromised accounts
  4. Block all C2 infrastructure at network level
  5. Remove unauthorized SSH keys and certificates
  6. Clean up web shells from all web servers

Phase 3: Credential Reset

Priority Order:

  1. KRBTGT password (reset twice, 12+ hours apart)
  2. Domain admin accounts
  3. Service accounts
  4. All accounts that logged into compromised systems
  5. Application credentials and API keys
  6. Machine account passwords (if targeted)

Phase 4: Vulnerability Remediation

  1. Patch the vulnerability used for initial access
  2. Patch any additional vulnerabilities discovered during investigation
  3. Harden configurations that were exploited
  4. Update security tool signatures and rules
  5. Close unnecessary ports and services

Phase 5: Validation

  1. Full AV/EDR scan on all previously compromised systems
  2. YARA scan for specific malware family artifacts
  3. Check all persistence locations are clean
  4. Verify no unauthorized processes running
  5. Confirm no unauthorized network connections
  6. Validate all credentials were successfully rotated
  7. Test that patches are properly applied

Decision: Clean vs. Re-Image

When to Clean (In-Place Remediation)

  • Limited number of artifacts
  • Well-understood malware family
  • No rootkit or bootkit components
  • Time pressure requires faster recovery
  • System configuration is complex to rebuild

When to Re-Image (Full Rebuild)

  • Rootkit or bootkit detected
  • Kernel-level compromise
  • Domain controller compromise
  • Inability to confirm complete eradication
  • Simpler to rebuild than to clean
  • Legal requirements demand clean systems

Common Persistence Locations Reference

Windows

Registry:
  HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Run
  HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Run
  HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce
  HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\RunServices
  HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon (Shell, Userinit)
  HKLM\SYSTEM\CurrentControlSet\Services
  HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options
 
Filesystem:
  %AppData%\Microsoft\Windows\Start Menu\Programs\Startup
  C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Startup
  C:\Windows\System32\Tasks\
  C:\Windows\System32\drivers\
 
WMI:
  root\Subscription\__EventFilter
  root\Subscription\CommandLineEventConsumer
  root\Subscription\__FilterToConsumerBinding

Linux

Cron:
  /etc/crontab
  /etc/cron.d/*
  /etc/cron.daily/*
  /var/spool/cron/crontabs/*
 
Services:
  /etc/systemd/system/*.service
  /etc/init.d/*
  /etc/rc.local
 
Shell:
  ~/.bashrc, ~/.profile, ~/.bash_profile
  /etc/profile.d/*
  /etc/environment
 
SSH:
  ~/.ssh/authorized_keys
  /etc/ssh/sshd_config
 
Other:
  /etc/ld.so.preload
  Kernel modules: /lib/modules/

Scripts 2

agent.py7.4 KB
Display-only source. This catalog never executes bundled scripts.
#!/usr/bin/env python3
"""Agent for malware eradication from infected systems — process kill, file removal, persistence cleanup."""

import argparse
import json
import os
import subprocess
import sys
from datetime import datetime, timezone


PERSISTENCE_LOCATIONS_WINDOWS = [
    r"HKCU\Software\Microsoft\Windows\CurrentVersion\Run",
    r"HKLM\Software\Microsoft\Windows\CurrentVersion\Run",
    r"HKCU\Software\Microsoft\Windows\CurrentVersion\RunOnce",
    r"HKLM\Software\Microsoft\Windows\CurrentVersion\RunOnce",
    r"HKLM\SYSTEM\CurrentControlSet\Services",
]

PERSISTENCE_LOCATIONS_LINUX = [
    "/etc/cron.d", "/var/spool/cron", "/etc/crontab",
    "/etc/init.d", "/etc/systemd/system", "/usr/lib/systemd/system",
    "/etc/rc.local", "~/.bashrc", "~/.bash_profile",
]


def kill_malicious_process(pid):
    """Terminate a malicious process by PID."""
    if sys.platform == "win32":
        cmd = ["taskkill", "/F", "/PID", str(pid)]
    else:
        cmd = ["kill", "-9", str(pid)]
    try:
        subprocess.check_output(cmd, text=True, errors="replace", timeout=10)
        return {"pid": pid, "status": "killed"}
    except subprocess.SubprocessError as e:
        return {"pid": pid, "status": "failed", "error": str(e)}


def quarantine_file(file_path, quarantine_dir):
    """Move malicious file to quarantine directory with metadata."""
    os.makedirs(quarantine_dir, exist_ok=True)
    if not os.path.isfile(file_path):
        return {"file": file_path, "status": "not_found"}
    import hashlib
    with open(file_path, "rb") as f:
        sha256 = hashlib.sha256(f.read()).hexdigest()
    dest = os.path.join(quarantine_dir, f"{sha256}_{os.path.basename(file_path)}.quarantine")
    try:
        os.rename(file_path, dest)
        meta = {
            "original_path": file_path,
            "sha256": sha256,
            "quarantined_at": datetime.now(timezone.utc).isoformat(),
            "quarantine_path": dest,
        }
        with open(dest + ".json", "w") as f:
            json.dump(meta, f, indent=2)
        return {"file": file_path, "status": "quarantined", "sha256": sha256}
    except OSError as e:
        return {"file": file_path, "status": "failed", "error": str(e)}


def scan_persistence_windows():
    """Scan Windows persistence locations for suspicious entries."""
    findings = []
    for key in PERSISTENCE_LOCATIONS_WINDOWS:
        try:
            result = subprocess.check_output(
                ["reg", "query", key], text=True, errors="replace", timeout=5
            )
            for line in result.splitlines():
                line = line.strip()
                if "REG_SZ" in line or "REG_EXPAND_SZ" in line:
                    parts = line.split(None, 2)
                    if len(parts) >= 3:
                        value = parts[2]
                        if any(d in value.lower() for d in ["temp", "appdata\\local\\temp", "public"]):
                            findings.append({
                                "key": key,
                                "name": parts[0],
                                "value": value,
                                "suspicious": True,
                            })
        except subprocess.SubprocessError:
            pass
    return findings


def scan_persistence_linux():
    """Scan Linux persistence locations for suspicious entries."""
    findings = []
    for loc in PERSISTENCE_LOCATIONS_LINUX:
        expanded = os.path.expanduser(loc)
        if os.path.isdir(expanded):
            for f in os.listdir(expanded):
                fpath = os.path.join(expanded, f)
                if os.path.isfile(fpath):
                    try:
                        stat = os.stat(fpath)
                        if stat.st_mtime > (datetime.now().timestamp() - 86400 * 7):
                            findings.append({
                                "path": fpath,
                                "modified": datetime.fromtimestamp(stat.st_mtime).isoformat(),
                                "note": "Recently modified persistence file",
                            })
                    except OSError:
                        pass
        elif os.path.isfile(expanded):
            try:
                with open(expanded, "r", errors="replace") as fh:
                    content = fh.read()
                for line in content.splitlines():
                    if any(s in line.lower() for s in ["curl", "wget", "nc ", "ncat", "bash -i"]):
                        findings.append({
                            "file": expanded,
                            "line": line.strip()[:200],
                            "note": "Suspicious command in startup file",
                        })
            except PermissionError:
                pass
    return findings


def clean_scheduled_tasks():
    """List suspicious scheduled tasks."""
    findings = []
    if sys.platform == "win32":
        try:
            result = subprocess.check_output(
                ["schtasks", "/Query", "/FO", "CSV", "/NH", "/V"],
                text=True, errors="replace", timeout=15
            )
            for line in result.splitlines():
                parts = line.strip('"').split('","')
                if len(parts) >= 9:
                    action = parts[8] if len(parts) > 8 else ""
                    if any(s in action.lower() for s in ["powershell", "cmd", "temp", "appdata"]):
                        findings.append({
                            "task_name": parts[1] if len(parts) > 1 else "",
                            "action": action[:200],
                        })
        except subprocess.SubprocessError:
            pass
    return findings


def main():
    parser = argparse.ArgumentParser(
        description="Eradicate malware from infected systems"
    )
    parser.add_argument("--scan-persistence", action="store_true")
    parser.add_argument("--kill-pid", type=int, nargs="*", help="PIDs to terminate")
    parser.add_argument("--quarantine-file", nargs="*", help="Files to quarantine")
    parser.add_argument("--quarantine-dir", default=os.environ.get("QUARANTINE_DIR", "/tmp/quarantine"))
    parser.add_argument("--output", "-o", help="Output JSON report")
    args = parser.parse_args()

    print("[*] Malware Eradication Agent")
    report = {"timestamp": datetime.now(timezone.utc).isoformat(), "actions": []}

    if args.kill_pid:
        for pid in args.kill_pid:
            result = kill_malicious_process(pid)
            report["actions"].append({"action": "kill", **result})
            print(f"[*] Kill PID {pid}: {result['status']}")

    if args.quarantine_file:
        for fpath in args.quarantine_file:
            result = quarantine_file(fpath, args.quarantine_dir)
            report["actions"].append({"action": "quarantine", **result})
            print(f"[*] Quarantine {fpath}: {result['status']}")

    if args.scan_persistence:
        if sys.platform == "win32":
            pers = scan_persistence_windows()
            tasks = clean_scheduled_tasks()
        else:
            pers = scan_persistence_linux()
            tasks = []
        report["persistence"] = pers
        report["scheduled_tasks"] = tasks
        print(f"[*] Persistence entries: {len(pers)}, Suspicious tasks: {len(tasks)}")

    if args.output:
        with open(args.output, "w") as f:
            json.dump(report, f, indent=2)
        print(f"[*] Report saved to {args.output}")
    else:
        print(json.dumps(report, indent=2))


if __name__ == "__main__":
    main()
process.py18.0 KB
Display-only source. This catalog never executes bundled scripts.
#!/usr/bin/env python3
"""
Malware Eradication Automation Script

Scans systems for persistence mechanisms, removes identified malware
artifacts, and validates eradication success.

Requirements:
    pip install psutil yara-python
"""

import argparse
import csv
import hashlib
import json
import logging
import os
import platform
import subprocess
import sys
from datetime import datetime, timezone
from pathlib import Path

try:
    import psutil
except ImportError:
    print("Install psutil: pip install psutil")
    sys.exit(1)

logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s [%(levelname)s] %(message)s",
    handlers=[
        logging.StreamHandler(),
        logging.FileHandler(f"eradication_{datetime.now().strftime('%Y%m%d_%H%M%S')}.log"),
    ],
)
logger = logging.getLogger("malware_eradication")


class PersistenceScanner:
    """Scan for known persistence mechanisms on Windows and Linux."""

    def __init__(self):
        self.findings = []
        self.system = platform.system()

    def scan_all(self) -> list:
        """Run all persistence checks."""
        if self.system == "Windows":
            self._scan_registry_run_keys()
            self._scan_scheduled_tasks()
            self._scan_services()
            self._scan_startup_folders()
            self._scan_wmi_subscriptions()
        else:
            self._scan_crontabs()
            self._scan_systemd_services()
            self._scan_rc_local()
            self._scan_shell_profiles()
            self._scan_authorized_keys()
            self._scan_ld_preload()
        return self.findings

    def _scan_registry_run_keys(self):
        """Scan Windows registry Run keys."""
        run_keys = [
            r"HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Run",
            r"HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Run",
            r"HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce",
            r"HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce",
            r"HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Explorer\Run",
        ]
        for key in run_keys:
            try:
                result = subprocess.run(
                    ["reg", "query", key], capture_output=True, text=True, timeout=10,
                )
                if result.returncode == 0 and result.stdout.strip():
                    for line in result.stdout.strip().split("\n"):
                        line = line.strip()
                        if line and "REG_SZ" in line or "REG_EXPAND_SZ" in line:
                            self.findings.append({
                                "type": "registry_run_key",
                                "location": key,
                                "value": line,
                                "risk": "medium",
                            })
            except Exception as e:
                logger.warning(f"Failed to scan {key}: {e}")

    def _scan_scheduled_tasks(self):
        """Scan Windows scheduled tasks."""
        try:
            result = subprocess.run(
                ["schtasks", "/query", "/fo", "CSV", "/v"],
                capture_output=True, text=True, timeout=30,
            )
            if result.returncode == 0:
                lines = result.stdout.strip().split("\n")
                if len(lines) > 1:
                    reader = csv.DictReader(lines)
                    for row in reader:
                        task_name = row.get("TaskName", "")
                        task_run = row.get("Task To Run", "")
                        if task_name and task_run and "N/A" not in task_run:
                            # Flag tasks with suspicious indicators
                            suspicious = any(kw in task_run.lower() for kw in [
                                "powershell", "cmd /c", "mshta", "wscript",
                                "cscript", "certutil", "bitsadmin", "temp",
                                "appdata", "public",
                            ])
                            self.findings.append({
                                "type": "scheduled_task",
                                "location": task_name,
                                "value": task_run,
                                "risk": "high" if suspicious else "low",
                            })
        except Exception as e:
            logger.warning(f"Failed to scan scheduled tasks: {e}")

    def _scan_services(self):
        """Scan Windows services for suspicious entries."""
        try:
            for service in psutil.win_service_iter():
                try:
                    info = service.as_dict()
                    binpath = info.get("binpath", "")
                    if binpath and not any(
                        binpath.lower().startswith(p) for p in [
                            "c:\\windows\\", "c:\\program files\\",
                            "c:\\program files (x86)\\",
                        ]
                    ):
                        self.findings.append({
                            "type": "service",
                            "location": info.get("name", ""),
                            "value": binpath,
                            "risk": "medium",
                            "status": info.get("status", ""),
                        })
                except Exception:
                    continue
        except Exception as e:
            logger.warning(f"Failed to scan services: {e}")

    def _scan_startup_folders(self):
        """Scan Windows startup folders."""
        startup_paths = [
            os.path.expandvars(r"%APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup"),
            r"C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Startup",
        ]
        for path in startup_paths:
            if os.path.exists(path):
                for item in os.listdir(path):
                    full_path = os.path.join(path, item)
                    if os.path.isfile(full_path) and item != "desktop.ini":
                        self.findings.append({
                            "type": "startup_folder",
                            "location": path,
                            "value": full_path,
                            "risk": "medium",
                        })

    def _scan_wmi_subscriptions(self):
        """Scan for WMI event subscriptions (Windows)."""
        try:
            result = subprocess.run(
                ["powershell", "-Command",
                 "Get-WMIObject -Namespace root\\Subscription -Class __EventFilter | Select-Object Name, Query | ConvertTo-Json"],
                capture_output=True, text=True, timeout=15,
            )
            if result.stdout.strip():
                filters = json.loads(result.stdout)
                if isinstance(filters, dict):
                    filters = [filters]
                for f in filters:
                    self.findings.append({
                        "type": "wmi_subscription",
                        "location": "root\\Subscription\\__EventFilter",
                        "value": f.get("Name", "") + ": " + f.get("Query", ""),
                        "risk": "high",
                    })
        except Exception as e:
            logger.warning(f"Failed to scan WMI subscriptions: {e}")

    def _scan_crontabs(self):
        """Scan Linux crontab entries."""
        cron_paths = ["/etc/crontab"]
        cron_dirs = ["/etc/cron.d", "/etc/cron.daily", "/etc/cron.hourly",
                     "/etc/cron.weekly", "/etc/cron.monthly"]
        # User crontabs
        spool_dir = "/var/spool/cron/crontabs"
        if os.path.exists(spool_dir):
            for f in os.listdir(spool_dir):
                cron_paths.append(os.path.join(spool_dir, f))
        for d in cron_dirs:
            if os.path.exists(d):
                for f in os.listdir(d):
                    cron_paths.append(os.path.join(d, f))
        for path in cron_paths:
            if os.path.isfile(path):
                try:
                    with open(path) as f:
                        for line in f:
                            line = line.strip()
                            if line and not line.startswith("#"):
                                self.findings.append({
                                    "type": "crontab",
                                    "location": path,
                                    "value": line,
                                    "risk": "medium",
                                })
                except PermissionError:
                    pass

    def _scan_systemd_services(self):
        """Scan for custom systemd services."""
        service_dirs = ["/etc/systemd/system", "/usr/lib/systemd/system"]
        for sdir in service_dirs:
            if os.path.exists(sdir):
                for f in os.listdir(sdir):
                    if f.endswith(".service"):
                        fpath = os.path.join(sdir, f)
                        try:
                            with open(fpath) as sf:
                                content = sf.read()
                                if "ExecStart" in content:
                                    self.findings.append({
                                        "type": "systemd_service",
                                        "location": fpath,
                                        "value": f,
                                        "risk": "low",
                                    })
                        except PermissionError:
                            pass

    def _scan_rc_local(self):
        """Scan /etc/rc.local."""
        if os.path.exists("/etc/rc.local"):
            try:
                with open("/etc/rc.local") as f:
                    for line in f:
                        line = line.strip()
                        if line and not line.startswith("#") and line != "exit 0":
                            self.findings.append({
                                "type": "rc_local",
                                "location": "/etc/rc.local",
                                "value": line,
                                "risk": "high",
                            })
            except PermissionError:
                pass

    def _scan_shell_profiles(self):
        """Scan shell profile files for suspicious entries."""
        profile_files = [
            os.path.expanduser("~/.bashrc"),
            os.path.expanduser("~/.profile"),
            os.path.expanduser("~/.bash_profile"),
            "/etc/profile",
        ]
        suspicious_commands = ["curl", "wget", "nc ", "ncat", "python -c", "perl -e",
                               "base64", "eval", "/dev/tcp"]
        for pf in profile_files:
            if os.path.exists(pf):
                try:
                    with open(pf) as f:
                        for line in f:
                            line = line.strip()
                            if any(cmd in line.lower() for cmd in suspicious_commands):
                                self.findings.append({
                                    "type": "shell_profile",
                                    "location": pf,
                                    "value": line,
                                    "risk": "high",
                                })
                except PermissionError:
                    pass

    def _scan_authorized_keys(self):
        """Scan for unauthorized SSH keys."""
        home_dirs = ["/root"] + [
            os.path.join("/home", d) for d in os.listdir("/home")
            if os.path.isdir(os.path.join("/home", d))
        ] if os.path.exists("/home") else ["/root"]
        for hd in home_dirs:
            ak_path = os.path.join(hd, ".ssh", "authorized_keys")
            if os.path.exists(ak_path):
                try:
                    with open(ak_path) as f:
                        for line in f:
                            line = line.strip()
                            if line and not line.startswith("#"):
                                self.findings.append({
                                    "type": "authorized_keys",
                                    "location": ak_path,
                                    "value": line[:100] + "..." if len(line) > 100 else line,
                                    "risk": "medium",
                                })
                except PermissionError:
                    pass

    def _scan_ld_preload(self):
        """Scan for LD_PRELOAD hijacking."""
        if os.path.exists("/etc/ld.so.preload"):
            try:
                with open("/etc/ld.so.preload") as f:
                    content = f.read().strip()
                    if content:
                        self.findings.append({
                            "type": "ld_preload",
                            "location": "/etc/ld.so.preload",
                            "value": content,
                            "risk": "critical",
                        })
            except PermissionError:
                pass


class MalwareRemover:
    """Remove identified malware artifacts."""

    def __init__(self, dry_run: bool = True):
        self.dry_run = dry_run
        self.removal_log = []

    def remove_file(self, filepath: str) -> bool:
        if self.dry_run:
            logger.info(f"[DRY RUN] Would remove file: {filepath}")
            self.removal_log.append({"action": "remove_file", "target": filepath, "status": "dry_run"})
            return True
        try:
            if os.path.exists(filepath):
                file_hash = hashlib.sha256(open(filepath, "rb").read()).hexdigest()
                os.remove(filepath)
                self.removal_log.append({
                    "action": "remove_file", "target": filepath,
                    "status": "removed", "sha256": file_hash,
                })
                logger.info(f"Removed file: {filepath} (SHA256: {file_hash})")
                return True
        except Exception as e:
            self.removal_log.append({"action": "remove_file", "target": filepath, "status": f"failed: {e}"})
            logger.error(f"Failed to remove {filepath}: {e}")
        return False

    def remove_scheduled_task(self, task_name: str) -> bool:
        if self.dry_run:
            logger.info(f"[DRY RUN] Would remove scheduled task: {task_name}")
            self.removal_log.append({"action": "remove_task", "target": task_name, "status": "dry_run"})
            return True
        try:
            result = subprocess.run(
                ["schtasks", "/delete", "/tn", task_name, "/f"],
                capture_output=True, text=True, timeout=10,
            )
            status = "removed" if result.returncode == 0 else f"failed: {result.stderr}"
            self.removal_log.append({"action": "remove_task", "target": task_name, "status": status})
            return result.returncode == 0
        except Exception as e:
            self.removal_log.append({"action": "remove_task", "target": task_name, "status": f"failed: {e}"})
            return False

    def remove_registry_value(self, key: str, value_name: str) -> bool:
        if self.dry_run:
            logger.info(f"[DRY RUN] Would remove registry: {key}\\{value_name}")
            self.removal_log.append({"action": "remove_reg", "target": f"{key}\\{value_name}", "status": "dry_run"})
            return True
        try:
            result = subprocess.run(
                ["reg", "delete", key, "/v", value_name, "/f"],
                capture_output=True, text=True, timeout=10,
            )
            status = "removed" if result.returncode == 0 else f"failed: {result.stderr}"
            self.removal_log.append({"action": "remove_reg", "target": f"{key}\\{value_name}", "status": status})
            return result.returncode == 0
        except Exception as e:
            self.removal_log.append({"action": "remove_reg", "target": f"{key}\\{value_name}", "status": f"failed: {e}"})
            return False


def generate_report(findings: list, removal_log: list, output_path: str):
    """Generate eradication report."""
    report = {
        "timestamp": datetime.now(timezone.utc).isoformat(),
        "hostname": platform.node(),
        "platform": platform.platform(),
        "persistence_findings": {
            "total": len(findings),
            "by_risk": {},
            "by_type": {},
            "details": findings,
        },
        "eradication_actions": removal_log,
    }
    for f in findings:
        risk = f.get("risk", "unknown")
        ftype = f.get("type", "unknown")
        report["persistence_findings"]["by_risk"][risk] = report["persistence_findings"]["by_risk"].get(risk, 0) + 1
        report["persistence_findings"]["by_type"][ftype] = report["persistence_findings"]["by_type"].get(ftype, 0) + 1

    with open(output_path, "w") as fp:
        json.dump(report, fp, indent=2)
    logger.info(f"Report saved to: {output_path}")
    return report


def main():
    parser = argparse.ArgumentParser(description="Malware Eradication Tool")
    parser.add_argument("--scan", action="store_true", help="Scan for persistence mechanisms")
    parser.add_argument("--remove-files", nargs="*", help="Files to remove")
    parser.add_argument("--remove-tasks", nargs="*", help="Scheduled tasks to remove")
    parser.add_argument("--dry-run", action="store_true", default=True, help="Simulate removal (default)")
    parser.add_argument("--execute", action="store_true", help="Actually perform removal (CAUTION)")
    parser.add_argument("--output", default="./eradication_report.json", help="Report output path")

    args = parser.parse_args()
    dry_run = not args.execute

    findings = []
    if args.scan:
        scanner = PersistenceScanner()
        findings = scanner.scan_all()
        logger.info(f"Found {len(findings)} persistence items")
        for f in findings:
            risk_label = f"[{f['risk'].upper()}]"
            logger.info(f"  {risk_label} {f['type']}: {f['location']} -> {f['value'][:80]}")

    remover = MalwareRemover(dry_run=dry_run)
    if args.remove_files:
        for fp in args.remove_files:
            remover.remove_file(fp)
    if args.remove_tasks:
        for task in args.remove_tasks:
            remover.remove_scheduled_task(task)

    generate_report(findings, remover.removal_log, args.output)


if __name__ == "__main__":
    main()

Assets 1

template.mdtext/markdown · 1.8 KB
Keep exploring