diff --git a/monkey/infection_monkey/credential_collectors/ssh_collector/ssh_handler.py b/monkey/infection_monkey/credential_collectors/ssh_collector/ssh_handler.py index ce3b17311..98ca0df4a 100644 --- a/monkey/infection_monkey/credential_collectors/ssh_collector/ssh_handler.py +++ b/monkey/infection_monkey/credential_collectors/ssh_collector/ssh_handler.py @@ -1,7 +1,6 @@ import glob import logging import os -import pwd from typing import Dict, Iterable from common.utils.attack_utils import ScanStatus @@ -30,6 +29,8 @@ def get_ssh_info(telemetry_messenger: ITelemetryMessenger) -> Iterable[Dict]: def _get_home_dirs() -> Iterable[Dict]: + import pwd + root_dir = _get_ssh_struct("root", "") home_dirs = [ _get_ssh_struct(x.pw_name, x.pw_dir) for x in pwd.getpwall() if x.pw_dir.startswith("/home") diff --git a/monkey/infection_monkey/exploit/HostExploiter.py b/monkey/infection_monkey/exploit/HostExploiter.py index ed7d29d18..744ea57e8 100644 --- a/monkey/infection_monkey/exploit/HostExploiter.py +++ b/monkey/infection_monkey/exploit/HostExploiter.py @@ -51,7 +51,7 @@ class HostExploiter: def send_exploit_telemetry(self, name: str, result: bool): from infection_monkey.telemetry.exploit_telem import ExploitTelem - ExploitTelem( + ExploitTelem( # stale code name=name, host=self.host, result=result, diff --git a/monkey/infection_monkey/i_puppet/i_puppet.py b/monkey/infection_monkey/i_puppet/i_puppet.py index cafc24f4d..79bd3b4fe 100644 --- a/monkey/infection_monkey/i_puppet/i_puppet.py +++ b/monkey/infection_monkey/i_puppet/i_puppet.py @@ -17,7 +17,8 @@ class UnknownPluginError(Exception): ExploiterResultData = namedtuple( - "ExploiterResultData", ["success", "info", "attempts", "error_message"] + "ExploiterResultData", + ["exploitation_success", "propagation_success", "os", "info", "attempts", "error_message"], ) PingScanData = namedtuple("PingScanData", ["response_received", "os"]) PortScanData = namedtuple("PortScanData", ["port", "status", "banner", "service"]) diff --git a/monkey/infection_monkey/master/exploiter.py b/monkey/infection_monkey/master/exploiter.py index 8405fb6e8..092bc78d8 100644 --- a/monkey/infection_monkey/master/exploiter.py +++ b/monkey/infection_monkey/master/exploiter.py @@ -104,7 +104,7 @@ class Exploiter: ) results_callback(exploiter_name, victim_host, exploiter_results) - if exploiter_name != "ZerologonExploiter" and exploiter_results.success: + if exploiter_results.propagation_success: break def _run_exploiter( diff --git a/monkey/infection_monkey/master/mock_master.py b/monkey/infection_monkey/master/mock_master.py index 5c522a565..e7e8e6237 100644 --- a/monkey/infection_monkey/master/mock_master.py +++ b/monkey/infection_monkey/master/mock_master.py @@ -101,20 +101,16 @@ class MockMaster(IMaster): def _exploit(self): logger.info("Exploiting victims") - result, info, attempts, error_message = self._puppet.exploit_host( - "PowerShellExploiter", "10.0.0.1", {}, None - ) - logger.info(f"Attempts for exploiting {attempts}") + result = self._puppet.exploit_host("PowerShellExploiter", "10.0.0.1", {}, None) + logger.info(f"Attempts for exploiting {result.attempts}") self._telemetry_messenger.send_telemetry( - ExploitTelem("PowerShellExploiter", self._hosts["10.0.0.1"], result, info, attempts) + ExploitTelem("PowerShellExploiter", self._hosts["10.0.0.1"], result) ) - result, info, attempts, error_message = self._puppet.exploit_host( - "SSHExploiter", "10.0.0.3", {}, None - ) - logger.info(f"Attempts for exploiting {attempts}") + result = self._puppet.exploit_host("SSHExploiter", "10.0.0.3", {}, None) + logger.info(f"Attempts for exploiting {result.attempts}") self._telemetry_messenger.send_telemetry( - ExploitTelem("SSHExploiter", self._hosts["10.0.0.3"], result, info, attempts) + ExploitTelem("SSHExploiter", self._hosts["10.0.0.3"], result) ) logger.info("Finished exploiting victims") diff --git a/monkey/infection_monkey/master/propagator.py b/monkey/infection_monkey/master/propagator.py index 87f9a1896..a8437cc94 100644 --- a/monkey/infection_monkey/master/propagator.py +++ b/monkey/infection_monkey/master/propagator.py @@ -153,13 +153,16 @@ class Propagator: def _process_exploit_attempts( self, exploiter_name: str, host: VictimHost, result: ExploiterResultData ): - if result.success: + if result.propagation_success: logger.info(f"Successfully propagated to {host} using {exploiter_name}") + elif result.exploitation_success: + logger.info( + f"Successfully exploited (but did not propagate to) {host} using {exploiter_name}" + ) else: logger.info( - f"Failed to propagate to {host} using {exploiter_name}: {result.error_message}" + f"Failed to exploit or propagate to {host} using {exploiter_name}: " + f"{result.error_message}" ) - self._telemetry_messenger.send_telemetry( - ExploitTelem(exploiter_name, host, result.success, result.info, result.attempts) - ) + self._telemetry_messenger.send_telemetry(ExploitTelem(exploiter_name, host, result)) diff --git a/monkey/infection_monkey/puppet/mock_puppet.py b/monkey/infection_monkey/puppet/mock_puppet.py index 8b76d175a..453265f55 100644 --- a/monkey/infection_monkey/puppet/mock_puppet.py +++ b/monkey/infection_monkey/puppet/mock_puppet.py @@ -177,25 +177,43 @@ class MockPuppet(IPuppet): "vulnerable_ports": [22], "executed_cmds": [], } + os_windows = "windows" + os_linux = "linux" + successful_exploiters = { DOT_1: { - "PowerShellExploiter": ExploiterResultData(True, info_powershell, attempts, None), - "ZerologonExploiter": ExploiterResultData(False, {}, [], "Zerologon failed"), - "SSHExploiter": ExploiterResultData(False, info_ssh, attempts, "Failed exploiting"), + "PowerShellExploiter": ExploiterResultData( + True, True, os_windows, info_powershell, attempts, None + ), + "ZerologonExploiter": ExploiterResultData( + False, False, os_windows, {}, [], "Zerologon failed" + ), + "SSHExploiter": ExploiterResultData( + False, False, os_linux, info_ssh, attempts, "Failed exploiting" + ), }, DOT_3: { "PowerShellExploiter": ExploiterResultData( - False, info_powershell, attempts, "PowerShell Exploiter Failed" + False, + False, + os_windows, + info_powershell, + attempts, + "PowerShell Exploiter Failed", ), - "SSHExploiter": ExploiterResultData(False, info_ssh, attempts, "Failed exploiting"), - "ZerologonExploiter": ExploiterResultData(True, {}, [], None), + "SSHExploiter": ExploiterResultData( + False, False, os_linux, info_ssh, attempts, "Failed exploiting" + ), + "ZerologonExploiter": ExploiterResultData(True, False, os_windows, {}, [], None), }, } try: return successful_exploiters[host][name] except KeyError: - return ExploiterResultData(False, {}, [], f"{name} failed for host {host}") + return ExploiterResultData( + False, False, os_linux, {}, [], f"{name} failed for host {host}" + ) def run_payload(self, name: str, options: Dict, interrupt: threading.Event): logger.debug(f"run_payload({name}, {options})") diff --git a/monkey/infection_monkey/telemetry/exploit_telem.py b/monkey/infection_monkey/telemetry/exploit_telem.py index a34b4e861..5c131dc77 100644 --- a/monkey/infection_monkey/telemetry/exploit_telem.py +++ b/monkey/infection_monkey/telemetry/exploit_telem.py @@ -1,33 +1,39 @@ -from typing import Dict, List +from typing import Dict from common.common_consts.telem_categories import TelemCategoryEnum from infection_monkey.model.host import VictimHost from infection_monkey.telemetry.base_telem import BaseTelem +from monkey.infection_monkey.i_puppet.i_puppet import ExploiterResultData class ExploitTelem(BaseTelem): - def __init__(self, name: str, host: VictimHost, result: bool, info: Dict, attempts: List): + def __init__( + self, + name: str, + host: VictimHost, + result: ExploiterResultData, + ): """ Default exploit telemetry constructor :param name: The name of exploiter used :param host: The host machine - :param result: The result from the 'exploit_host' method - :param info: Information about the exploiter - :param attempts: Information about the exploiter's attempts + :param result: Data about the exploitation (success status, info, attempts, etc) """ super(ExploitTelem, self).__init__() self.name = name self.host = host.__dict__ - self.result = result - self.info = info - self.attempts = attempts + self.exploitation_result = result.exploitation_success + self.propagation_result = result.propagation_success + self.info = result.info + self.attempts = result.attempts telem_category = TelemCategoryEnum.EXPLOIT def get_data(self) -> Dict: return { - "result": self.result, + "exploitation_result": self.exploitation_result, + "propagation_result": self.propagation_result, "machine": self.host, "exploiter": self.name, "info": self.info, diff --git a/monkey/monkey_island/cc/services/edge/edge.py b/monkey/monkey_island/cc/services/edge/edge.py index 461b0e8a5..1ec7462c3 100644 --- a/monkey/monkey_island/cc/services/edge/edge.py +++ b/monkey/monkey_island/cc/services/edge/edge.py @@ -78,7 +78,7 @@ class EdgeService(Edge): def update_based_on_exploit(self, exploit: Dict): self.exploits.append(exploit) self.save() - if exploit["result"]: + if exploit["exploitation_success"]: self.set_exploited() def set_exploited(self): diff --git a/monkey/monkey_island/cc/services/telemetry/processing/exploit.py b/monkey/monkey_island/cc/services/telemetry/processing/exploit.py index e302be5f5..6cd4bc4ae 100644 --- a/monkey/monkey_island/cc/services/telemetry/processing/exploit.py +++ b/monkey/monkey_island/cc/services/telemetry/processing/exploit.py @@ -24,7 +24,7 @@ def process_exploit_telemetry(telemetry_json): check_machine_exploited( current_monkey=Monkey.get_single_monkey_by_guid(telemetry_json["monkey_guid"]), - exploit_successful=telemetry_json["data"]["result"], + exploit_successful=telemetry_json["data"]["exploitation_success"], exploiter=telemetry_json["data"]["exploiter"], target_ip=telemetry_json["data"]["machine"]["ip_addr"], timestamp=telemetry_json["timestamp"], @@ -65,7 +65,7 @@ def update_network_with_exploit(edge: EdgeService, telemetry_json): new_exploit.pop("machine") new_exploit["timestamp"] = telemetry_json["timestamp"] edge.update_based_on_exploit(new_exploit) - if new_exploit["result"]: + if new_exploit["exploitation_success"]: NodeService.set_node_exploited(edge.dst_node_id) diff --git a/monkey/tests/unit_tests/infection_monkey/master/test_propagator.py b/monkey/tests/unit_tests/infection_monkey/master/test_propagator.py index 0e54f2a4e..06be41c71 100644 --- a/monkey/tests/unit_tests/infection_monkey/master/test_propagator.py +++ b/monkey/tests/unit_tests/infection_monkey/master/test_propagator.py @@ -100,6 +100,10 @@ dot_3_services = { }, } +os_windows = "windows" + +os_linux = "linux" + @pytest.fixture def mock_ip_scanner(): @@ -184,34 +188,38 @@ class MockExploiter: results_callback( "PowerShellExploiter", host, - ExploiterResultData(True, {}, {}, None), + ExploiterResultData(True, True, os_windows, {}, {}, None), ) results_callback( "SSHExploiter", host, - ExploiterResultData(False, {}, {}, "SSH FAILED for .1"), + ExploiterResultData(False, False, os_linux, {}, {}, "SSH FAILED for .1"), ) elif host.ip_addr.endswith(".2"): results_callback( "PowerShellExploiter", host, - ExploiterResultData(False, {}, {}, "POWERSHELL FAILED for .2"), + ExploiterResultData( + False, False, os_windows, {}, {}, "POWERSHELL FAILED for .2" + ), ) results_callback( "SSHExploiter", host, - ExploiterResultData(False, {}, {}, "SSH FAILED for .2"), + ExploiterResultData(False, False, os_linux, {}, {}, "SSH FAILED for .2"), ) elif host.ip_addr.endswith(".3"): results_callback( "PowerShellExploiter", host, - ExploiterResultData(False, {}, {}, "POWERSHELL FAILED for .3"), + ExploiterResultData( + False, False, os_windows, {}, {}, "POWERSHELL FAILED for .3" + ), ) results_callback( "SSHExploiter", host, - ExploiterResultData(True, {}, {}, None), + ExploiterResultData(True, True, os_linux, {}, {}, None), ) @@ -246,14 +254,14 @@ def test_exploiter_result_processing( if ip.endswith(".1"): if data["exploiter"].startswith("PowerShell"): - assert data["result"] + assert data["propagation_result"] else: - assert not data["result"] + assert not data["propagation_result"] elif ip.endswith(".3"): if data["exploiter"].startswith("PowerShell"): - assert not data["result"] + assert not data["propagation_result"] else: - assert data["result"] + assert data["propagation_result"] def test_scan_target_generation(telemetry_messenger_spy, mock_ip_scanner, mock_victim_host_factory): diff --git a/monkey/tests/unit_tests/infection_monkey/telemetry/test_exploit_telem.py b/monkey/tests/unit_tests/infection_monkey/telemetry/test_exploit_telem.py index 982299947..5d6c81f56 100644 --- a/monkey/tests/unit_tests/infection_monkey/telemetry/test_exploit_telem.py +++ b/monkey/tests/unit_tests/infection_monkey/telemetry/test_exploit_telem.py @@ -5,6 +5,7 @@ import pytest from infection_monkey.exploit.sshexec import SSHExploiter from infection_monkey.model.host import VictimHost from infection_monkey.telemetry.exploit_telem import ExploitTelem +from monkey.infection_monkey.i_puppet.i_puppet import ExploiterResultData DOMAIN_NAME = "domain-name" IP = "0.0.0.0" @@ -30,17 +31,20 @@ EXPLOITER_INFO = { } EXPLOITER_ATTEMPTS = [] RESULT = False +OS_LINUX = "linux" +ERROR_MSG = "failed because yolo" @pytest.fixture def exploit_telem_test_instance(): - return ExploitTelem(EXPLOITER_NAME, HOST, RESULT, EXPLOITER_INFO, EXPLOITER_ATTEMPTS) + return ExploitTelem(EXPLOITER_NAME, HOST, ExploiterResultData(RESULT, RESULT, OS_LINUX, EXPLOITER_INFO, EXPLOITER_ATTEMPTS, ERROR_MSG)) def test_exploit_telem_send(exploit_telem_test_instance, spy_send_telemetry): exploit_telem_test_instance.send() expected_data = { - "result": RESULT, + "exploitation_result": RESULT, + "propagation_result": RESULT, "machine": HOST_AS_DICT, "exploiter": EXPLOITER_NAME, "info": EXPLOITER_INFO,