def __init__(self, source_host, port=50000, target=None): """ Creates a metasploit class to connect and use metasploit functionality. Args: source_host (str): the host that uses metasploit. port (int): on which port the msfrpc listens to. target (str): target host to perform actions. Raises: MsfrpcdConnectionError: in case metasploit connection attempt has failed. InvalidHostName: in case the provided target host name is invalid. """ self._source_host = source_host try: if target: self._target_host = socket.gethostbyname(target) self._ssh = SSH(hostname=source_host) ping_cmd = f"ping -c3 {self._target_host}" is_ping_success, _ = self._ssh.execute_commands( commands=[ping_cmd]) if not is_ping_success: raise HostIsUnreachable(source_host=self._source_host, target_host=self._target_host) except socket.gaierror: raise InvalidHostName(invalid_host=target) try: self._metasploit = Metasploit(server=source_host, port=port) except ConnectionError: raise MsfrpcdConnectionError(host=source_host, port=port)
def __init__(self, docker_server_ip, protocol='tcp', docker_port=2375): """ Initialize the connection to the docker server over an AWS ec2 instance using a chosen protocol, docker server ip and a port that the docker server listens to. Args: docker_server_ip (str): the docker server public ip. protocol (str): a protocol to use in order to connect to the docker server. etc: tcp/udp. docker_port (int): the port that the docker server listens to. """ base_url = f"{protocol}://{docker_server_ip}:{docker_port}" ssh = None connection_attempts = 0 while connection_attempts < 5: try: self._docker_client = docker.DockerClient(base_url=base_url) self._api_client = docker.APIClient(base_url=base_url) self._docker_client.ping() break except ConnectionError: if not ssh: ssh = SSH(hostname=docker_server_ip) are_commands_successful, cmd = ssh.execute_commands(commands=aws_const.RELOAD_DOCKER_DAEMON) if not are_commands_successful: raise CommandFailureError(cmd=cmd, instance_fqdn=docker_server_ip) connection_attempts += 1 if connection_attempts >= 5: raise DockerServerConnectionError(docker_server=docker_server_ip, url=base_url)
def __init__(self, instance_obj, ssh_flag=False, init_docker_server_flag=False): """ Args: instance_obj (Aws.Instance): Aws instance obj. ssh_flag (bool): indicate if the instance requires a SSH connection. True to open connection, False otherwise. init_docker_server_flag (bool): indicate if the instance needs to be configured with a docker server. True to deploy docker server, False means it's already deployed. """ self._instance_obj = instance_obj if ssh_flag: self._ssh = SSH(hostname=self.public_dns_name) if init_docker_server_flag: self._init_docker_server_on_instance() self._docker = Docker(docker_server_ip=self.public_dns_name)
def stop_docker_daemon(docker_server_dns): """ Stops the docker servers docker daemon. """ stop_docker_daemon_cmd = ["sudo systemctl stop docker"] for docker_dns in docker_server_dns: logger.info(f"Stop docker daemon at {docker_dns}") is_cmd_success, cmd = SSH(hostname=docker_dns).execute_commands(commands=stop_docker_daemon_cmd) assert is_cmd_success, f"Failed to stop docker daemon at {docker_dns} using cmd {cmd}"
def __init__(self, docker_server_ip, protocol='tcp', docker_port=2375, max_connection_attempts=5): """ Initialize the connection to the docker server over an AWS ec2 instance using a chosen protocol, docker server ip and a port that the docker server listens to. Args: docker_server_ip (str): the docker server public ip. protocol (str): a protocol to use in order to connect to the docker server. etc: tcp/udp. docker_port (int): the port that the docker server listens to. max_connection_attempts (int): maximum times to try and connect the docker API. """ base_url = f"{protocol}://{docker_server_ip}:{docker_port}" is_docker_daemon_recovered = False ssh = None connection_attempts = 0 while connection_attempts < max_connection_attempts: try: self._docker_client = docker.DockerClient(base_url=base_url) self._api_client = docker.APIClient(base_url=base_url) if self._docker_client.ping(): if is_docker_daemon_recovered: recover_containers(containers=self._docker_client.containers.list(all=True)) break except (ConnectionError, DockerException): if not ssh: ssh = SSH(hostname=docker_server_ip) are_commands_successful, cmd = ssh.execute_commands(commands=aws_const.RELOAD_DOCKER_DAEMON) if not are_commands_successful: raise CommandFailureError(cmd=cmd, instance_fqdn=docker_server_ip) connection_attempts += 1 is_docker_daemon_recovered = True if connection_attempts >= max_connection_attempts: raise DockerServerConnectionError(docker_server=docker_server_ip, url=base_url)
class DockerServerInstance(object): """ This class represents an instance in AWS with a docker server configured. Attributes: _instance_obj (Aws.Instance): a aws instance obj. _ssh (SSH): a SSH client that opens a connection to the instance. _docker (Docker): a docker class that represents docker-container over an instance. """ def __init__(self, instance_obj, ssh_flag=False, init_docker_server_flag=False): """ Args: instance_obj (Aws.Instance): Aws instance obj. ssh_flag (bool): indicate if the instance requires a SSH connection. True to open connection, False otherwise. init_docker_server_flag (bool): indicate if the instance needs to be configured with a docker server. True to deploy docker server, False means it's already deployed. """ self._instance_obj = instance_obj if ssh_flag: self._ssh = SSH(hostname=self.public_dns_name) if init_docker_server_flag: self._init_docker_server_on_instance() self._docker = Docker(docker_server_ip=self.public_dns_name) @property def docker(self): return self._docker @property def instance_id(self): return self._instance_obj.instance_id @property def public_ip_address(self): return self._instance_obj.public_ip_address @property def public_dns_name(self): return self._instance_obj.public_dns_name @property def state(self): return self._instance_obj.state @property def key_name(self): return self._instance_obj.key_name @property def security_groups(self): return self._instance_obj.security_groups @property def image_id(self): return self._instance_obj.image_id @property def private_ip_address(self): return self._instance_obj.private_ip_address @property def private_dns_name(self): return self._instance_obj.private_dns_name @property def instance_obj(self): return self._instance_obj def _reload(self): self._instance_obj.reload() def start(self): """ Start the instance and wait till is is on running state """ if self.state['Name'] == aws_constants.STOPPED_STATE: self._instance_obj.start() self._instance_obj.wait_until_running() self._reload() def stop(self): """ Stop the instance and wait till it's in a stopped state """ if self.state['Name'] == aws_constants.RUNNING_STATE: self._instance_obj.stop() self._instance_obj.wait_until_stopped() self._reload() def reboot(self): """ Reboot the instance and wait till it's in a running state """ if self.state['Name'] == aws_constants.RUNNING_STATE: self._instance_obj.reboot() self._instance_obj.wait_until_running() self._reload() def terminate(self): """ Terminate the instance and delete the current instance """ self._instance_obj.terminate() def execute_shell_commands(self, commands): """ Executes the given commands over the instance. Args: commands (list(str)) - list of all the commands to execute on the instance. Raises: CommandFailureError in case the command fails over the instance """ if self.state['Name'] == aws_constants.RUNNING_STATE: are_commands_successful, cmd = self._ssh.execute_commands(commands=commands) if not are_commands_successful: raise CommandFailureError( cmd=cmd, instance_fqdn=self.public_dns_name ) def write_to_file(self, filename, mode, data=None): """ Write to a file in the instance. Args: filename (str): the exact file path. etc: /etc/docker/my_file. mode (str): read or write on the file etc ("w" for write, "r" for read). data (str): which data should be written on the file. Returns: True if the write operation is success, False otherwise. """ sftp = self._ssh.get_sftp() file_obj = sftp.open(filename=filename, mode=mode) if mode == 'w': file_obj.write(data=data) def _init_docker_server_on_instance(self): """ Initialize the docker server over an instance in AWS. """ self.execute_shell_commands( commands=aws_constants.MAKE_DOCKER_FILES_COMMANDS ) self.write_to_file( filename='/etc/docker/daemon.json', mode='w', data='{"hosts": ["tcp://0.0.0.0:2375", "unix:///var/run/docker.sock"]}\n' ) self.write_to_file( filename='/etc/systemd/system/docker.service.d/override.conf', mode='w', data='[Service]\nExecStart=\nExecStart=/usr/bin/dockerd\n' ) self.execute_shell_commands( commands=aws_constants.RELOAD_DOCKER_DAEMON )
class MetasploitModule(object): def __init__(self, source_host, port=50000, target=None): """ Creates a metasploit class to connect and use metasploit functionality. Args: source_host (str): the host that uses metasploit. port (int): on which port the msfrpc listens to. target (str): target host to perform actions. Raises: MsfrpcdConnectionError: in case metasploit connection attempt has failed. InvalidHostName: in case the provided target host name is invalid. """ self._source_host = source_host try: if target: self._target_host = socket.gethostbyname(target) self._ssh = SSH(hostname=source_host) ping_cmd = f"ping -c3 {self._target_host}" is_ping_success, _ = self._ssh.execute_commands( commands=[ping_cmd]) if not is_ping_success: raise HostIsUnreachable(source_host=self._source_host, target_host=self._target_host) except socket.gaierror: raise InvalidHostName(invalid_host=target) try: self._metasploit = Metasploit(server=source_host, port=port) except ConnectionError: raise MsfrpcdConnectionError(host=source_host, port=port) def init_module(self, module_name, module_type): """ initializes the requested module of the metasploit. Args: module_name (str): module name. module_type (str): module type. Returns: MsfModule: a msfmodule object. e.g. ExploitModule, AuxiliaryModule, PayloadModule. """ return self._metasploit.modules.use(mtype=module_type, mname=module_name) def execute_shell_commands(self, commands, session_id): """ Executes shell commands on a remote host that we gained a remote shell to. Args: commands (list(str)): a list of commands to execute. session_id (str): session ID of the required shell. Yields: str: output of a command that was executed in the remote shell. """ shell = self._metasploit.metasploit_client.sessions.session( sid=session_id) for cmd in commands: shell.write(data=cmd) yield shell.read() def execute_host_console_commands(self, commands, timeout=90, sleep=3): """ executes host console commands in msf console. Args: commands (list(str)): set of commands to be done on the msf console. timeout (int): timeout limit to sample the duration of each command. sleep (int): sleep time between each sampling of the console status for every command. Yields: str: data output from the console of each command. """ console = self._metasploit.host_console for cmd in commands: console.write(command=cmd) try: for is_busy in TimeoutSampler(timeout=timeout, sleep=sleep, func=console.is_busy): if not is_busy: break except TimeoutExpiredError: pass yield console.read()["data"] self._metasploit.destory_console() def job_info(self, job_id): """ Gets information about an existing job in metasploit, ignores jobs that produce errors. Args: job_id (int): job ID. Returns: dict: job information in case exists, empty dict otherwise. """ if str(job_id) in self._metasploit.metasploit_client.jobs.list: job_details = self._metasploit.metasploit_client.jobs.info( jobid=job_id) if "error" not in job_details: return job_details return {} def stop_job(self, job_id): """ Stops a job performed by metasploit. Args: job_id (int): job ID. Returns: bool: True if job was stopped successfully, False otherwise. """ self._metasploit.metasploit_client.jobs.stop(jobid=job_id) return True if str( job_id ) not in self._metasploit.metasploit_client.jobs.list else False def build_module(self, name, options, type='payload'): """ Builds up the MsfModule along with the options for the module. Args: name (str): module name. options (dict): module options. type (str): module type. Returns: MsfModule: a msfmodule object. e.g. ExploitModule, PayloadModule. """ msf_module = self.init_module(module_name=name, module_type=type) for option, option_value in options.items(): msf_module[option] = option_value return msf_module def execute(self, *args, **kwargs): """ Base method to execute metasploit modules. """ pass def info(self, *args, **kwargs): """ Base method to collect information about metasploit modules. """ pass