Merge pull request #1791 from guardicore/1611-interruptable-powershell

1611 Make powershell exploiter interruptable
This commit is contained in:
Mike Salvatore 2022-03-21 10:27:01 -04:00 committed by GitHub
commit b1716e9457
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 40 additions and 24 deletions

View File

@ -94,14 +94,15 @@ class HostExploiter:
) )
self.set_start_time() self.set_start_time()
def is_interrupted(self): def _is_interrupted(self):
return self.interrupt.is_set()
def _set_interrupted(self):
# This method should be refactored to raise an exception to reduce duplication in the # This method should be refactored to raise an exception to reduce duplication in the
# "if is_interrupted: return self.exploitation_results" # "if is_interrupted: return self.exploitation_results"
# Ideally the user should only do "check_for_interrupt()" # Ideally the user should only do "check_for_interrupt()"
if self.interrupt.is_set():
logger.info("Exploiter has been interrupted") logger.info("Exploiter has been interrupted")
self.exploit_result.error_message = "Exploiter has been interrupted" self.exploit_result.interrupted = True
return self.interrupt.is_set()
def post_exploit(self): def post_exploit(self):
self.set_finish_time() self.set_finish_time()

View File

@ -73,7 +73,8 @@ class MSSQLExploiter(HostExploiter):
) )
return self.exploit_result return self.exploit_result
if self.is_interrupted(): if self._is_interrupted():
self._set_interrupted()
return self.exploit_result return self.exploit_result
try: try:

View File

@ -23,6 +23,7 @@ from infection_monkey.exploit.tools.helpers import get_random_file_suffix
from infection_monkey.model import DROPPER_ARG, RUN_MONKEY, VictimHost from infection_monkey.model import DROPPER_ARG, RUN_MONKEY, VictimHost
from infection_monkey.utils.commands import build_monkey_commandline from infection_monkey.utils.commands import build_monkey_commandline
from infection_monkey.utils.environment import is_windows_os from infection_monkey.utils.environment import is_windows_os
from infection_monkey.utils.threading import interruptable_iter
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -68,14 +69,17 @@ class PowerShellExploiter(HostExploiter):
auth_options = [get_auth_options(creds, use_ssl) for creds in credentials] auth_options = [get_auth_options(creds, use_ssl) for creds in credentials]
self._client = self._authenticate_via_brute_force(credentials, auth_options) self._client = self._authenticate_via_brute_force(credentials, auth_options)
if self._is_interrupted():
self._set_interrupted()
return self.exploit_result
if not self._client: if not self._client:
self.exploit_result.error_message = ( self.exploit_result.error_message = (
"Unable to authenticate to the remote host using any of the available credentials" "Unable to authenticate to the remote host using any of the available credentials"
) )
return self.exploit_result return self.exploit_result
self.exploit_result.exploitation_success = True
try: try:
self._execute_monkey_agent_on_victim() self._execute_monkey_agent_on_victim()
self.exploit_result.propagation_success = True self.exploit_result.propagation_success = True
@ -133,7 +137,7 @@ class PowerShellExploiter(HostExploiter):
def _authenticate_via_brute_force( def _authenticate_via_brute_force(
self, credentials: List[Credentials], auth_options: List[AuthOptions] self, credentials: List[Credentials], auth_options: List[AuthOptions]
) -> Optional[IPowerShellClient]: ) -> Optional[IPowerShellClient]:
for (creds, opts) in zip(credentials, auth_options): for (creds, opts) in interruptable_iter(zip(credentials, auth_options), self.interrupt):
try: try:
client = PowerShellClient(self.host.ip_addr, creds, opts) client = PowerShellClient(self.host.ip_addr, creds, opts)
client.connect() client.connect()
@ -142,7 +146,9 @@ class PowerShellExploiter(HostExploiter):
f"{creds.username}, Secret Type: {creds.secret_type.name}" f"{creds.username}, Secret Type: {creds.secret_type.name}"
) )
self.exploit_result.exploitation_success = True
self._report_login_attempt(True, creds) self._report_login_attempt(True, creds)
return client return client
except Exception as ex: except Exception as ex:
logger.debug( logger.debug(

View File

@ -70,7 +70,8 @@ class WmiExploiter(HostExploiter):
downloaded_agent = self.agent_repository.get_agent_binary(self.host.os["type"]) downloaded_agent = self.agent_repository.get_agent_binary(self.host.os["type"])
if self.is_interrupted(): if self._is_interrupted():
self._set_interrupted()
return self.exploit_result return self.exploit_result
remote_full_path = SmbTools.copy_file( remote_full_path = SmbTools.copy_file(

View File

@ -24,6 +24,7 @@ class UnknownPluginError(Exception):
class ExploiterResultData: class ExploiterResultData:
exploitation_success: bool = False exploitation_success: bool = False
propagation_success: bool = False propagation_success: bool = False
interrupted: bool = False
os: str = "" os: str = ""
info: Mapping = None info: Mapping = None
attempts: Iterable = None attempts: Iterable = None

View File

@ -190,17 +190,18 @@ class MockPuppet(IPuppet):
successful_exploiters = { successful_exploiters = {
DOT_1: { DOT_1: {
"PowerShellExploiter": ExploiterResultData( "PowerShellExploiter": ExploiterResultData(
True, True, os_windows, info_powershell, attempts, None True, True, False, os_windows, info_powershell, attempts, None
), ),
"ZerologonExploiter": ExploiterResultData( "ZerologonExploiter": ExploiterResultData(
False, False, os_windows, {}, [], "Zerologon failed" False, False, False, os_windows, {}, [], "Zerologon failed"
), ),
"SSHExploiter": ExploiterResultData( "SSHExploiter": ExploiterResultData(
False, False, os_linux, info_ssh, attempts, "Failed exploiting" False, False, False, os_linux, info_ssh, attempts, "Failed exploiting"
), ),
}, },
DOT_3: { DOT_3: {
"PowerShellExploiter": ExploiterResultData( "PowerShellExploiter": ExploiterResultData(
False,
False, False,
False, False,
os_windows, os_windows,
@ -209,9 +210,11 @@ class MockPuppet(IPuppet):
"PowerShell Exploiter Failed", "PowerShell Exploiter Failed",
), ),
"SSHExploiter": ExploiterResultData( "SSHExploiter": ExploiterResultData(
False, False, os_linux, info_ssh, attempts, "Failed exploiting" False, False, False, os_linux, info_ssh, attempts, "Failed exploiting"
),
"ZerologonExploiter": ExploiterResultData(
True, False, False, os_windows, {}, [], None
), ),
"ZerologonExploiter": ExploiterResultData(True, False, os_windows, {}, [], None),
}, },
} }
@ -219,7 +222,7 @@ class MockPuppet(IPuppet):
return successful_exploiters[host.ip_addr][name] return successful_exploiters[host.ip_addr][name]
except KeyError: except KeyError:
return ExploiterResultData( return ExploiterResultData(
False, False, os_linux, {}, [], f"{name} failed for host {host}" False, False, False, os_linux, {}, [], f"{name} failed for host {host}"
) )
def run_payload(self, name: str, options: Dict, interrupt: threading.Event): def run_payload(self, name: str, options: Dict, interrupt: threading.Event):

View File

@ -1,9 +1,9 @@
from typing import Dict from typing import Dict
from common.common_consts.telem_categories import TelemCategoryEnum from common.common_consts.telem_categories import TelemCategoryEnum
from infection_monkey.i_puppet.i_puppet import ExploiterResultData
from infection_monkey.model.host import VictimHost from infection_monkey.model.host import VictimHost
from infection_monkey.telemetry.base_telem import BaseTelem from infection_monkey.telemetry.base_telem import BaseTelem
from infection_monkey.i_puppet.i_puppet import ExploiterResultData
class ExploitTelem(BaseTelem): class ExploitTelem(BaseTelem):
@ -25,6 +25,7 @@ class ExploitTelem(BaseTelem):
self.host = host.__dict__ self.host = host.__dict__
self.exploitation_result = result.exploitation_success self.exploitation_result = result.exploitation_success
self.propagation_result = result.propagation_success self.propagation_result = result.propagation_success
self.interrupted = result.interrupted
self.info = result.info self.info = result.info
self.attempts = result.attempts self.attempts = result.attempts
@ -34,6 +35,7 @@ class ExploitTelem(BaseTelem):
return { return {
"exploitation_result": self.exploitation_result, "exploitation_result": self.exploitation_result,
"propagation_result": self.propagation_result, "propagation_result": self.propagation_result,
"interrupted": self.interrupted,
"machine": self.host, "machine": self.host,
"exploiter": self.name, "exploiter": self.name,
"info": self.info, "info": self.info,

View File

@ -201,38 +201,38 @@ class MockExploiter:
results_callback( results_callback(
"PowerShellExploiter", "PowerShellExploiter",
host, host,
ExploiterResultData(True, True, os_windows, {}, {}, None), ExploiterResultData(True, True, False, os_windows, {}, {}, None),
) )
results_callback( results_callback(
"SSHExploiter", "SSHExploiter",
host, host,
ExploiterResultData(False, False, os_linux, {}, {}, "SSH FAILED for .1"), ExploiterResultData(False, False, False, os_linux, {}, {}, "SSH FAILED for .1"),
) )
elif host.ip_addr.endswith(".2"): elif host.ip_addr.endswith(".2"):
results_callback( results_callback(
"PowerShellExploiter", "PowerShellExploiter",
host, host,
ExploiterResultData( ExploiterResultData(
False, False, os_windows, {}, {}, "POWERSHELL FAILED for .2" False, False, False, os_windows, {}, {}, "POWERSHELL FAILED for .2"
), ),
) )
results_callback( results_callback(
"SSHExploiter", "SSHExploiter",
host, host,
ExploiterResultData(False, False, os_linux, {}, {}, "SSH FAILED for .2"), ExploiterResultData(False, False, False, os_linux, {}, {}, "SSH FAILED for .2"),
) )
elif host.ip_addr.endswith(".3"): elif host.ip_addr.endswith(".3"):
results_callback( results_callback(
"PowerShellExploiter", "PowerShellExploiter",
host, host,
ExploiterResultData( ExploiterResultData(
False, False, os_windows, {}, {}, "POWERSHELL FAILED for .3" False, False, False, os_windows, {}, {}, "POWERSHELL FAILED for .3"
), ),
) )
results_callback( results_callback(
"SSHExploiter", "SSHExploiter",
host, host,
ExploiterResultData(True, True, os_linux, {}, {}, None), ExploiterResultData(True, True, False, os_linux, {}, {}, None),
) )

View File

@ -40,7 +40,7 @@ def exploit_telem_test_instance():
EXPLOITER_NAME, EXPLOITER_NAME,
HOST, HOST,
ExploiterResultData( ExploiterResultData(
RESULT, RESULT, OS_LINUX, EXPLOITER_INFO, EXPLOITER_ATTEMPTS, ERROR_MSG RESULT, RESULT, False, OS_LINUX, EXPLOITER_INFO, EXPLOITER_ATTEMPTS, ERROR_MSG
), ),
) )
@ -50,6 +50,7 @@ def test_exploit_telem_send(exploit_telem_test_instance, spy_send_telemetry):
expected_data = { expected_data = {
"exploitation_result": RESULT, "exploitation_result": RESULT,
"propagation_result": RESULT, "propagation_result": RESULT,
"interrupted": False,
"machine": HOST_AS_DICT, "machine": HOST_AS_DICT,
"exploiter": EXPLOITER_NAME, "exploiter": EXPLOITER_NAME,
"info": EXPLOITER_INFO, "info": EXPLOITER_INFO,