diff --git a/psparent.py b/psparent.py new file mode 100644 index 0000000000..1e3e118334 --- /dev/null +++ b/psparent.py @@ -0,0 +1,841 @@ +import logging +from typing import List, Dict, Any, Set, Tuple, Optional +from volatility3.framework import interfaces, renderers, exceptions, constants +from volatility3.framework.configuration import requirements +from volatility3.framework.renderers import format_hints +from volatility3.plugins.windows import pslist, psscan, handles, dlllist +from volatility3.framework.objects import utility +from volatility3.framework.symbols import intermed +from volatility3.framework.symbols.windows import extensions + +vollog = logging.getLogger(__name__) + +class PSParent(interfaces.plugins.PluginInterface): + """ + Dynamic Parent-Child Process Relationship Validation + Uses built-in Windows behaviors to detect anomalies + """ + + _required_framework_version = (2, 0, 0) + _version = (2, 0, 0) + + @classmethod + def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]: + return [ + requirements.ModuleRequirement( + name="kernel", + description="Windows kernel module", + architectures=["Intel32", "Intel64"], + ), + requirements.BooleanRequirement( + name="verbose", + description="Show additional process details", + optional=True, + default=False + ), + requirements.BooleanRequirement( + name="debug", + description="Show debug information", + optional=True, + default=False + ), + requirements.IntRequirement( + name="pid", + description="Filter by specific Process ID", + optional=True, + default=None + ), + requirements.BooleanRequirement( + name="show-legitimate", + description="Show legitimate processes for comparison", + optional=True, + default=True + ) + ] + + def _generator(self): + kernel = self.context.modules[self.config["kernel"]] + verbose = self.config.get("verbose", False) + debug = self.config.get("debug", False) + target_pid = self.config.get("pid") + show_legitimate = self.config.get("show-legitimate", True) + + vollog.info("Starting dynamic parent-child process analysis...") + + # Build comprehensive process map using built-in data + processes = self._build_process_map(debug) + + if debug: + vollog.info(f"Analyzing {len(processes)} processes") + + # Analyze each process using dynamic Windows behavior rules + for pid, proc_info in processes.items(): + try: + if target_pid is not None and pid != target_pid: + continue + + # Get analysis results + analysis = self._analyze_process_dynamically(pid, proc_info, processes, debug) + + # Show legitimate processes if requested + if show_legitimate or analysis['status'] != 'LEGITIMATE': + yield self._format_output(proc_info, analysis, verbose) + + except Exception as e: + if debug: + vollog.error(f"Error analyzing PID {pid}: {e}") + continue + + def _build_process_map(self, debug: bool) -> Dict[int, Dict[str, Any]]: + """Build process map using built-in Windows data structures""" + processes = {} + + for proc in pslist.PsList.list_processes(self.context, self.config["kernel"]): + try: + pid = int(proc.UniqueProcessId) + ppid = int(proc.InheritedFromUniqueProcessId) + name = self._get_process_name(proc) + + processes[pid] = { + 'process': proc, + 'name': name, + 'ppid': ppid, + 'create_time': self._get_create_time(proc), + 'session_id': self._get_session_id(proc), + 'integrity': self._get_integrity_level(proc), + 'is_protected': self._is_protected_process(proc), + 'parent_name': self._get_parent_name(processes, ppid) if ppid in processes else "Unknown" + } + + except Exception as e: + if debug: + vollog.debug(f"Error building process map for PID {pid}: {e}") + continue + + return processes + + def _analyze_process_dynamically(self, pid: int, proc_info: Dict, processes: Dict, debug: bool) -> Dict[str, Any]: + """Dynamically analyze process using Windows behavior rules""" + name = proc_info['name'] + ppid = proc_info['ppid'] + + # Initialize analysis result + analysis = { + 'status': 'LEGITIMATE', + 'severity': 'INFO', + 'evidence': 'Normal parent-child relationship', + 'technique': 'Normal Execution', + 'confidence': 'HIGH' + } + + # First check if this is a known legitimate relationship + legitimate_check = self._is_known_legitimate_relationship(proc_info, processes) + if legitimate_check: + return legitimate_check + + # Rule 1: Check if parent process exists + if not self._is_valid_parent(ppid, processes): + analysis.update({ + 'status': 'SUSPICIOUS', + 'severity': 'HIGH', + 'evidence': f'Parent process (PID {ppid}) does not exist', + 'technique': 'Process Orphaning', + 'confidence': 'HIGH' + }) + return analysis + + parent_info = processes[ppid] + + # Rule 2: Check session consistency + if not self._is_session_consistent(proc_info, parent_info): + analysis.update({ + 'status': 'SUSPICIOUS', + 'severity': 'MEDIUM', + 'evidence': f'Session mismatch: Child in {proc_info["session_id"]}, Parent in {parent_info["session_id"]}', + 'technique': 'Cross-Session Injection', + 'confidence': 'MEDIUM' + }) + return analysis + + # Rule 3: Check integrity level inheritance + if not self._is_integrity_consistent(proc_info, parent_info): + analysis.update({ + 'status': 'SUSPICIOUS', + 'severity': 'HIGH', + 'evidence': 'Integrity level violation', + 'technique': 'Token Manipulation', + 'confidence': 'HIGH' + }) + return analysis + + # Rule 4: Check creation time consistency + if not self._is_time_consistent(proc_info, parent_info): + analysis.update({ + 'status': 'SUSPICIOUS', + 'severity': 'HIGH', + 'evidence': 'Child process created before parent', + 'technique': 'Process Tampering', + 'confidence': 'HIGH' + }) + return analysis + + # Rule 5: Check protected process violations + if not self._is_protection_consistent(proc_info, parent_info): + analysis.update({ + 'status': 'SUSPICIOUS', + 'severity': 'CRITICAL', + 'evidence': 'Protected process spawned by unprotected parent', + 'technique': 'Protected Process Bypass', + 'confidence': 'HIGH' + }) + return analysis + + # Rule 6: Check for system process anomalies + system_anomaly = self._check_system_process_anomaly(proc_info, parent_info) + if system_anomaly: + analysis.update(system_anomaly) + return analysis + + # Rule 7: Check for common malware patterns + malware_pattern = self._check_malware_patterns(proc_info, parent_info) + if malware_pattern: + analysis.update(malware_pattern) + return analysis + + return analysis + + def _is_known_legitimate_relationship(self, child_info: Dict, processes: Dict) -> Optional[Dict[str, Any]]: + """Check if this is a known legitimate Windows process relationship""" + child_name = child_info['name'].lower() + ppid = child_info['ppid'] + parent_name = child_info.get('parent_name', '').lower() + + # Comprehensive database of legitimate Windows process relationships + legitimate_relationships = { + # ===== CORE SYSTEM PROCESSES ===== + 'smss.exe': {'system', 'smss.exe'}, + 'csrss.exe': {'smss.exe'}, + 'wininit.exe': {'smss.exe'}, + 'services.exe': {'wininit.exe'}, + 'lsass.exe': {'wininit.exe'}, + 'winlogon.exe': {'smss.exe'}, + 'system': {'system'}, + + # ===== SESSION MANAGER SUBSYSTEM ===== + 'conhost.exe': {'csrss.exe', 'svchost.exe', 'explorer.exe', 'cmd.exe', 'powershell.exe'}, + 'dwm.exe': {'services.exe'}, + 'fontdrvhost.exe': {'services.exe'}, + + # ===== SERVICE HOST PROCESSES ===== + 'svchost.exe': {'services.exe', 'svchost.exe'}, + + # ===== WINDOWS LOGON PROCESSES ===== + 'userinit.exe': {'winlogon.exe'}, + 'explorer.exe': {'userinit.exe'}, + 'taskhost.exe': {'services.exe'}, + 'taskhostw.exe': {'services.exe'}, + 'runtimebroker.exe': {'svchost.exe'}, + 'applicationframehost.exe': {'svchost.exe'}, + 'sihost.exe': {'services.exe'}, + 'ctfmon.exe': {'services.exe'}, + 'textinputhost.exe': {'svchost.exe'}, + + # ===== WINDOWS SERVICES ===== + 'spoolsv.exe': {'services.exe'}, + 'taskeng.exe': {'services.exe'}, + 'msdtc.exe': {'services.exe'}, + 'lsm.exe': {'services.exe'}, + 'samss.exe': {'services.exe'}, + 'dns.exe': {'services.exe'}, + 'dhcp.exe': {'services.exe'}, + 'wmpnetwk.exe': {'services.exe'}, + 'sqlservr.exe': {'services.exe'}, + 'mysqld.exe': {'services.exe'}, + 'oracle.exe': {'services.exe'}, + 'java.exe': {'services.exe'}, + 'tomcat.exe': {'services.exe'}, + 'httpd.exe': {'services.exe'}, + 'nginx.exe': {'services.exe'}, + 'postgres.exe': {'services.exe'}, + 'mongod.exe': {'services.exe'}, + 'redis-server.exe': {'services.exe'}, + + # ===== SECURITY PROCESSES ===== + 'audiodg.exe': {'services.exe'}, + 'wisptis.exe': {'services.exe'}, + 'tabtip.exe': {'services.exe'}, + + # ===== WINDOWS APPLICATIONS ===== + 'notepad.exe': {'explorer.exe'}, + 'calc.exe': {'explorer.exe'}, + 'mspaint.exe': {'explorer.exe'}, + 'write.exe': {'explorer.exe'}, + 'charmap.exe': {'explorer.exe'}, + 'snippingtool.exe': {'explorer.exe'}, + 'stikynot.exe': {'explorer.exe'}, + 'wordpad.exe': {'explorer.exe'}, + + # ===== COMMAND LINE AND SHELL ===== + 'cmd.exe': {'explorer.exe', 'cmd.exe', 'svchost.exe', 'services.exe'}, + 'powershell.exe': {'explorer.exe', 'svchost.exe', 'services.exe', 'cmd.exe'}, + 'pwsh.exe': {'explorer.exe', 'svchost.exe', 'services.exe', 'cmd.exe'}, + 'wscript.exe': {'explorer.exe', 'svchost.exe', 'cmd.exe'}, + 'cscript.exe': {'explorer.exe', 'svchost.exe', 'cmd.exe'}, + 'mshta.exe': {'explorer.exe', 'svchost.exe'}, + + # ===== SYSTEM MANAGEMENT ===== + 'mmc.exe': {'explorer.exe'}, + 'regedit.exe': {'explorer.exe'}, + 'services.msc': {'mmc.exe'}, + 'compmgmt.msc': {'mmc.exe'}, + 'eventvwr.msc': {'mmc.exe'}, + 'devmgmt.msc': {'mmc.exe'}, + 'diskmgmt.msc': {'mmc.exe'}, + 'taskmgr.exe': {'winlogon.exe', 'explorer.exe'}, + 'msconfig.exe': {'explorer.exe'}, + 'systempropertiesadvanced.exe': {'explorer.exe'}, + + # ===== BROWSERS ===== + 'iexplore.exe': {'explorer.exe'}, + 'chrome.exe': {'explorer.exe'}, + 'firefox.exe': {'explorer.exe'}, + 'msedge.exe': {'explorer.exe'}, + 'opera.exe': {'explorer.exe'}, + 'safari.exe': {'explorer.exe'}, + 'browser_broker.exe': {'svchost.exe'}, + + # ===== MICROSOFT OFFICE ===== + 'winword.exe': {'explorer.exe'}, + 'excel.exe': {'explorer.exe'}, + 'powerpnt.exe': {'explorer.exe'}, + 'outlook.exe': {'explorer.exe'}, + 'msaccess.exe': {'explorer.exe'}, + 'mspub.exe': {'explorer.exe'}, + 'onenote.exe': {'explorer.exe'}, + 'lync.exe': {'explorer.exe'}, + 'teams.exe': {'explorer.exe'}, + 'msteams.exe': {'explorer.exe'}, + 'officeclicktorun.exe': {'svchost.exe'}, + + # ===== DEVELOPMENT TOOLS ===== + 'devenv.exe': {'explorer.exe'}, + 'code.exe': {'explorer.exe'}, + 'pycharm.exe': {'explorer.exe'}, + 'intellij.exe': {'explorer.exe'}, + 'eclipse.exe': {'explorer.exe'}, + 'netbeans.exe': {'explorer.exe'}, + 'atom.exe': {'explorer.exe'}, + 'sublime_text.exe': {'explorer.exe'}, + 'notepad++.exe': {'explorer.exe'}, + 'vim.exe': {'explorer.exe'}, + 'git-bash.exe': {'explorer.exe'}, + 'tortoisegit.exe': {'explorer.exe'}, + 'sourceTree.exe': {'explorer.exe'}, + + # ===== SYSTEM UTILITIES ===== + 'wuauclt.exe': {'svchost.exe'}, + 'wermgr.exe': {'svchost.exe'}, + 'werfault.exe': {'svchost.exe'}, + 'dllhst3g.exe': {'svchost.exe'}, + 'backgroundtaskhost.exe': {'svchost.exe'}, + 'searchui.exe': {'svchost.exe'}, + 'startmenuexperiencehost.exe': {'svchost.exe'}, + 'shellexperiencehost.exe': {'svchost.exe'}, + 'gamebar.exe': {'explorer.exe'}, + 'gamebarftserver.exe': {'svchost.exe'}, + + # ===== WINDOWS DEFENDER ===== + 'msmpeng.exe': {'services.exe'}, + 'nissrv.exe': {'services.exe'}, + 'securityhealthservice.exe': {'services.exe'}, + 'securityhealthsystray.exe': {'explorer.exe'}, + 'windowsdefender://': {'services.exe'}, + 'smartscreen.exe': {'svchost.exe'}, + + # ===== PRINT SPOOLER ===== + 'splwow64.exe': {'spoolsv.exe'}, + + # ===== .NET AND RUNTIME ===== + 'dotnet.exe': {'explorer.exe', 'svchost.exe'}, + 'vstest.console.exe': {'explorer.exe', 'cmd.exe'}, + 'msbuild.exe': {'explorer.exe', 'cmd.exe', 'svchost.exe'}, + + # ===== WINDOWS UPDATE ===== + 'tiworker.exe': {'svchost.exe'}, + 'usoclient.exe': {'svchost.exe'}, + 'musnotification.exe': {'svchost.exe'}, + 'musnotificationux.exe': {'svchost.exe'}, + + # ===== WINDOWS ERROR REPORTING ===== + 'wermgr.exe': {'svchost.exe'}, + 'werfault.exe': {'svchost.exe'}, + + # ===== COM AND DCOM ===== + 'comsurrogate.exe': {'dllhost.exe', 'svchost.exe'}, + 'dllhost.exe': {'services.exe', 'svchost.exe', 'explorer.exe'}, + + # ===== VIRTUALIZATION AND CONTAINERS ===== + 'vmware-tray.exe': {'explorer.exe'}, + 'vmtoolsd.exe': {'services.exe', 'explorer.exe'}, + 'vboxservice.exe': {'services.exe'}, + 'vboxtray.exe': {'explorer.exe'}, + 'docker.exe': {'services.exe', 'explorer.exe'}, + 'kubelet.exe': {'services.exe'}, + 'containerd.exe': {'services.exe'}, + + # ===== DATABASE SERVICES ===== + 'sqlservr.exe': {'services.exe'}, + 'mysqld.exe': {'services.exe'}, + 'postgres.exe': {'services.exe'}, + 'mongod.exe': {'services.exe'}, + 'oracle.exe': {'services.exe'}, + 'redis-server.exe': {'services.exe'}, + + # ===== WEB SERVERS ===== + 'httpd.exe': {'services.exe'}, + 'nginx.exe': {'services.exe'}, + 'iisexpress.exe': {'explorer.exe', 'svchost.exe'}, + 'w3wp.exe': {'services.exe'}, + + # ===== NETWORKING ===== + 'svchost.exe': {'services.exe'}, # Network-related services + 'dns.exe': {'services.exe'}, + 'dhcp.exe': {'services.exe'}, + 'rasman.exe': {'services.exe'}, + 'rastls.exe': {'services.exe'}, + 'ikeext.exe': {'services.exe'}, + 'bfe.exe': {'services.exe'}, + + # ===== ANTIVIRUS AND SECURITY SOFTWARE ===== + 'avp.exe': {'services.exe', 'explorer.exe'}, # Kaspersky + 'bdagent.exe': {'services.exe', 'explorer.exe'}, # BitDefender + 'ccsvchst.exe': {'services.exe'}, # Norton + 'mcshield.exe': {'services.exe'}, # McAfee + 'msseces.exe': {'explorer.exe'}, # Microsoft Security + 'sbamtray.exe': {'explorer.exe'}, # VIPRE + 'avguard.exe': {'services.exe'}, # Avira + 'avgui.exe': {'explorer.exe'}, # AVG + 'avastui.exe': {'explorer.exe'}, # Avast + + # ===== BACKUP SOFTWARE ===== + 'sbiesvc.exe': {'services.exe'}, # Sandboxie + 'vssvc.exe': {'services.exe'}, # Volume Shadow Copy + 'backupexec.exe': {'services.exe', 'explorer.exe'}, + 'arcserve.exe': {'services.exe'}, + + # ===== MONITORING AND MANAGEMENT ===== + 'teamviewer.exe': {'services.exe', 'explorer.exe'}, + 'teamviewer_service.exe': {'services.exe'}, + 'solarwinds.exe': {'services.exe'}, + 'nagios.exe': {'services.exe'}, + 'zabbix_agent.exe': {'services.exe'}, + 'prtg.exe': {'services.exe'}, + + # ===== REMOTE ACCESS ===== + 'rdpclip.exe': {'services.exe'}, + 'rdpinit.exe': {'services.exe'}, + 'mstsc.exe': {'explorer.exe'}, + 'termsrv.exe': {'services.exe'}, + + # ===== FILE SHARING AND SYNC ===== + 'dropbox.exe': {'explorer.exe'}, + 'googledrivesync.exe': {'explorer.exe'}, + 'onedrive.exe': {'explorer.exe'}, + 'boxsync.exe': {'explorer.exe'}, + 'megasync.exe': {'explorer.exe'}, + + # ===== MEDIA PLAYERS ===== + 'wmplayer.exe': {'explorer.exe'}, + 'itunes.exe': {'explorer.exe'}, + 'spotify.exe': {'explorer.exe'}, + 'vlc.exe': {'explorer.exe'}, + 'potplayer.exe': {'explorer.exe'}, + 'winamp.exe': {'explorer.exe'}, + + # ===== ARCHIVE TOOLS ===== + 'winrar.exe': {'explorer.exe'}, + '7z.exe': {'explorer.exe'}, + 'winzip.exe': {'explorer.exe'}, + 'winzip64.exe': {'explorer.exe'}, + + # ===== PDF READERS ===== + 'acrobat.exe': {'explorer.exe'}, + 'acrord32.exe': {'explorer.exe'}, + 'foxitreader.exe': {'explorer.exe'}, + 'sumatrapdf.exe': {'explorer.exe'}, + + # ===== IMAGE VIEWERS ===== + 'photos.exe': {'explorer.exe'}, + 'mspview.exe': {'explorer.exe'}, + 'irfanview.exe': {'explorer.exe'}, + 'xnview.exe': {'explorer.exe'}, + + # ===== SYSTEM CLEANING AND OPTIMIZATION ===== + 'ccleaner.exe': {'explorer.exe'}, + 'ccleaner64.exe': {'explorer.exe'}, + 'defraggler.exe': {'explorer.exe'}, + 'recuva.exe': {'explorer.exe'}, + 'glaryutilities.exe': {'explorer.exe'}, + 'iobituninstaller.exe': {'explorer.exe'}, + + # ===== GAMING ===== + 'steam.exe': {'explorer.exe'}, + 'steamservice.exe': {'services.exe'}, + 'epicgameslauncher.exe': {'explorer.exe'}, + 'battlenet.exe': {'explorer.exe'}, + 'origin.exe': {'explorer.exe'}, + 'galaxyclient.exe': {'explorer.exe'}, + + # ===== COMMUNICATION ===== + 'skype.exe': {'explorer.exe'}, + 'discord.exe': {'explorer.exe'}, + 'slack.exe': {'explorer.exe'}, + 'zoom.exe': {'explorer.exe'}, + 'teams.exe': {'explorer.exe'}, + 'whatsapp.exe': {'explorer.exe'}, + 'telegram.exe': {'explorer.exe'}, + + # ===== CLOUD STORAGE ===== + 'amazon drive.exe': {'explorer.exe'}, + 'icloud.exe': {'explorer.exe'}, + 'pcloud.exe': {'explorer.exe'}, + 'sync.com.exe': {'explorer.exe'}, + + # ===== VIRTUAL MACHINES ===== + 'virtualbox.exe': {'explorer.exe'}, + 'vmware.exe': {'explorer.exe'}, + 'hyper-v.exe': {'services.exe'}, + 'qemu.exe': {'explorer.exe'}, + + # ===== PROGRAMMING LANGUAGES ===== + 'python.exe': {'explorer.exe', 'cmd.exe', 'powershell.exe'}, + 'pythonw.exe': {'explorer.exe', 'svchost.exe'}, + 'node.exe': {'explorer.exe', 'cmd.exe'}, + 'npm.exe': {'cmd.exe'}, + 'java.exe': {'explorer.exe', 'services.exe', 'cmd.exe'}, + 'javaw.exe': {'explorer.exe'}, + 'perl.exe': {'explorer.exe', 'cmd.exe'}, + 'ruby.exe': {'explorer.exe', 'cmd.exe'}, + 'php.exe': {'explorer.exe', 'cmd.exe'}, + 'go.exe': {'explorer.exe', 'cmd.exe'}, + 'rustc.exe': {'explorer.exe', 'cmd.exe'}, + + # ===== BUILD TOOLS ===== + 'make.exe': {'cmd.exe'}, + 'cmake.exe': {'cmd.exe'}, + 'gradle.exe': {'cmd.exe'}, + 'maven.exe': {'cmd.exe'}, + 'ant.exe': {'cmd.exe'}, + 'scons.exe': {'cmd.exe'}, + + # ===== VERSION CONTROL ===== + 'git.exe': {'cmd.exe', 'explorer.exe'}, + 'svn.exe': {'cmd.exe'}, + 'hg.exe': {'cmd.exe'}, + 'tfs.exe': {'cmd.exe'}, + } + + # Check if this child process has known legitimate parents + if child_name in legitimate_relationships: + valid_parents = legitimate_relationships[child_name] + + # Check if current parent is in the valid set + if parent_name in valid_parents: + return { + 'status': 'LEGITIMATE', + 'severity': 'INFO', + 'evidence': f'Known legitimate relationship: {child_info["name"]} -> {child_info["parent_name"]}', + 'technique': 'Normal Execution', + 'confidence': 'HIGH' + } + + # Special case: svchost.exe can be parent to many legitimate children + if parent_name == 'svchost.exe' and child_name not in ['explorer.exe', 'winlogon.exe', 'csrss.exe', 'smss.exe']: + return { + 'status': 'LEGITIMATE', + 'severity': 'INFO', + 'evidence': 'Legitimate service host child process', + 'technique': 'Normal Execution', + 'confidence': 'HIGH' + } + + # Special case: services.exe can spawn many legitimate processes + if parent_name == 'services.exe' and not child_name.endswith('.exe'): + return { + 'status': 'LEGITIMATE', + 'severity': 'INFO', + 'evidence': 'Legitimate service child process', + 'technique': 'Normal Execution', + 'confidence': 'HIGH' + } + + # Special case: explorer.exe spawning user applications + if parent_name == 'explorer.exe' and child_name.endswith('.exe'): + return { + 'status': 'LEGITIMATE', + 'severity': 'INFO', + 'evidence': 'User-launched application', + 'technique': 'Normal Execution', + 'confidence': 'HIGH' + } + + return None + + def _check_malware_patterns(self, child_info: Dict, parent_info: Dict) -> Optional[Dict[str, Any]]: + """Check for common malware parent-child patterns""" + child_name = child_info['name'].lower() + parent_name = parent_info['name'].lower() + + # Common malware patterns + suspicious_patterns = [ + # System processes spawning unusual children + ('lsass.exe', ['cmd.exe', 'powershell.exe', 'wscript.exe']), + ('services.exe', ['rundll32.exe', 'regsvr32.exe', 'mshta.exe']), + ('svchost.exe', ['cmd.exe', 'powershell.exe', 'wscript.exe', 'mshta.exe']), + ('explorer.exe', ['rundll32.exe', 'regsvr32.exe', 'mshta.exe']), + + # Unusual parents for common processes + ('winword.exe', ['cmd.exe', 'powershell.exe', 'wscript.exe']), + ('excel.exe', ['cmd.exe', 'powershell.exe', 'wscript.exe']), + ('powerpnt.exe', ['cmd.exe', 'powershell.exe', 'wscript.exe']), + ('outlook.exe', ['cmd.exe', 'powershell.exe', 'wscript.exe']), + + # Script hosts spawning unusual children + ('wscript.exe', ['cmd.exe', 'powershell.exe']), + ('cscript.exe', ['cmd.exe', 'powershell.exe']), + ('mshta.exe', ['cmd.exe', 'powershell.exe']), + ] + + for suspicious_parent, suspicious_children in suspicious_patterns: + if parent_name == suspicious_parent and child_name in suspicious_children: + return { + 'status': 'SUSPICIOUS', + 'severity': 'HIGH', + 'evidence': f'Suspicious pattern: {parent_info["name"]} -> {child_info["name"]}', + 'technique': 'Living Off The Land (LOLBins)', + 'confidence': 'MEDIUM' + } + + return None + + def _is_valid_parent(self, ppid: int, processes: Dict) -> bool: + """Check if parent process exists and is valid""" + # PID 0 and 4 are valid system parents + if ppid in [0, 4]: + return True + + # Check if parent exists in process list + return ppid in processes + + def _is_session_consistent(self, child_info: Dict, parent_info: Dict) -> bool: + """Check if session IDs are consistent""" + child_session = child_info.get('session_id', -1) + parent_session = parent_info.get('session_id', -1) + + # Skip if session info unavailable + if child_session == -1 or parent_session == -1: + return True + + # Services can create processes in different sessions + if parent_info['name'].lower() in ['services.exe', 'svchost.exe']: + return True + + # Winlogon can create processes in user sessions + if parent_info['name'].lower() == 'winlogon.exe': + return True + + # Session Manager can create processes across sessions + if parent_info['name'].lower() == 'smss.exe': + return True + + # Normally, child should be in same session as parent + return child_session == parent_session + + def _is_integrity_consistent(self, child_info: Dict, parent_info: Dict) -> bool: + """Check integrity level consistency""" + child_integrity = child_info.get('integrity', 'Unknown') + parent_integrity = parent_info.get('integrity', 'Unknown') + + # Skip if integrity info unavailable + if child_integrity == 'Unknown' or parent_integrity == 'Unknown': + return True + + # Child should not have higher integrity than parent + integrity_levels = {'Low': 0, 'Medium': 1, 'High': 2, 'System': 3} + child_level = integrity_levels.get(child_integrity, 0) + parent_level = integrity_levels.get(parent_integrity, 0) + + return child_level <= parent_level + + def _is_time_consistent(self, child_info: Dict, parent_info: Dict) -> bool: + """Check process creation time consistency""" + child_time = child_info.get('create_time') + parent_time = parent_info.get('create_time') + + # Skip if time info unavailable + if not child_time or not parent_time: + return True + + # Child should never be created before parent + return child_time >= parent_time + + def _is_protection_consistent(self, child_info: Dict, parent_info: Dict) -> bool: + """Check protected process consistency""" + child_protected = child_info.get('is_protected', False) + parent_protected = parent_info.get('is_protected', False) + + # Protected process should not be spawned by unprotected process + if child_protected and not parent_protected: + return False + + return True + + def _check_system_process_anomaly(self, child_info: Dict, parent_info: Dict) -> Optional[Dict[str, Any]]: + """Check for system process anomalies using dynamic rules""" + child_name = child_info['name'].lower() + parent_name = parent_info['name'].lower() + + # System processes that should only have specific parents + system_processes = { + 'lsass.exe': {'wininit.exe'}, + 'csrss.exe': {'smss.exe'}, + 'wininit.exe': {'smss.exe'}, + 'services.exe': {'wininit.exe'}, + 'smss.exe': {'system', 'smss.exe'}, # Can be self-spawned + 'winlogon.exe': {'smss.exe'} + } + + for sys_proc, valid_parents in system_processes.items(): + if child_name == sys_proc.lower(): + valid_parents_lower = {p.lower() for p in valid_parents} + if parent_name not in valid_parents_lower: + return { + 'status': 'MALICIOUS', + 'severity': 'CRITICAL', + 'evidence': f'System process {child_info["name"]} has invalid parent {parent_info["name"]}', + 'technique': 'PPID Spoofing / Process Hollowing', + 'confidence': 'HIGH' + } + + return None + + def _get_process_name(self, proc) -> str: + """Safely extract process name""" + try: + return utility.array_to_string(proc.ImageFileName) + except: + try: + return proc.ImageFileName.cast( + "string", + max_length=proc.ImageFileName.vol.count, + errors="replace" + ) + except: + return "Unknown" + + def _get_create_time(self, proc) -> Optional[float]: + """Get process creation time""" + try: + if hasattr(proc, 'CreateTime'): + return float(proc.CreateTime) + except: + pass + return None + + def _get_session_id(self, proc) -> int: + """Get process session ID""" + try: + if hasattr(proc, 'SessionId'): + return int(proc.SessionId) + except: + pass + return -1 + + def _get_integrity_level(self, proc) -> str: + """Get process integrity level""" + try: + # This would require token parsing - simplified for example + if hasattr(proc, 'Token'): + return "Medium" # Default assumption + except: + pass + return "Unknown" + + def _is_protected_process(self, proc) -> bool: + """Check if process is protected""" + try: + # Check for protected process flags + if hasattr(proc, 'Flags'): + flags = int(proc.Fields) + # Simplified check - real implementation would parse PS_PROTECTION + return flags & 0x00000001 != 0 # Basic flag check + except: + pass + return False + + def _get_parent_name(self, processes: Dict, ppid: int) -> str: + """Get parent process name""" + if ppid in processes: + return processes[ppid]['name'] + elif ppid == 0: + return "System" + elif ppid == 4: + return "System" + else: + return "Unknown" + + def _format_output(self, proc_info: Dict, analysis: Dict, verbose: bool) -> Tuple: + """Format analysis result for output""" + + if verbose: + return (0, ( + proc_info['name'], + proc_info['process'].UniqueProcessId, + proc_info['ppid'], + proc_info['parent_name'], + analysis['status'], + analysis['severity'], + analysis['technique'], + analysis['evidence'], + analysis['confidence'], + proc_info.get('session_id', 'N/A') + )) + else: + return (0, ( + proc_info['name'], + proc_info['process'].UniqueProcessId, + proc_info['ppid'], + proc_info['parent_name'], + analysis['status'], + analysis['severity'], + analysis['evidence'] + )) + + def run(self): + verbose = self.config.get("verbose", False) + + if verbose: + columns = [ + ("Process", str), + ("PID", int), + ("PPID", int), + ("Parent Name", str), + ("Status", str), + ("Severity", str), + ("Technique", str), + ("Evidence", str), + ("Confidence", str), + ("Session", str) + ] + else: + columns = [ + ("Process", str), + ("PID", int), + ("PPID", int), + ("Parent Name", str), + ("Status", str), + ("Severity", str), + ("Evidence", str) + ] + + return renderers.TreeGrid(columns, self._generator())