#!/usr/bin/env python3 """Fileless malware detection agent using Windows event logs and Volatility.""" import json import os import re import subprocess import sys from datetime import datetime try: import Evtx.Evtx as evtx HAS_EVTX = True except ImportError: HAS_EVTX = False LOLBINS = { "mshta.exe": {"risk": "HIGH", "usage": "Execute HTA with embedded VBScript/JScript"}, "regsvr32.exe": {"risk": "HIGH", "usage": "Proxy execution via COM scriptlets"}, "rundll32.exe": {"risk": "HIGH", "usage": "Execute DLL exports or JavaScript"}, "certutil.exe": {"risk": "HIGH", "usage": "Download files, decode base64 payloads"}, "bitsadmin.exe": {"risk": "MEDIUM", "usage": "Download files via BITS service"}, "wmic.exe": {"risk": "HIGH", "usage": "Remote execution, XSL script processing"}, "cmstp.exe": {"risk": "HIGH", "usage": "UAC bypass, COM object registration"}, "msbuild.exe": {"risk": "HIGH", "usage": "Execute inline C# tasks from XML"}, "installutil.exe": {"risk": "MEDIUM", "usage": "Execute .NET assemblies"}, "regasm.exe": {"risk": "MEDIUM", "usage": "Execute .NET COM assemblies"}, "powershell.exe": {"risk": "CONTEXT", "usage": "Script execution, download cradle"}, "cmd.exe": {"risk": "CONTEXT", "usage": "Command execution, script chaining"}, "wscript.exe": {"risk": "MEDIUM", "usage": "Execute VBScript/JScript files"}, "cscript.exe": {"risk": "MEDIUM", "usage": "Execute VBScript/JScript files"}, } SUSPICIOUS_PS_PATTERNS = [ (r'-enc\s', "Encoded command execution"), (r'IEX\s*\(', "Invoke-Expression (download cradle)"), (r'Invoke-Expression', "Invoke-Expression"), (r'Net\.WebClient', "WebClient download"), (r'DownloadString\(', "Remote script download"), (r'DownloadFile\(', "File download"), (r'FromBase64String', "Base64 decoding"), (r'Reflection\.Assembly', ".NET reflection loading"), (r'\[System\.Convert\]', "Type conversion (possible decode)"), (r'New-Object\s+IO\.MemoryStream', "In-memory stream (reflective load)"), (r'VirtualAlloc', "Memory allocation (shellcode)"), (r'CreateThread', "Thread creation (injection)"), (r'Add-MpPreference.*ExclusionPath', "Defender exclusion modification"), (r'Set-MpPreference.*DisableRealtimeMonitoring', "Defender disablement"), ] def scan_powershell_logs(log_dir=None): """Scan PowerShell script block logs for suspicious patterns.""" if not log_dir: log_dir = r"C:\Windows\System32\winevt\Logs" ps_log = os.path.join(log_dir, "Microsoft-Windows-PowerShell%4Operational.evtx") if not os.path.exists(ps_log) or not HAS_EVTX: return {"error": "PowerShell log not found or python-evtx not installed"} alerts = [] with evtx.Evtx(ps_log) as log: for record in log.records(): try: xml = record.xml() if "4104" not in xml: continue for pattern, desc in SUSPICIOUS_PS_PATTERNS: if re.search(pattern, xml, re.IGNORECASE): alerts.append({ "event_id": 4104, "timestamp": record.timestamp().isoformat(), "detection": desc, "snippet": xml[:500], }) break except Exception: continue return {"log_file": ps_log, "suspicious_events": len(alerts), "alerts": alerts[:50]} def scan_sysmon_for_lolbins(log_dir=None): """Scan Sysmon logs for LOLBin process creation events.""" if not log_dir: log_dir = r"C:\Windows\System32\winevt\Logs" sysmon_log = os.path.join(log_dir, "Microsoft-Windows-Sysmon%4Operational.evtx") if not os.path.exists(sysmon_log) or not HAS_EVTX: return {"error": "Sysmon log not found or python-evtx not installed"} detections = [] with evtx.Evtx(sysmon_log) as log: for record in log.records(): try: xml = record.xml() if "1" not in xml: continue for lolbin, info in LOLBINS.items(): if lolbin.lower() in xml.lower(): detections.append({ "timestamp": record.timestamp().isoformat(), "lolbin": lolbin, "risk": info["risk"], "known_abuse": info["usage"], "snippet": xml[:500], }) break except Exception: continue return {"log_file": sysmon_log, "lolbin_detections": len(detections), "detections": detections[:50]} def scan_wmi_persistence(): """Detect WMI event subscription persistence mechanisms.""" cmd = [ "powershell", "-Command", "Get-WMIObject -Namespace root\\Subscription -Class __EventFilter | " "Select-Object Name, Query | ConvertTo-Json" ] try: result = subprocess.run(cmd, capture_output=True, text=True, timeout=15) if result.returncode == 0 and result.stdout.strip(): filters = json.loads(result.stdout) if not isinstance(filters, list): filters = [filters] return {"wmi_event_filters": filters, "count": len(filters)} return {"wmi_event_filters": [], "count": 0} except Exception as e: return {"error": str(e)} def scan_registry_run_keys(): """Check registry Run keys for suspicious persistence entries.""" keys_to_check = [ r"HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Run", r"HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Run", r"HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce", ] results = [] for key in keys_to_check: cmd = ["reg", "query", key] try: r = subprocess.run(cmd, capture_output=True, text=True, timeout=10) if r.returncode == 0: for line in r.stdout.strip().splitlines(): line = line.strip() if line and not line.startswith("HKEY"): for lolbin in LOLBINS: if lolbin.lower() in line.lower(): results.append({ "key": key, "entry": line, "lolbin_detected": lolbin, "risk": "HIGH", }) except Exception: continue return {"registry_persistence": results, "count": len(results)} def run_volatility_malfind(memory_dump): """Run Volatility malfind to detect injected code in memory.""" if not os.path.exists(memory_dump): return {"error": f"Memory dump not found: {memory_dump}"} cmd = ["vol3", "-f", memory_dump, "windows.malfind"] try: result = subprocess.run(cmd, capture_output=True, text=True, timeout=300) return {"output": result.stdout.strip(), "exit_code": result.returncode} except FileNotFoundError: return {"error": "Volatility 3 (vol3) not installed"} except subprocess.TimeoutExpired: return {"error": "Volatility analysis timed out"} def generate_report(): """Generate fileless malware detection report.""" return { "timestamp": datetime.utcnow().isoformat() + "Z", "powershell_scan": scan_powershell_logs(), "lolbin_scan": scan_sysmon_for_lolbins(), "wmi_persistence": scan_wmi_persistence(), "registry_persistence": scan_registry_run_keys(), } if __name__ == "__main__": action = sys.argv[1] if len(sys.argv) > 1 else "report" if action == "report": print(json.dumps(generate_report(), indent=2, default=str)) elif action == "powershell": log_dir = sys.argv[2] if len(sys.argv) > 2 else None print(json.dumps(scan_powershell_logs(log_dir), indent=2, default=str)) elif action == "lolbins": print(json.dumps(scan_sysmon_for_lolbins(), indent=2, default=str)) elif action == "wmi": print(json.dumps(scan_wmi_persistence(), indent=2)) elif action == "registry": print(json.dumps(scan_registry_run_keys(), indent=2)) elif action == "malfind" and len(sys.argv) > 2: print(json.dumps(run_volatility_malfind(sys.argv[2]), indent=2)) else: print("Usage: agent.py [report|powershell [log_dir]|lolbins|wmi|registry|malfind ]")