diff --git a/.swm/AzD8XysWg1BBXCjCDkfq.swm b/.swm/AzD8XysWg1BBXCjCDkfq.swm index 708d8e8c5..3339f5178 100644 --- a/.swm/AzD8XysWg1BBXCjCDkfq.swm +++ b/.swm/AzD8XysWg1BBXCjCDkfq.swm @@ -29,24 +29,6 @@ " victims_max_exploit = 100" ] }, - { - "type": "snippet", - "path": "monkey/infection_monkey/monkey.py", - "comments": [], - "firstLineNumber": 220, - "lines": [ - " if not WormConfiguration.alive:", - " logger.info(\"Marked not alive from configuration\")", - " ", - "* machines = self._network.get_victim_machines(", - "* max_find=WormConfiguration.victims_max_find,", - "* stop_callback=ControlClient.check_for_stop,", - "* )", - " for machine in machines:", - " if ControlClient.check_for_stop():", - " break" - ] - }, { "type": "snippet", "path": "monkey/monkey_island/cc/services/config_schema/internal.py", @@ -79,7 +61,6 @@ "app_version": "0.6.6-2", "file_blobs": { "monkey/infection_monkey/config.py": "8f4984ba6563564343282765ab498efca5d89ba8", - "monkey/infection_monkey/monkey.py": "4160a36e0e624404d77526472d51dd07bba49e5a", "monkey/monkey_island/cc/services/config_schema/internal.py": "86318eaf19b9991a8af5de861a3eb085238e17a4" } } diff --git a/monkey/infection_monkey/dropper.py b/monkey/infection_monkey/dropper.py index f74767cef..e2f59e601 100644 --- a/monkey/infection_monkey/dropper.py +++ b/monkey/infection_monkey/dropper.py @@ -55,7 +55,6 @@ class MonkeyDrops(object): "destination_path": self.opts.location, } - def initialize(self): logger.debug("Dropper is running with config:\n%s", pprint.pformat(self._config)) def start(self): diff --git a/monkey/infection_monkey/exploit/HostExploiter.py b/monkey/infection_monkey/exploit/HostExploiter.py index 3a5abf4c5..34fd674ff 100644 --- a/monkey/infection_monkey/exploit/HostExploiter.py +++ b/monkey/infection_monkey/exploit/HostExploiter.py @@ -66,10 +66,16 @@ class HostExploiter(Plugin): def is_os_supported(self): return self.host.os.get("type") in self._TARGET_OS_TYPE - def send_exploit_telemetry(self, result): + def send_exploit_telemetry(self, name: str, result: bool): from infection_monkey.telemetry.exploit_telem import ExploitTelem - ExploitTelem(self, result).send() + ExploitTelem( + name=name, + host=self.host, + result=result, + info=self.exploit_info, + attempts=self.exploit_attempts, + ).send() def report_login_attempt(self, result, user, password="", lm_hash="", ntlm_hash="", ssh_key=""): self.exploit_attempts.append( diff --git a/monkey/infection_monkey/main.py b/monkey/infection_monkey/main.py index bb08f4b4f..d6edfaec2 100644 --- a/monkey/infection_monkey/main.py +++ b/monkey/infection_monkey/main.py @@ -118,11 +118,9 @@ def main(): logger.info(f"version: {get_version()}") monkey = monkey_cls(monkey_args) - monkey.initialize() try: monkey.start() - return True except Exception as e: logger.exception("Exception thrown from monkey's start function. More info: {}".format(e)) diff --git a/monkey/infection_monkey/master/mock_master.py b/monkey/infection_monkey/master/mock_master.py index 4cf6dc176..e78519a43 100644 --- a/monkey/infection_monkey/master/mock_master.py +++ b/monkey/infection_monkey/master/mock_master.py @@ -33,7 +33,7 @@ class MockMaster(IMaster): self._run_payload() def _run_sys_info_collectors(self): - logging.info("Running system info collectors") + logger.info("Running system info collectors") system_info_telemetry = {} system_info_telemetry["ProcessListCollector"] = self._puppet.run_sys_info_collector( "ProcessListCollector" @@ -43,10 +43,13 @@ class MockMaster(IMaster): ) system_info = self._puppet.run_sys_info_collector("LinuxInfoCollector") self._telemetry_messenger.send_telemetry(SystemInfoTelem(system_info)) - logging.info("Finished running system info collectors") + logger.info("Finished running system info collectors") def _run_pbas(self): - logging.info("Running post breach actions") + + # TODO: Create monkey_dir and revise setup in monkey.py + + logger.info("Running post breach actions") name = "AccountDiscovery" command, result = self._puppet.run_pba(name, {}) self._telemetry_messenger.send_telemetry(PostBreachTelem(name, command, result)) @@ -54,10 +57,10 @@ class MockMaster(IMaster): name = "CommunicateAsBackdoorUser" command, result = self._puppet.run_pba(name, {}) self._telemetry_messenger.send_telemetry(PostBreachTelem(name, command, result)) - logging.info("Finished running post breach actions") + logger.info("Finished running post breach actions") def _scan_victims(self): - logging.info("Scanning network for potential victims") + logger.info("Scanning network for potential victims") ips = ["10.0.0.1", "10.0.0.2", "10.0.0.3"] ports = [22, 445, 3389, 8008] for ip in ips: @@ -78,10 +81,10 @@ class MockMaster(IMaster): h.services[port_scan_data.service]["banner"] = port_scan_data.banner self._telemetry_messenger.send_telemetry(ScanTelem(h)) - logging.info("Finished scanning network for potential victims") + logger.info("Finished scanning network for potential victims") def _fingerprint(self): - logging.info("Running fingerprinters on potential victims") + logger.info("Running fingerprinters on potential victims") machine_1 = self._hosts["10.0.0.1"] machine_3 = self._hosts["10.0.0.3"] @@ -93,32 +96,35 @@ class MockMaster(IMaster): self._puppet.fingerprint("HTTPFinger", machine_3) self._telemetry_messenger.send_telemetry(ScanTelem(machine_3)) - logging.info("Finished running fingerprinters on potential victims") + logger.info("Finished running fingerprinters on potential victims") def _exploit(self): - logging.info("Exploiting victims") + logger.info("Exploiting victims") result, info, attempts = self._puppet.exploit_host( "PowerShellExploiter", "10.0.0.1", {}, None ) + logger.info(f"Attempts for exploiting {attempts}") self._telemetry_messenger.send_telemetry( ExploitTelem("PowerShellExploiter", self._hosts["10.0.0.1"], result, info, attempts) ) result, info, attempts = self._puppet.exploit_host("SSHExploiter", "10.0.0.3", {}, None) + logger.info(f"Attempts for exploiting {attempts}") self._telemetry_messenger.send_telemetry( ExploitTelem("SSHExploiter", self._hosts["10.0.0.3"], result, info, attempts) ) - logging.info("Finished exploiting victims") + logger.info("Finished exploiting victims") def _run_payload(self): - logging.info("Running payloads") + logger.info("Running payloads") # TODO: modify what FileEncryptionTelem gets path, success, error = self._puppet.run_payload("RansomwarePayload", {}, None) self._telemetry_messenger.send_telemetry(FileEncryptionTelem(path, success, error)) - logging.info("Finished running payloads") + logger.info("Finished running payloads") def terminate(self) -> None: logger.info("Terminating MockMaster") def cleanup(self) -> None: + # TODO: Cleanup monkey_dir and send telemetry pass diff --git a/monkey/infection_monkey/monkey.py b/monkey/infection_monkey/monkey.py index 76bcbdf02..4eb959129 100644 --- a/monkey/infection_monkey/monkey.py +++ b/monkey/infection_monkey/monkey.py @@ -4,316 +4,239 @@ import os import subprocess import sys import time -from threading import Thread import infection_monkey.tunnel as tunnel from common.utils.attack_utils import ScanStatus, UsageEnum -from common.utils.exceptions import ExploitingVulnerableMachineError, FailedExploitationError from common.version import get_version from infection_monkey.config import WormConfiguration from infection_monkey.control import ControlClient -from infection_monkey.exploit.HostExploiter import HostExploiter +from infection_monkey.master.mock_master import MockMaster from infection_monkey.model import DELAY_DELETE_CMD from infection_monkey.network.firewall import app as firewall -from infection_monkey.network.HostFinger import HostFinger -from infection_monkey.network.network_scanner import NetworkScanner -from infection_monkey.network.tools import get_interface_to_target, is_running_on_island -from infection_monkey.post_breach.post_breach_handler import PostBreach -from infection_monkey.ransomware.ransomware_payload_builder import build_ransomware_payload -from infection_monkey.system_info import SystemInfoCollector +from infection_monkey.network.tools import is_running_on_island +from infection_monkey.puppet.mock_puppet import MockPuppet from infection_monkey.system_singleton import SystemSingleton from infection_monkey.telemetry.attack.t1106_telem import T1106Telem from infection_monkey.telemetry.attack.t1107_telem import T1107Telem -from infection_monkey.telemetry.attack.victim_host_telem import VictimHostTelem -from infection_monkey.telemetry.scan_telem import ScanTelem +from infection_monkey.telemetry.messengers.legacy_telemetry_messenger_adapter import ( + LegacyTelemetryMessengerAdapter, +) from infection_monkey.telemetry.state_telem import StateTelem -from infection_monkey.telemetry.system_info_telem import SystemInfoTelem -from infection_monkey.telemetry.trace_telem import TraceTelem from infection_monkey.telemetry.tunnel_telem import TunnelTelem from infection_monkey.utils.environment import is_windows_os -from infection_monkey.utils.exceptions.planned_shutdown_exception import PlannedShutdownException -from infection_monkey.utils.monkey_dir import ( - create_monkey_dir, - get_monkey_dir_path, - remove_monkey_dir, -) +from infection_monkey.utils.monkey_dir import get_monkey_dir_path, remove_monkey_dir from infection_monkey.utils.monkey_log_path import get_monkey_log_path +from infection_monkey.utils.signal_handler import register_signal_handlers from infection_monkey.windows_upgrader import WindowsUpgrader -MAX_DEPTH_REACHED_MESSAGE = "Reached max depth, skipping propagation phase." - - logger = logging.getLogger(__name__) -class InfectionMonkey(object): +class InfectionMonkey: def __init__(self, args): - self._keep_running = False - self._exploited_machines = set() - self._fail_exploitation_machines = set() - self._singleton = SystemSingleton() - self._parent = None - self._default_tunnel = None - self._args = args - self._network = None - self._exploiters = None - self._fingerprint = None - self._default_server = None - self._default_server_port = None - self._opts = None - self._upgrading_to_64 = False - self._monkey_tunnel = None - self._post_breach_phase = None - - def initialize(self): logger.info("Monkey is initializing...") + self._master = MockMaster(MockPuppet(), LegacyTelemetryMessengerAdapter()) + self._singleton = SystemSingleton() + self._opts = self._get_arguments(args) + # TODO Used in propagation phase to set the default server for the victim + self._default_server_port = None + # TODO used in propogation phase + self._monkey_inbound_tunnel = None - if not self._singleton.try_lock(): - raise Exception("Another instance of the monkey is already running") - + @staticmethod + def _get_arguments(args): arg_parser = argparse.ArgumentParser() arg_parser.add_argument("-p", "--parent") arg_parser.add_argument("-t", "--tunnel") arg_parser.add_argument("-s", "--server") arg_parser.add_argument("-d", "--depth", type=int) arg_parser.add_argument("-vp", "--vulnerable-port") - self._opts, self._args = arg_parser.parse_known_args(self._args) - self.log_arguments() + opts, _ = arg_parser.parse_known_args(args) + InfectionMonkey._log_arguments(opts) + return opts - self._parent = self._opts.parent - self._default_tunnel = self._opts.tunnel - self._default_server = self._opts.server + @staticmethod + def _log_arguments(args): + arg_string = " ".join([f"{key}: {value}" for key, value in vars(args).items()]) + logger.info(f"Monkey started with arguments: {arg_string}") - if self._opts.depth is not None: + def start(self): + if self._is_another_monkey_running(): + logger.info("Another instance of the monkey is already running") + return + + logger.info("Monkey is starting...") + + self._set_propagation_depth(self._opts) + self._add_default_server_to_config(self._opts.server) + self._connect_to_island() + + # TODO: Reevaluate who is responsible to send this information + if is_windows_os(): + T1106Telem(ScanStatus.USED, UsageEnum.SINGLETON_WINAPI).send() + + if InfectionMonkey._is_monkey_alive_by_config(): + logger.info("Monkey marked 'not alive' from configuration.") + return + + if InfectionMonkey._is_upgrade_to_64_needed(): + self._upgrade_to_64() + logger.info("32 bit Agent can't run on 64 bit system.") + return + + self._setup() + self._master.start() + + @staticmethod + def _set_propagation_depth(options): + if options.depth is not None: WormConfiguration._depth_from_commandline = True - WormConfiguration.depth = self._opts.depth + WormConfiguration.depth = options.depth logger.debug("Setting propagation depth from command line") logger.debug(f"Set propagation depth to {WormConfiguration.depth}") - self._keep_running = True - self._network = NetworkScanner() - - if self._default_server: - if self._default_server not in WormConfiguration.command_servers: - logger.debug("Added default server: %s" % self._default_server) - WormConfiguration.command_servers.insert(0, self._default_server) + @staticmethod + def _add_default_server_to_config(default_server: str): + if default_server: + if default_server not in WormConfiguration.command_servers: + logger.debug("Added default server: %s" % default_server) + WormConfiguration.command_servers.insert(0, default_server) else: logger.debug( - "Default server: %s is already in command servers list" % self._default_server + "Default server: %s is already in command servers list" % default_server ) - def start(self): - try: - logger.info("Monkey is starting...") - - logger.debug("Starting the setup phase.") - - # Sets island's IP and port for monkey to communicate to - self.set_default_server() - self.set_default_port() - - # Create a dir for monkey files if there isn't one - create_monkey_dir() - - self.upgrade_to_64_if_needed() - - ControlClient.wakeup(parent=self._parent) - ControlClient.load_control_config() - - if is_windows_os(): - T1106Telem(ScanStatus.USED, UsageEnum.SINGLETON_WINAPI).send() - - self.shutdown_by_not_alive_config() - - if is_running_on_island(): - WormConfiguration.started_on_island = True - ControlClient.report_start_on_island() - - if not ControlClient.should_monkey_run(self._opts.vulnerable_port): - raise PlannedShutdownException( - "Monkey shouldn't run on current machine " - "(it will be exploited later with more depth)." - ) - - if firewall.is_enabled(): - firewall.add_firewall_rule() - - self._monkey_tunnel = ControlClient.create_control_tunnel() - if self._monkey_tunnel: - self._monkey_tunnel.start() - - StateTelem(is_done=False, version=get_version()).send() - TunnelTelem().send() - - logger.debug("Starting the post-breach phase asynchronously.") - self._post_breach_phase = Thread(target=self.start_post_breach_phase) - self._post_breach_phase.start() - - if not InfectionMonkey.max_propagation_depth_reached(): - logger.info("Starting the propagation phase.") - logger.debug("Running with depth: %d" % WormConfiguration.depth) - self.propagate() - else: - logger.info( - "Maximum propagation depth has been reached; monkey will not propagate." - ) - TraceTelem(MAX_DEPTH_REACHED_MESSAGE).send() - - if self._keep_running and WormConfiguration.alive: - InfectionMonkey.run_ransomware() - - # if host was exploited, before continue to closing the tunnel ensure the exploited - # host had its chance to - # connect to the tunnel - if len(self._exploited_machines) > 0: - time_to_sleep = WormConfiguration.keep_tunnel_open_time - logger.info( - "Sleeping %d seconds for exploited machines to connect to tunnel", time_to_sleep - ) - time.sleep(time_to_sleep) - - except PlannedShutdownException: - logger.info( - "A planned shutdown of the Monkey occurred. Logging the reason and finishing " - "execution." + def _connect_to_island(self): + # Sets island's IP and port for monkey to communicate to + if not self._is_default_server_set(): + raise Exception( + "Monkey couldn't find server with {} default tunnel.".format(self._opts.tunnel) ) - logger.exception("Planned shutdown, reason:") + self._set_default_port() - finally: - if self._monkey_tunnel: - self._monkey_tunnel.stop() - self._monkey_tunnel.join() - - if self._post_breach_phase: - self._post_breach_phase.join() - - def start_post_breach_phase(self): - self.collect_system_info_if_configured() - PostBreach().execute_all_configured() - - @staticmethod - def max_propagation_depth_reached(): - return 0 == WormConfiguration.depth - - def collect_system_info_if_configured(self): - logger.debug("Calling for system info collection") - try: - system_info_collector = SystemInfoCollector() - system_info = system_info_collector.get_info() - SystemInfoTelem(system_info).send() - except Exception as e: - logger.exception(f"Exception encountered during system info collection: {str(e)}") - - def shutdown_by_not_alive_config(self): - if not WormConfiguration.alive: - raise PlannedShutdownException("Marked 'not alive' from configuration.") - - def propagate(self): - ControlClient.keepalive() + ControlClient.wakeup(parent=self._opts.parent) ControlClient.load_control_config() - self._network.initialize() + def _is_default_server_set(self) -> bool: + """ + Sets the default server for the Monkey to communicate back to. + :return + """ + if not ControlClient.find_server(default_tunnel=self._opts.tunnel): + return False + self._opts.server = WormConfiguration.current_server + logger.debug("default server set to: %s" % self._opts.server) + return True - self._fingerprint = HostFinger.get_instances() + @staticmethod + def _is_monkey_alive_by_config(): + return not WormConfiguration.alive - self._exploiters = HostExploiter.get_classes() + @staticmethod + def _is_upgrade_to_64_needed(): + return WindowsUpgrader.should_upgrade() - if not WormConfiguration.alive: - logger.info("Marked not alive from configuration") + def _upgrade_to_64(self): + self._singleton.unlock() + logger.info("32bit monkey running on 64bit Windows. Upgrading.") + WindowsUpgrader.upgrade(self._opts) + logger.info("Finished upgrading from 32bit to 64bit.") - machines = self._network.get_victim_machines( - max_find=WormConfiguration.victims_max_find, - stop_callback=ControlClient.check_for_stop, - ) - for machine in machines: - if ControlClient.check_for_stop(): - break + def _setup(self): + logger.debug("Starting the setup phase.") - for finger in self._fingerprint: - logger.info( - "Trying to get OS fingerprint from %r with module %s", - machine, - finger.__class__.__name__, - ) - try: - finger.get_host_fingerprint(machine) - except BaseException as exc: - logger.error( - "Failed to run fingerprinter %s, exception %s" % finger.__class__.__name__, - str(exc), - ) - - ScanTelem(machine).send() - - # skip machines that we've already exploited - if machine in self._exploited_machines: - logger.debug("Skipping %r - already exploited", machine) - continue - - if self._monkey_tunnel: - self._monkey_tunnel.set_tunnel_for_host(machine) - if self._default_server: - if self._network.on_island(self._default_server): - machine.set_default_server( - get_interface_to_target(machine.ip_addr) - + (":" + self._default_server_port if self._default_server_port else "") - ) - else: - machine.set_default_server(self._default_server) - logger.debug( - "Default server for machine: %r set to %s" % (machine, machine.default_server) - ) - - # Order exploits according to their type - self._exploiters = sorted( - self._exploiters, key=lambda exploiter_: exploiter_.EXPLOIT_TYPE.value + if self._should_exit_for_performance(): + logger.info( + "Monkey shouldn't run on current machine to improve perfomance" + "(it will be exploited later with more depth)." ) - host_exploited = False - for exploiter in [exploiter(machine) for exploiter in self._exploiters]: - if self.try_exploiting(machine, exploiter): - host_exploited = True - VictimHostTelem("T1210", ScanStatus.USED, machine=machine).send() - if exploiter.RUNS_AGENT_ON_SUCCESS: - break # if adding machine to exploited, won't try other exploits - # on it - if not host_exploited: - self._fail_exploitation_machines.add(machine) - VictimHostTelem("T1210", ScanStatus.SCANNED, machine=machine).send() - if not self._keep_running: - break + return - if not WormConfiguration.alive: - logger.info("Marked not alive from configuration") + if firewall.is_enabled(): + firewall.add_firewall_rule() - def upgrade_to_64_if_needed(self): - if WindowsUpgrader.should_upgrade(): - self._upgrading_to_64 = True - self._singleton.unlock() - logger.info("32bit monkey running on 64bit Windows. Upgrading.") - WindowsUpgrader.upgrade(self._opts) - raise PlannedShutdownException("Finished upgrading from 32bit to 64bit.") + self._monkey_inbound_tunnel = ControlClient.create_control_tunnel() + if self._monkey_inbound_tunnel: + self._monkey_inbound_tunnel.start() + + StateTelem(is_done=False, version=get_version()).send() + TunnelTelem().send() + + register_signal_handlers(self._master) + + def _should_exit_for_performance(self): + """ + This method implements propagation performance enhancing algorithm that + kicks in if the run was started from the Island. + Should get replaced by other, better performance enhancement solutions + """ + if is_running_on_island(): + WormConfiguration.started_on_island = True + ControlClient.report_start_on_island() + + return not ControlClient.should_monkey_run(self._opts.vulnerable_port) + + def _is_another_monkey_running(self): + return not self._singleton.try_lock() + + def _set_default_port(self): + try: + self._default_server_port = self._opts.server.split(":")[1] + except KeyError: + self._default_server_port = "" def cleanup(self): logger.info("Monkey cleanup started") - self._keep_running = False + self._wait_for_exploited_machine_connection() + try: + if self._is_upgrade_to_64_needed(): + logger.debug("Cleanup not needed for 32 bit agent on 64 bit system(it didn't run)") + return + + if self._master: + self._master.cleanup() + + if self._monkey_inbound_tunnel: + self._monkey_inbound_tunnel.stop() + self._monkey_inbound_tunnel.join() + + if firewall.is_enabled(): + firewall.remove_firewall_rule() + firewall.close() + + InfectionMonkey._self_delete() + + InfectionMonkey._send_log() - if self._upgrading_to_64: - InfectionMonkey.close_tunnel() - firewall.close() - else: StateTelem( is_done=True, version=get_version() ).send() # Signal the server (before closing the tunnel) - InfectionMonkey.close_tunnel() - firewall.close() - self.send_log() - self._singleton.unlock() - InfectionMonkey.self_delete() + # TODO: Determine how long between when we + # send telemetry and the monkey actually exits + InfectionMonkey._close_tunnel() + self._singleton.unlock() + except Exception as e: + logger.error(f"An error occurred while cleaning up the monkey agent: {e}") + InfectionMonkey._self_delete() + logger.info("Monkey is shutting down") + def _wait_for_exploited_machine_connection(self): + # TODO check for actual exploitation + machines_exploited = False + # if host was exploited, before continue to closing the tunnel ensure the exploited + # host had its chance to + # connect to the tunnel + if machines_exploited: + time_to_sleep = WormConfiguration.keep_tunnel_open_time + logger.info( + "Sleeping %d seconds for exploited machines to connect to tunnel", time_to_sleep + ) + time.sleep(time_to_sleep) + @staticmethod - def close_tunnel(): + def _close_tunnel(): tunnel_address = ( ControlClient.proxies.get("https", "").replace("https://", "").split(":")[0] ) @@ -322,7 +245,18 @@ class InfectionMonkey(object): tunnel.quit_tunnel(tunnel_address) @staticmethod - def self_delete(): + def _send_log(): + monkey_log_path = get_monkey_log_path() + if os.path.exists(monkey_log_path): + with open(monkey_log_path, "r") as f: + log = f.read() + else: + log = "" + + ControlClient.send_log(log) + + @staticmethod + def _self_delete(): status = ScanStatus.USED if remove_monkey_dir() else ScanStatus.SCANNED T1107Telem(status, get_monkey_dir_path()).send() @@ -351,117 +285,3 @@ class InfectionMonkey(object): status = ScanStatus.SCANNED if status: T1107Telem(status, sys.executable).send() - - def send_log(self): - monkey_log_path = get_monkey_log_path() - if os.path.exists(monkey_log_path): - with open(monkey_log_path, "r") as f: - log = f.read() - else: - log = "" - - ControlClient.send_log(log) - - def try_exploiting(self, machine, exploiter): - """ - Workflow of exploiting one machine with one exploiter - :param machine: Machine monkey tries to exploit - :param exploiter: Exploiter to use on that machine - :return: True if successfully exploited, False otherwise - """ - if not exploiter.is_os_supported(): - logger.info( - "Skipping exploiter %s host:%r, os %s is not supported", - exploiter.__class__.__name__, - machine, - machine.os, - ) - return False - - logger.info( - "Trying to exploit %r with exploiter %s...", machine, exploiter.__class__.__name__ - ) - - result = False - try: - result = exploiter.exploit_host() - if result: - self.successfully_exploited(machine, exploiter, exploiter.RUNS_AGENT_ON_SUCCESS) - return True - else: - logger.info( - "Failed exploiting %r with exploiter %s", machine, exploiter.__class__.__name__ - ) - except ExploitingVulnerableMachineError as exc: - logger.error( - "Exception while attacking %s using %s: %s", - machine, - exploiter.__class__.__name__, - exc, - ) - self.successfully_exploited(machine, exploiter, exploiter.RUNS_AGENT_ON_SUCCESS) - return True - except FailedExploitationError as e: - logger.info( - "Failed exploiting %r with exploiter %s, %s", - machine, - exploiter.__class__.__name__, - e, - ) - except Exception as exc: - logger.exception( - "Exception while attacking %s using %s: %s", - machine, - exploiter.__class__.__name__, - exc, - ) - finally: - exploiter.send_exploit_telemetry(result) - return False - - def successfully_exploited(self, machine, exploiter, RUNS_AGENT_ON_SUCCESS=True): - """ - Workflow of registering successfully exploited machine - :param machine: machine that was exploited - :param exploiter: exploiter that succeeded - """ - if RUNS_AGENT_ON_SUCCESS: - self._exploited_machines.add(machine) - - logger.info("Successfully propagated to %s using %s", machine, exploiter.__class__.__name__) - - # check if max-exploitation limit is reached - if WormConfiguration.victims_max_exploit <= len(self._exploited_machines): - self._keep_running = False - - logger.info("Max exploited victims reached (%d)", WormConfiguration.victims_max_exploit) - - def set_default_port(self): - try: - self._default_server_port = self._default_server.split(":")[1] - except KeyError: - self._default_server_port = "" - - def set_default_server(self): - """ - Sets the default server for the Monkey to communicate back to. - :raises PlannedShutdownException if couldn't find the server. - """ - if not ControlClient.find_server(default_tunnel=self._default_tunnel): - raise PlannedShutdownException( - "Monkey couldn't find server with {} default tunnel.".format(self._default_tunnel) - ) - self._default_server = WormConfiguration.current_server - logger.debug("default server set to: %s" % self._default_server) - - def log_arguments(self): - arg_string = " ".join([f"{key}: {value}" for key, value in vars(self._opts).items()]) - logger.info(f"Monkey started with arguments: {arg_string}") - - @staticmethod - def run_ransomware(): - try: - ransomware_payload = build_ransomware_payload(WormConfiguration.ransomware) - ransomware_payload.run_payload() - except Exception as ex: - logger.error(f"An unexpected error occurred while running the ransomware payload: {ex}") diff --git a/monkey/infection_monkey/puppet/mock_puppet.py b/monkey/infection_monkey/puppet/mock_puppet.py index 6996d4d7c..3a32f3718 100644 --- a/monkey/infection_monkey/puppet/mock_puppet.py +++ b/monkey/infection_monkey/puppet/mock_puppet.py @@ -220,17 +220,48 @@ class MockPuppet(IPuppet): self, name: str, host: str, options: Dict, interrupt: threading.Event ) -> ExploiterResultData: logger.debug(f"exploit_hosts({name}, {host}, {options})") + attempts = [ + { + "result": False, + "user": "Administrator", + "password": "", + "lm_hash": "", + "ntlm_hash": "", + "ssh_key": host, + }, + { + "result": False, + "user": "root", + "password": "", + "lm_hash": "", + "ntlm_hash": "", + "ssh_key": host, + }, + ] + info_powershell = { + "display_name": "PowerShell", + "started": "2021-11-25T15:57:06.307696", + "finished": "2021-11-25T15:58:33.788238", + "vulnerable_urls": [], + "vulnerable_ports": [], + "executed_cmds": [ + { + "cmd": "/tmp/monkey m0nk3y -s 10.10.10.10:5000 -d 1 >git s /dev/null 2>&1 &", + "powershell": True, + } + ], + } + info_ssh = { + "display_name": "SSH", + "started": "2021-11-25T15:57:06.307696", + "finished": "2021-11-25T15:58:33.788238", + "vulnerable_urls": [], + "vulnerable_ports": [22], + "executed_cmds": [], + } successful_exploiters = { - DOT_1: { - "PowerShellExploiter": ExploiterResultData( - True, {"info": "important success stuff"}, ["attempt 1"] - ) - }, - DOT_3: { - "SSHExploiter": ExploiterResultData( - False, {"info": "important failure stuff"}, ["attempt 2"] - ) - }, + DOT_1: {"PowerShellExploiter": ExploiterResultData(True, info_powershell, attempts)}, + DOT_3: {"SSHExploiter": ExploiterResultData(False, info_ssh, attempts)}, } return successful_exploiters[host][name] diff --git a/monkey/infection_monkey/utils/exceptions/planned_shutdown_error.py b/monkey/infection_monkey/utils/exceptions/planned_shutdown_error.py new file mode 100644 index 000000000..885340c23 --- /dev/null +++ b/monkey/infection_monkey/utils/exceptions/planned_shutdown_error.py @@ -0,0 +1,2 @@ +class PlannedShutdownError(Exception): + pass diff --git a/monkey/infection_monkey/utils/exceptions/planned_shutdown_exception.py b/monkey/infection_monkey/utils/exceptions/planned_shutdown_exception.py deleted file mode 100644 index f0147e1e5..000000000 --- a/monkey/infection_monkey/utils/exceptions/planned_shutdown_exception.py +++ /dev/null @@ -1,2 +0,0 @@ -class PlannedShutdownException(Exception): - pass diff --git a/monkey/infection_monkey/utils/signal_handler.py b/monkey/infection_monkey/utils/signal_handler.py index 87d965398..6fda3bc12 100644 --- a/monkey/infection_monkey/utils/signal_handler.py +++ b/monkey/infection_monkey/utils/signal_handler.py @@ -3,7 +3,7 @@ import signal from infection_monkey.i_master import IMaster from infection_monkey.utils.environment import is_windows_os -from infection_monkey.utils.exceptions.planned_shutdown_exception import PlannedShutdownException +from infection_monkey.utils.exceptions.planned_shutdown_error import PlannedShutdownError logger = logging.getLogger(__name__) @@ -16,7 +16,7 @@ class StopSignalHandler: self._handle_signal(signum) # Windows signal handlers must return boolean. Only raising this exception for POSIX # signals. - raise PlannedShutdownException("Monkey Agent got an interrupt signal") + raise PlannedShutdownError("Monkey Agent got an interrupt signal") def handle_windows_signals(self, signum): import win32con