From dc9e2c6bc796a5a13e782155101f41f75f27c12d Mon Sep 17 00:00:00 2001 From: ghost-ng Date: Wed, 27 Dec 2023 14:07:43 -0500 Subject: [PATCH] fixed download upload paths, refactorred dce transport, fixed cli cmds, fixed portfwd rules --- TODO.txt | 17 +-- main.py | 244 +++++------------------------- slinger/lib/dcetransport.py | 54 ++++++- slinger/lib/schtasks.py | 88 ++++++----- slinger/lib/scm.py | 167 +++++++++++++++----- slinger/lib/smblib.py | 188 ++++++++++++++++++++--- slinger/lib/winreg.py | 147 ++++++++++++------ slinger/plugins/my_plugin copy.py | 35 +++++ slinger/plugins/my_plugin.py | 2 + slinger/slingerclient.py | 89 +++++++---- slinger/utils/cli.py | 116 ++++++++++---- slinger/utils/common.py | 12 +- 12 files changed, 737 insertions(+), 422 deletions(-) create mode 100644 slinger/plugins/my_plugin copy.py diff --git a/TODO.txt b/TODO.txt index 9bb8547..1d6f3f5 100755 --- a/TODO.txt +++ b/TODO.txt @@ -9,21 +9,10 @@ task scheduler registry - uptime -- processes service control +- sc modify -- sc delete -- sc add - -factor encoding -if sys.stdout.encoding is None: - # Output is redirected to a file - sys.stdout = codecs.getwriter('utf8')(sys.stdout) - -- properly disconnect dce -- move None checks for transport to function -- clean up unused imports - -fix error reporting \ No newline at end of file +command chaining +run slinger script file (from folder) \ No newline at end of file diff --git a/main.py b/main.py index b4ae057..3e2922b 100755 --- a/main.py +++ b/main.py @@ -1,16 +1,14 @@ from slinger.utils.printlib import * from slinger.slingerclient import SlingerClient from slinger.utils.common import * -from slinger.utils.cli import setup_cli_parser, get_prompt, CommandCompleter, setup_completer, merge_parsers, force_help +from slinger.utils.cli import setup_cli_parser, get_prompt, CommandCompleter, setup_completer, merge_parsers, force_help, get_subparser_aliases import shlex, argparse, sys, os, pty, termios from prompt_toolkit import PromptSession from prompt_toolkit.history import FileHistory from prompt_toolkit.formatted_text import to_formatted_text, ANSI from prompt_toolkit.completion import Completer, Completion - from slinger.lib.plugin_base import load_plugins from slinger.var.config import version -from slinger.utils.cli import extract_commands_and_args plugins_folder = "slinger/plugins" @@ -51,6 +49,7 @@ def main(): parser.add_argument('--nojoy', action='store_true', help='Turn off emojis') parser.add_argument('--ntlm', help='NTLM hash for authentication') parser.add_argument('--kerberos', action='store_true', help='Use Kerberos for authentication') + parser.add_argument('--debug', action='store_true', help='Turn on debug output') if len(sys.argv) == 1: print(banner_art) @@ -58,9 +57,11 @@ def main(): sys.exit(1) prgm_args = parser.parse_args() + if prgm_args.debug: + set_config_value('debug', True) slingerClient = SlingerClient(prgm_args.host, prgm_args.username, prgm_args.password, prgm_args.domain, prgm_args.port, prgm_args.ntlm, prgm_args.kerberos) - slinger_parser = setup_cli_parser() + slinger_parser = setup_cli_parser(slingerClient) plugins = load_plugins(plugins_folder, slingerClient) # merge all parsers from the plugins into the main parser for plugin in plugins: @@ -107,9 +108,11 @@ def main(): #user_input = input(prompt_text) split = shlex.split(user_input) args = slinger_parser.parse_args(split) + if hasattr(args, 'func'): + args.func(args) #print(args) except (argparse.ArgumentError, ValueError): - print_debug(str(e)) + print_debug('',sys.exc_info()) print_warning("Failed to parse command. Try quoting your arguments.") print_log(sys.exc_info()) continue @@ -127,7 +130,7 @@ def main(): except SystemExit: continue except Exception as e: - print_debug(str(e)) + print_debug(str(e), sys.exc_info()) print_bad(f"Error: {e}: {sys.exc_info()}") continue except argparse.ArgumentError as e: @@ -147,8 +150,8 @@ def main(): print_warning("Invalid arguments. Usage: set ") elif args.command == "config": show_config() - elif args.command == "info": - slingerClient.info() + #elif args.command == "info": + # slingerClient.info() elif args.command == '#shell': os.system('clear') print_info("You're in local terminal mode. Type exit to return.") @@ -178,204 +181,39 @@ def main(): elif args.command == "exit": slingerClient.exit() break - elif args.command == "enumservices" or args.command == "servicesenum": - slingerClient.enum_services(args.new, args.filter) - elif args.command == 'enumdisk': - slingerClient.enum_server_disk() - elif args.command == "enumtime": - slingerClient.get_server_time() - elif args.command == "enuminfo": - slingerClient.enum_info() - elif args.command == "enumlogons": - slingerClient.enum_logons() - elif args.command == "enumtransport": - slingerClient.enum_transport() - elif args.command == "enumsys": - slingerClient.enum_sys() - elif args.command == "enumtasks" or args.command == "tasksenum": - slingerClient.enum_folders_recursive("\\") - elif args.command == "serviceshow" or args.command == "svcshow": - if not slingerClient.services_list and args.serviceid: - print_warning("No services have been enumerated. Run enumservices first.") - continue - else: - service_arg = args.serviceid if args.serviceid else args.service_name - slingerClient.view_service_details(service_arg) - elif args.command == "servicestart" or args.command == "svcstart": - if not slingerClient.services_list and args.serviceid: - print_warning("No services have been enumerated. Run enumservices first.") - continue - else: - service_arg = args.serviceid if args.serviceid else args.service_name - slingerClient.start_service(service_arg) - elif args.command == "servicestop" or args.command == "svcstop": - if not slingerClient.services_list and args.serviceid: - print_warning("No services have been enumerated. Run enumservices first.") - continue - else: - service_arg = args.serviceid if args.serviceid else args.service_name - slingerClient.stop_service(service_arg) - - elif args.command == "taskcreate" or args.command == "taskadd": - slingerClient.task_create(args.name, args.program, args.arguments, args.folder) - elif args.command == "taskrun" or args.command == "taskexec": - slingerClient.task_run(args.task_path) - elif args.command in ["taskdelete", "taskdel", "taskrm"]: - if not slingerClient.folder_list_dict and args.taskid: - print_warning("No tasks have been enumerated. Run enumtasks first.") - continue - else: - task_arg = args.taskid if args.taskid else args.task_path - slingerClient.task_delete(task_arg) - elif args.command == "tasksshow" or args.command == "taskshow": - if not slingerClient.folder_list_dict and args.taskid: - print_warning("No tasks have been enumerated. Run enumtasks first.") - continue - else: - if args.folder: - slingerClient.view_tasks_in_folder(args.folder) - else: - task_arg = args.taskid if args.taskid else args.task_path - slingerClient.view_task_details(task_arg) - elif args.command == "who": - slingerClient.who() - elif args.command == "stats": - slingerClient.server_stats() - elif args.command== "upload" or args.command == "put": - remote_path = "" - if slingerClient.check_if_connected(): - if args.remote_path == "." or args.remote_path == "" or args.remote_path is None: - remote_path = os.path.basename(args.local_path) - else: - remote_path = args.remote_path - if os.path.exists(args.local_path): - print_info(f"Uploading: {args.local_path} --> {slingerClient.share}\\{remote_path}") - slingerClient.upload(args.local_path, remote_path) - else: - print_warning(f"Local path {args.local_path} does not exist.") - elif args.command == "download" or args.command == "get": - # handle if remote_path is a file name (relative to current path) - remote_path = os.path.normpath(os.path.join(slingerClient.relative_path, args.remote_path)) - local_path = "" - if slingerClient.check_if_connected(): - if not slingerClient.file_exists(args.remote_path): - print_warning(f"Remote file {args.remote_path} does not exist.") - continue - if args.local_path == "." or args.local_path == "" or args.local_path is None: - local_path = os.path.join(os.getcwd(), os.path.basename(args.remote_path)) - else: - local_path = args.local_path - if os.path.isdir(os.path.dirname(local_path)): - print_info(f"Downloading: {slingerClient.share}\\{remote_path} --> {local_path}") - slingerClient.download(remote_path, local_path) - else: - print_warning(f"Local path {args.local_path} does not exist.") - - elif args.command == "mget": - if slingerClient.check_if_connected(): - remote_path = args.remote_path if args.remote_path else slingerClient.relative_path - if slingerClient.is_valid_directory(remote_path): - local_path = args.local_path if args.local_path else os.getcwd() - slingerClient.mget(remote_path, local_path, args.r, args.p, args.d) - else: - print_log(f"Remote directory {remote_path} does not exist.") - elif args.command == "rm": - if slingerClient.check_if_connected(): - if slingerClient.file_exists(args.remote_path): - slingerClient.delete(args.remote_path) - else: - print_warning(f"Remote file {args.remote_path} does not exist.") - elif args.command == "use": - slingerClient.connect_share(args.share) - elif args.command == "ls": - if slingerClient.check_if_connected(): - slingerClient.dir_list(args.path) - elif args.command == "mkdir": - if slingerClient.check_if_connected(): - slingerClient.mkdir(args.path) - elif args.command == 'cat': - if slingerClient.check_if_connected(): - slingerClient.cat(args.remote_path) - elif args.command == 'rmdir': - if slingerClient.check_if_connected(): - slingerClient.rmdir(args.remote_path) - elif args.command == "shares" or args.command == "enumshares": - slingerClient.list_shares() - elif args.command == "cd": - if slingerClient.check_if_connected(): - if args.path: - slingerClient.cd(args.path) - elif args.command == "cd": - slingerClient.print_current_path() - elif args.command == "pwd": - if slingerClient.check_if_connected(): - slingerClient.print_current_path() - elif args.command == "reguse": - slingerClient.setup_remote_registry() - elif args.command == "regstart": - slingerClient.setup_remote_registry() - elif args.command == "regstop": - slingerClient.stop_remote_registry() - elif args.command == "regquery": - if args.list: - slingerClient.enum_subkeys(args.key) - elif args.value: - slingerClient.enum_key_value(args.key) - elif args.key: - try: - slingerClient.enum_key_value(args.key) - slingerClient.enum_subkeys(args.key) - except Exception as e: - if "ERROR_FILE_NOT_FOUND" in str(e): - print_bad("Registry key does not exist") - continue - elif args.command == "regset": - slingerClient.add_reg_value(args.key, args.value, args.data, args.type) - elif args.command == "regdel": - if args.value: - slingerClient.del_reg_key_value(args.key, args.value) - elif args.key: - slingerClient.reg_delete_key(args.key) - else: - print_warning("Invalid arguments. Usage: regdel -k [-v ]") - elif args.command == "portfwd": - if args.list: - slingerClient.print_portfwd_rules() - elif args.remove: - pass - slingerClient.del_port_fwd_rule(args.local) - elif args.add: - slingerClient.add_port_fwd_rule(args.local, args.remote) - elif args.command == "portfwdrules": - if args.load: - slingerClient.load_port_fwd_rules() - else: - slingerClient.print_portfwd_rules() - elif args.command == "regcreate": - slingerClient.reg_create_key(args.key) - elif args.command == "regcheck": - slingerClient.does_key_exist(args.key) - elif args.command == "fwrules": - slingerClient.show_fw_rules() - elif args.command == "ifconfig" or args.command == "ipconfig": - slingerClient.ipconfig() - elif args.command == "hostname": - slingerClient.hostname() + # elif args.command== "upload" or args.command == "put": + # remote_path = "" + # if slingerClient.check_if_connected(): + # if args.remote_path == "." or args.remote_path == "" or args.remote_path is None: + # remote_path = os.path.basename(args.local_path) + # else: + # remote_path = args.remote_path + # if os.path.exists(args.local_path): + # print_info(f"Uploading: {args.local_path} --> {slingerClient.share}\\{remote_path}") + # slingerClient.upload(args.local_path, remote_path) + # else: + # print_warning(f"Local path {args.local_path} does not exist.") + + # elif args.command == "download" or args.command == "get": + # # handle if remote_path is a file name (relative to current path) + # remote_path = os.path.normpath(os.path.join(slingerClient.relative_path, args.remote_path)) + # local_path = "" + # if slingerClient.check_if_connected(): + # if not slingerClient.file_exists(args.remote_path): + # print_warning(f"Remote file {args.remote_path} does not exist.") + # continue + # if args.local_path == "." or args.local_path == "" or args.local_path is None: + # local_path = os.path.join(os.getcwd(), os.path.basename(args.remote_path)) + # else: + # local_path = args.local_path + # if os.path.isdir(os.path.dirname(local_path)): + # print_info(f"Downloading: {slingerClient.share}\\{remote_path} --> {local_path}") + # slingerClient.download(remote_path, local_path) + # else: + # print_warning(f"Local path {args.local_path} does not exist.") else: - try: - for plugin in plugins: - p = plugin.get_parser() - cmds = extract_commands_and_args(p) - if args.command in cmds: - plugin.run(args) - except Exception as e: - print_debug(str(e)) - print_warning(f"Error: {e}: {sys.exc_info()}") - continue - pass - #parser.print_help() except Exception as e: print_warning(f"Uncaught Error: {e}: {sys.exc_info()}") print_debug('',sys.exc_info()) diff --git a/slinger/lib/dcetransport.py b/slinger/lib/dcetransport.py index d000014..3f66e6a 100755 --- a/slinger/lib/dcetransport.py +++ b/slinger/lib/dcetransport.py @@ -299,8 +299,7 @@ def _start_service(self, service_name, bind=True): if "rpc_s_access_denied" in str(e): print_bad("Unable to connect to service, access denied") else: - print_bad("An error occurred:") - traceback.print_exc() + raise e return serviceHandle = ans['lpServiceHandle'] response = scmr.hRStartServiceW(self.dce, serviceHandle) @@ -322,8 +321,8 @@ def _stop_service(self, service_name): if "rpc_s_access_denied" in str(e): print_bad("Unable to connect to service, access denied") else: - print_bad("An error occurred:") - traceback.print_exc() + print_bad("An error occurred: " + str(e)) + print_debug('', sys.exc_info()) return serviceHandle = ans['lpServiceHandle'] response = scmr.hRControlService(self.dce, serviceHandle, scmr.SERVICE_CONTROL_STOP) @@ -355,6 +354,51 @@ def _checkServiceStatus(self, serviceName): else: raise Exception('Unknown service state 0x%x - Aborting' % ans['CurrentState']) + def _delete_service(self, service_name): + if not self.is_connected: + raise Exception("Not connected to remote host") + self.bind_override = True + self._bind(scmr.MSRPC_UUID_SCMR) + ans = scmr.hROpenSCManagerW(self.dce) + self.scManagerHandle = ans['lpScHandle'] + try: + ans = scmr.hROpenServiceW(self.dce, self.scManagerHandle, service_name + '\x00') + except Exception as e: + if "rpc_s_access_denied" in str(e): + print_bad("Unable to connect to service, access denied") + else: + print_bad("An error occurred: " + str(e)) + print_debug('', sys.exc_info()) + return + serviceHandle = ans['lpServiceHandle'] + response = scmr.hRDeleteService(self.dce, serviceHandle) + self._close_scm_handle(serviceHandle) + return response + + def _create_service(self, service_name, bin_path, start_type, display_name=None): + if not self.is_connected: + raise Exception("Not connected to remote host") + self.bind_override = True + self._bind(scmr.MSRPC_UUID_SCMR) + ans = scmr.hROpenSCManagerW(self.dce) + self.scManagerHandle = ans['lpScHandle'] + if display_name is None: + display_name = service_name + + try: + if start_type == 'auto': + start_type = scmr.SERVICE_AUTO_START + elif start_type == 'demand': + start_type = scmr.SERVICE_DEMAND_START + elif start_type == 'system': + start_type = scmr.SERVICE_SYSTEM_START + except AttributeError: + start_type = scmr.SERVICE_DEMAND_START + + response = scmr.hRCreateServiceW(self.dce, self.scManagerHandle, service_name, display_name, + dwServiceType=scmr.SERVICE_WIN32_OWN_PROCESS, dwErrorControl=scmr.SERVICE_ERROR_IGNORE, lpBinaryPathName=bin_path, dwStartType=start_type) + return response + def _get_root_key(self, keyName): # Let's strip the root key try: @@ -398,7 +442,7 @@ def _enum_subkeys(self, keyName, bind=True): i += 1 except Exception as e: - print_debug(str(e)) + print_debug(str(e), sys.exc_info()) break return subkeys diff --git a/slinger/lib/schtasks.py b/slinger/lib/schtasks.py index c8d6f15..2a29bf4 100755 --- a/slinger/lib/schtasks.py +++ b/slinger/lib/schtasks.py @@ -23,8 +23,7 @@ def __init__(self): def enum_folders_old(self, folder_path="\\", start_index=0): - if self.dce_transport is None: - self.dce_transport = DCETransport(self.host, self.username, self.port, self.conn) + self.setup_dce_transport() self.dce_transport._connect('atsvc') # Call SchRpcEnumFolders @@ -43,9 +42,7 @@ def enum_folders_old(self, folder_path="\\", start_index=0): if folder_name is not None and full_folder_path not in self.folder_list: print_info("Found Folder: " + full_folder_path) self.folder_list.append(full_folder_path) - - #print_info("Found Folders: " + ' '.join(self.folder_list)) - #self.enum_folders_recursive(self.folder_list, start_index) + def enum_folders(self, folder_path="\\", start_index=0): """ @@ -58,8 +55,7 @@ def enum_folders(self, folder_path="\\", start_index=0): Returns: None """ - if self.dce_transport is None: - self.dce_transport = DCETransport(self.host, self.username, self.port, self.conn) + self.setup_dce_transport() self.dce_transport._connect('atsvc') response = self.dce_transport._enum_folders(folder_path, start_index) if response['ErrorCode'] == 0: @@ -75,7 +71,7 @@ def enum_folders(self, folder_path="\\", start_index=0): else: return - def enum_folders_recursive(self, folder="\\", start_index=0): + def enum_task_folders_recursive(self, args): """ Enumerates the Task Scheduler folders recursively. @@ -83,6 +79,8 @@ def enum_folders_recursive(self, folder="\\", start_index=0): folder (str): The folder to start the enumeration from. Defaults to "\\". start_index (int): The starting index for the enumeration. Defaults to 0. """ + folder="\\" + start_index=0 print_info("Enumerating Task Scheduler...") self.folder_list = ["\\"] self.folder_list_dict = {} @@ -139,8 +137,7 @@ def parse_folder_tasks(self, response, folder): return None def view_tasks_in_folder(self, folder=None): - if self.dce_transport is None: - self.dce_transport = DCETransport(self.host, self.username, self.port, self.conn) + self.setup_dce_transport() #self.dce_transport.connect('atsvc') folder_paths = self.folder_list if folder is None else [folder] @@ -149,7 +146,7 @@ def view_tasks_in_folder(self, folder=None): self.dce_transport._connect('atsvc') folder_path = folder_path.rstrip("\\") try: - #print_info(f"Enumerating tasks in folder: {folder_path}") + print_info(f"Enumerating tasks in folder: {folder_path}") response = self.dce_transport._view_tasks_in_folder(folder_path) #print_log(response.dump()) #print_info(f"Parsing Tasks in {folder_path}:") @@ -165,9 +162,9 @@ def view_tasks_in_folder(self, folder=None): print_bad("Unable to view tasks in folder: " + folder_path) print_log(e) - def task_run(self, abs_path): - if self.dce_transport is None: - self.dce_transport = DCETransport(self.host, self.username, self.port, self.conn) + def task_run(self, args): + abs_path = args.task_path + self.setup_dce_transport() self.dce_transport._connect('atsvc') response = self.dce_transport._run_task(abs_path) if response['ErrorCode'] == 0: @@ -175,7 +172,11 @@ def task_run(self, abs_path): else: print_bad(f"Error running task '{abs_path}': {response['ErrorCode']}") - def task_create(self, task_name, program, arguments, folder_path): + def task_create(self, args): + task_name = args.name + program = args.program + arguments = args.arguments + folder_path = args.folder # generate random date in last year using format 2023-01-01T08:00:00 new_date = generate_random_date() task_xml = f""" @@ -228,8 +229,7 @@ def task_create(self, task_name, program, arguments, folder_path): """ #validate_xml(task_xml) - if self.dce_transport is None: - self.dce_transport = DCETransport(self.host, self.username, self.port, self.conn) + self.setup_dce_transport() self.dce_transport._connect('atsvc') print_info(f"Creating task '{task_name}' in folder '{folder_path}'") @@ -250,11 +250,15 @@ def task_create(self, task_name, program, arguments, folder_path): print_log(f"Error creating task '{task_name}': {response['ErrorCode']}") - + def task_delete_handler(self, args): + if not self.folder_list_dict and args.taskid: + print_warning("No tasks have been enumerated. Run enumtasks first.") + else: + task_arg = args.taskid if args.taskid else args.task_path + self.task_delete(task_arg) def task_delete(self, task_arg): - if self.dce_transport is None: - self.dce_transport = DCETransport(self.host, self.username, self.port, self.conn) + self.setup_dce_transport() self.dce_transport._connect('atsvc') task_name = None @@ -279,12 +283,21 @@ def task_delete(self, task_arg): print_warning("Task ID not found") return else: - print_info(f"Chosen Task:\nTask Path: {task_path}\nTask Name: {task_name}") - response = self.dce_transport._delete_task(task_abs_path) - if response['ErrorCode'] == 0: - print_good(f"Task '{task_abs_path}' deleted successfully.") - else: - print_bad(f"Error deleting task '{task_abs_path}': {response['ErrorCode']}") + #print_info(f"Chosen Task:\nTask Path: {task_path}\nTask Name: {task_name}") + try: + response = self.dce_transport._delete_task(task_abs_path) + if response['ErrorCode'] == 0: + print_good(f"Task '{task_abs_path}' deleted successfully.") + else: + print_bad(f"Error deleting task '{task_abs_path}': {response['ErrorCode']}") + except Exception as e: + if "ERROR_FILE_NOT_FOUND" in str(e) or "ERROR_INVALID_NAME" in str(e): + print_warning(f"Task '{task_abs_path}' does not exist.") + return + else: + print_bad(f"Error deleting task '{task_abs_path}': {e}") + return + except Exception as e: task_arg = str(task_arg) if "Bind context rejected: reason_not_specified" in str(e): @@ -293,21 +306,22 @@ def task_delete(self, task_arg): print_warning("Unable to delete task: " + task_arg +" - invalid name") else: print_bad("Unable to delete task: " + task_arg) - print_bad("An error occurred:") - traceback.print_exc() - + print_debug("An error occurred:", sys.exc_info()) + + def task_show_handler(self, args): + + if not self.folder_list_dict and args.taskid: + print_warning("No tasks have been enumerated. Run enumtasks first.") + else: + task_arg = args.taskid if args.taskid else args.task_path + self.view_task_details(task_arg) def task_manager(self): pass - - - - def view_task_details(self, task_arg): - if self.dce_transport is None: - self.dce_transport = DCETransport(self.host, self.username, self.port, self.conn) + self.setup_dce_transport() self.dce_transport._connect('atsvc') # lookup taskpath and task name from dict with task_id task_name = None @@ -333,7 +347,7 @@ def view_task_details(self, task_arg): return else: print_info(f"Chosen Task:\nTask Path: {task_path}\nTask Name: {task_name}") - response = self.dce_transport._view_tasks(task_name, task_path) + response = self.dce_transport._view_tasks(task_name, task_path) if response['ErrorCode'] == 0: task_xml = response['pXml'] print_log(f"{task_xml}") @@ -343,7 +357,7 @@ def view_task_details(self, task_arg): except Exception as e: if "Bind context rejected: reason_not_specified" in str(e): print_warning("Unable to view task: " + task_arg +" - invalid context") - elif "ERROR_INVALID_NAME" in str(e): + elif "ERROR_INVALID_NAME" in str(e) or "ERROR_FILE_NOT_FOUND" in str(e): print_warning("Unable to view task: " + task_arg +" - invalid name") else: print_bad("Unable to view task: " + task_arg) diff --git a/slinger/lib/scm.py b/slinger/lib/scm.py index ce7fccf..4157296 100755 --- a/slinger/lib/scm.py +++ b/slinger/lib/scm.py @@ -28,10 +28,23 @@ def filter_services(self, filtered): return new_list else: return self.services_list - + + def show_service_handler(self, args): + if not self.services_list and args.serviceid: + print_warning("No services have been enumerated. Run enumservices first.") + else: + service_arg = args.serviceid if args.serviceid else args.service_name + self.view_service_details(service_arg) + + def start_service_handler(self, args): + if not self.services_list and args.serviceid: + print_warning("No services have been enumerated. Run enumservices first.") + else: + service_arg = args.serviceid if args.serviceid else args.service_name + self.start_service(service_arg) + def start_service(self, service_arg): - if self.dce_transport is None: - self.dce_transport = DCETransport(self.host, self.username, self.port, self.conn) + self.setup_dce_transport() self.dce_transport._connect('svcctl') # lookup taskpath and task name from dict with task_id @@ -50,32 +63,48 @@ def start_service(self, service_arg): print_info("Getting service details for %s" % service_arg) service_name = service_arg + if service_name is None: + print_warning("Service name not found") + return + else: + print_info("Chosen Service: " + service_name) try: - if service_name is None: - print_warning("Service name not found") - return + response = self.dce_transport._start_service(service_name) + if response['ErrorCode'] == 0: + print_good("Service started successfully") else: - print_info("Chosen Service: " + service_name) - try: - response = self.dce_transport._start_service(service_name) - if response['ErrorCode'] == 0: - print_good("Service started successfully") - else: - print_log(f"Error starting service '{service_name}': {response['ErrorCode']}") - except Exception as e: - if "ERROR_SERVICE_ALREADY_RUNNING" in str(e): - print_warning("Service already running") - self.winreg_already_setup = True - return - + print_log(f"Error starting service '{service_name}': {response['ErrorCode']}") except Exception as e: - print_bad("Unable to start service: " + service_name) - print_bad("An error occurred:") - traceback.print_exc() + if "ERROR_SERVICE_ALREADY_RUNNING" in str(e): + print_bad("Unable to start service: " + service_name) + print_warning("Service already running") + return + elif "ERROR_SERVICE_DISABLED" in str(e): + print_bad("Unable to start service: " + service_name) + print_warning("Service disabled") + return + elif "ERROR_SERVICE_LOGON_FAILED" in str(e): + print_bad("Unable to start service: " + service_name) + print_warning("Service logon failed") + return + elif "ERROR_SERVICE_DOES_NOT_EXIST" in str(e): + print_bad("Unable to start service: " + service_name) + print_warning("Service does not exist") + return + else: + print_bad("Unable to start service: " + service_name) + print_bad("An error occurred:" + str(e)) + print_debug('', sys.exc_info()) + + def service_stop_handler(self, args): + if not self.services_list and args.serviceid: + print_warning("No services have been enumerated. Run enumservices first.") + else: + service_arg = args.serviceid if args.serviceid else args.service_name + self.stop_service(service_arg) def stop_service(self, service_arg): - if self.dce_transport is None: - self.dce_transport = DCETransport(self.host, self.username, self.port, self.conn) + self.setup_dce_transport() self.dce_transport._connect('svcctl') # lookup taskpath and task name from dict with task_id @@ -106,13 +135,23 @@ def stop_service(self, service_arg): else: print_log(f"Error stopping service '{service_name}': {response['ErrorCode']}") except Exception as e: - print_bad("Unable to stop service: " + service_name) - print_bad("An error occurred:") - traceback.print_exc() + if "ERROR_SERVICE_NOT_ACTIVE" in str(e): + print_bad("Unable to stop service: " + service_name) + print_warning("Service not active") + return + elif "ERROR_SERVICE_DOES_NOT_EXIST" in str(e): + print_bad("Unable to stop service: " + service_name) + print_warning("Service does not exist") + return + else: + print_bad("Unable to start service: " + service_name) + print_bad("An error occurred:" + str(e)) + print_debug('', sys.exc_info()) - def enum_services(self, force=False, filtered=None): - if self.dce_transport is None: - self.dce_transport = DCETransport(self.host, self.username, self.port, self.conn) + def enum_services(self, args): + force = args.new + filtered = args.filter + self.setup_dce_transport() self.dce_transport._connect('svcctl') if force or len(self.services_list) == 0: @@ -174,8 +213,7 @@ def enum_services(self, force=False, filtered=None): def view_service_details(self, service_arg): - if self.dce_transport is None: - self.dce_transport = DCETransport(self.host, self.username, self.port, self.conn) + self.setup_dce_transport() self.dce_transport._connect('svcctl') # lookup taskpath and task name from dict with task_id @@ -278,10 +316,71 @@ def view_service_details(self, service_arg): return print_bad("Unable to view service details: " + service_name) - print_bad("An error occurred:") - traceback.print_exc() + print_bad("An error occurred:" + str(e)) + print_debug('', sys.exc_info()) + + def service_del_handler(self, args): + if not self.services_list and args.serviceid: + print_warning("No services have been enumerated. Run enumservices first.") + else: + service_arg = args.serviceid if args.serviceid else args.service_name + self.delete_service(service_arg) + + def delete_service(self, service_arg): + self.setup_dce_transport() + self.dce_transport._connect('svcctl') + # lookup taskpath and task name from dict with task_id + service_name = None + if type(service_arg) is int: + print_info("Looking up service ID...") + count = 1 + #print_log("Searching through %d services" % len(self.services_list)) + for service in self.services_list: + #print_info("Checking service %d" % count) + if count == service_arg: + service_name = service[1] + break + count += 1 + else: + service_name = service_arg + + try: + if service_name is None: + print_warning("Service name not found") + return + else: + print_info("Chosen Service: " + service_name) + response = self.dce_transport._delete_service(service_name) + if response['ErrorCode'] == 0: + print_good("Service deleted successfully") + else: + print_log(f"Error deleting service '{service_name}': {response['ErrorCode']}") + except Exception as e: + print_bad("Unable to delete service: " + service_name) + print_bad("An error occurred:" + str(e)) + print_debug('', sys.exc_info()) + + def create_service(self, args): + service_name = args.servicename + bin_path = args.binarypath + display_name = args.displayname + start_type = args.starttype + + self.setup_dce_transport() + self.dce_transport._connect('svcctl') + try: + print_info("Creating service...") + response = self.dce_transport._create_service(service_name, bin_path, start_type, display_name) + if response['ErrorCode'] == 0: + print_good("Service created successfully") + else: + print_log(f"Error creating service '{service_name}': {response['ErrorCode']}") + except Exception as e: + print_bad("Unable to create service: " + service_name) + print_bad("An error occurred:" + str(e)) + print_debug('', sys.exc_info()) diff --git a/slinger/lib/smblib.py b/slinger/lib/smblib.py index a047925..899d6e8 100755 --- a/slinger/lib/smblib.py +++ b/slinger/lib/smblib.py @@ -12,11 +12,20 @@ class smblib(): def __init__(self): print_debug("Smb Commands Module Loaded!") - def print_current_path(self): - print_log(self.current_path) + def cd_handler(self, args=None): + if self.check_if_connected(): + if args.path: + self.cd(args.path) + elif args.command == "cd": + self.print_current_path() + + def print_current_path(self, args=None): + if self.check_if_connected(): + print_log(self.current_path) # connect to a share - def connect_share(self, share): + def connect_share(self, args): + share = args.share try: self.tree_id = self.conn.connectTree(share) self.share = share @@ -24,6 +33,7 @@ def connect_share(self, share): self.is_connected_to_share = True self.update_current_path() except Exception as e: + print_debug(str(e), sys.exc_info()) if "STATUS_BAD_NETWORK_NAME" in str(e): print_bad(f"Failed to connect to share {share}: Invalid share name.") else: @@ -32,31 +42,96 @@ def connect_share(self, share): - def list_shares(self): + def list_shares(self, args=None): shares = self.conn.listShares() print_info("Available Shares") for share in shares: print_log(f"{share['shi1_netname']}") - def mkdir(self, path): - + def mkdir(self, args): + if not self.check_if_connected(): + return + path = args.path try: self.conn.createDirectory(self.share, path) print_info(f"Directory created {path}") except Exception as e: - print_bad(f"Failed to create directory {path}: {e}") - raise e + if "STATUS_OBJECT_NAME_COLLISION" in str(e): + print_warning(f"Directory already exists {path}") + else: + print_bad(f"Failed to create directory {path}: {e}") + print_debug(str(e), sys.exc_info()) - def rmdir(self, path): + def rmdir(self, args): + if not self.check_if_connected(): + return + path = ntpath.normpath(ntpath.join(self.relative_path, args.remote_path)) try: + print_debug(f"Removing directory {path}") self.conn.deleteDirectory(self.share, path) print_info(f"Directory removed {path}") except Exception as e: + print_debug(str(e), sys.exc_info()) print_bad(f"Failed to remove directory {path}: {e}") raise e + def rm_handler(self, args): + if not self.check_if_connected(): + return + if args.remote_path == "." or args.remote_path == "" or args.remote_path is None: + print_warning("Please specify a file to remove.") + return + path = ntpath.normpath(ntpath.join(self.relative_path, args.remote_path)) + + if self.check_if_connected(): + #if self.file_exists(path): + # self.delete(path) + #else: + # print_warning(f"Remote file {path} does not exist.") + try: + self.delete(path) + except Exception as e: + print_debug(str(e), sys.exc_info()) + if "STATUS_OBJECT_NAME_NOT_FOUND" in str(e): + print_warning(f"Remote file {path} does not exist.") + def delete(self, remote_path): - self.conn.deleteFile(self.share, remote_path) + + if not self.check_if_connected(): + return + + #recursively delete files in directory + if remote_path.endswith('*'): + + remote_path = remote_path[:-2] + if self.is_valid_directory(remote_path): + print_info(f"Deleting files in directory '{remote_path}'") + list_path = remote_path + '\\*' if remote_path else '*' + files = self.conn.listPath(self.share, list_path) + for f in files: + if f.is_directory() and f.get_longname() in ['.', '..']: + print_debug(f"Found directory {os.path.join(remote_path, f.get_longname())}") + continue + print_debug(f"Deleting file {os.path.join(remote_path, f.get_longname())}") + self.conn.deleteFile(self.share,os.path.join(remote_path, f.get_longname())) + print_info(f"File Removed '{os.path.join(remote_path, f.get_longname())}'") + else: + print_warning(f"Invalid directory: {remote_path}") + return + else: + print_debug(f"Deleting file '{remote_path}'") + try: + self.conn.deleteFile(self.share, remote_path) + print_info(f"File Removed '{remote_path}'") + except Exception as e: + print_debug(str(e), sys.exc_info()) + if "STATUS_OBJECT_NAME_NOT_FOUND" in str(e): + print_warning(f"Remote file '{remote_path}' does not exist.") + elif "STATUS_FILE_IS_A_DIRECTORY" in str(e): + print_warning(f"Remote object '{remote_path}' is a directory, skipping.") + else: + print_bad(f"Failed to delete file '{remote_path}': {e}") + raise e #update current path as share + relative path def update_current_path(self): @@ -70,12 +145,21 @@ def is_valid_directory(self, path): self.conn.listPath(self.share, list_path) return True except Exception as e: + if "STATUS_STOPPED_ON_SYMLINK" in str(e): + print_warning(f"Remote directory {path} is a symlink.") + elif "STATUS_NOT_A_DIRECTORY" in str(e): + print_warning(f"Remote object {path} is not a directory.") + + print_debug(f"Failed to list directory {path} on share {self.share}: {e}", sys.exc_info()) return False def cd(self, path): + if not self.check_if_connected(): + return # Handle ".." in path + print_debug(f"Changing directory to {path}") if ".." in path: - path = os.path.normpath(os.path.join(self.relative_path, path)) + path = ntpath.normpath(ntpath.join(self.relative_path, path)) if path.startswith(".."): print_warning("Cannot go above root directory.") return @@ -89,7 +173,7 @@ def cd(self, path): # Handle relative paths else: #path = os.path.join(self.relative_path, path) - path = ntpath.normpath(os.path.join(self.relative_path, path)) + path = ntpath.normpath(ntpath.join(self.relative_path, path)) if path == self.share: self.relative_path = "" @@ -104,14 +188,47 @@ def cd(self, path): print_warning(f"Invalid directory: {path}") # handle file uploads + def upload_handler(self, args): + remote_path = "" + if self.check_if_connected(): + if args.remote_path == "." or args.remote_path == "" or args.remote_path is None or "\\" not in args.remote_path: + remote_path = ntpath.join(self.relative_path,ntpath.basename(args.local_path)) + else: + remote_path = args.remote_path + if os.path.exists(args.local_path): + print_info(f"Uploading: {args.local_path} --> {self.share}\\{remote_path}") + self.upload(args.local_path, remote_path) + else: + print_warning(f"Local path {args.local_path} does not exist.") + def upload(self, local_path, remote_path): + if not self.check_if_connected(): + return try: with open(local_path, 'rb') as file_obj: self.conn.putFile(self.share, remote_path, file_obj.read) except Exception as e: + print_debug(str(e), sys.exc_info()) print_bad(f"Failed to upload file {local_path} to {remote_path}: {e}") print_log(sys.exc_info()) + def download_handler(self, args): + remote_path = os.path.normpath(os.path.join(self.relative_path, args.remote_path)) + local_path = "" + if self.check_if_connected(): + if args.local_path == "." or args.local_path == "" or args.local_path is None or "/" not in args.local_path: + local_path = os.path.join(os.getcwd(), os.path.basename(args.remote_path)) + else: + if os.path.isdir(os.path.dirname(args.local_path)): + local_path = os.path.join(args.local_path, ntpath.basename(args.remote_path)) + else: + local_path = args.local_path + if os.path.isdir(os.path.dirname(local_path)): + print_info(f"Downloading: {ntpath.join(self.share,remote_path)} --> {local_path}") + self.download(remote_path, local_path) + else: + print_warning(f"Local path {args.local_path} does not exist.") + def download(self, remote_path, local_path): if remote_path.endswith('.') or remote_path.endswith('..'): return @@ -120,10 +237,25 @@ def download(self, remote_path, local_path): with open(local_path, 'wb') as file_obj: self.conn.getFile(self.share, remote_path, file_obj.write) except Exception as e: - print_bad(f"Failed to download file {remote_path} to {local_path}: {e}") - print_log(sys.exc_info()) + print_debug(f"Failed to download file '{remote_path}' to '{local_path}': {e}", sys.exc_info()) + if "STATUS_OBJECT_NAME_NOT_FOUND" in str(e): + print_warning(f"Remote file '{remote_path}' does not exist.") + else: + print_bad(f"Failed to download file '{remote_path}' to '{local_path}': {e}") + + def mget_handler(self, args): + if not self.check_if_connected(): + return + remote_path = args.remote_path if args.remote_path else self.relative_path + if self.is_valid_directory(remote_path): + local_path = args.local_path if args.local_path else os.getcwd() + self.mget(remote_path, local_path, args.r, args.p, args.d) + else: + print_log(f"Remote directory {remote_path} does not exist.") def mget(self, remote_path=None, local_path=None, go_into_dirs=False, regex=None, current_depth=1, max_depth=1): + if not self.check_if_connected(): + return if local_path is None: local_path = os.getcwd() @@ -154,21 +286,36 @@ def mget(self, remote_path=None, local_path=None, go_into_dirs=False, regex=None def file_exists(self, remote_path): - files = self.conn.listPath(self.share, remote_path) + print_debug(f"Checking if file exists: {remote_path}") + path = ntpath.normpath(ntpath.join(remote_path, "..")) + print_debug(f"Listing Files in Directory: {path}") + files = self.conn.listPath(self.share, path) + print_debug(f"Checking if file exists: {ntpath.basename(remote_path)}") for file in files: - if file.get_longname() == os.path.basename(remote_path): + if file.get_longname() == ntpath.basename(remote_path): + print_debug(f"File exists: {remote_path}") return True + print_debug(f"File does not exist: {remote_path}") return False - def cat(self, path): + def cat(self, args): + if not self.check_if_connected(): + return + path = ntpath.normpath(ntpath.join(self.relative_path, args.remote_path)) temp_path = tempfile.NamedTemporaryFile(dir='/tmp', delete=False).name self.download(path, temp_path) - with open(temp_path, 'r') as file_obj: - print_log(file_obj.read()) + try: + with open(temp_path, 'r', encoding=get_config_value("Codec")) as file_obj: + print(file_obj.read()) + except UnicodeDecodeError: + print_warning(f"Failed to decode file '{path}' using codec {get_config_value('Codec')}. Try changing the codec using the 'set codec ' command.") os.remove(temp_path) # dir list - def dir_list(self, path=None): + def dir_list(self, args=None): + if not self.check_if_connected(): + return + path = args.path if path is None or path == "." or path == "": if self.relative_path == "": path = "" @@ -209,4 +356,5 @@ def dir_list(self, path=None): print_info("Showing directory listing for: " + os.path.normpath(self.share + "\\" + suffix)) print_log(tabulate(dirList, headers=['Type', 'Created', 'Last Access', 'Last Write', 'Size', 'Attribs', 'Name'], tablefmt='psql')) except Exception as e: + print_debug(f"Failed to list directory {path} on share {self.share}: {e}", sys.exc_info()) print_bad(f"Failed to list directory {path} on share {self.share}: {e}") diff --git a/slinger/lib/winreg.py b/slinger/lib/winreg.py index a0195d7..87d322a 100755 --- a/slinger/lib/winreg.py +++ b/slinger/lib/winreg.py @@ -1,10 +1,7 @@ from slinger.utils.printlib import * from slinger.lib.dcetransport import * -import traceback from tabulate import tabulate -import os import traceback -import datetime from time import sleep from slinger.utils.common import reduce_slashes @@ -66,8 +63,7 @@ def enum_key_value(self, keyName, hex_dump=True, return_val=False): """ self.registry_used = True - if self.dce_transport is None: - self.dce_transport = DCETransport(self.host, self.username, self.port, self.conn) + self.setup_dce_transport() self.dce_transport._connect('winreg') print_info("Enumerating Key: " + keyName) @@ -81,7 +77,7 @@ def enum_key_value(self, keyName, hex_dump=True, return_val=False): return ans - def setup_remote_registry(self): + def setup_remote_registry(self, args): """ Sets up the remote registry service. @@ -94,23 +90,41 @@ def setup_remote_registry(self): Returns: None """ - if self.dce_transport is None: - self.dce_transport = DCETransport(self.host, self.username, self.port, self.conn) + self.setup_dce_transport() self.dce_transport._connect('svcctl') try: print_info("Starting Remote Registry service") - self.registry_used = True response = self.dce_transport._start_service('RemoteRegistry') print_good("Remote Registry service started") except Exception as e: - print_debug(str(e)) + if "ERROR_SERVICE_ALREADY_RUNNING" in str(e): print_warning("RemoteRegistry Service already running") self.winreg_already_setup = True return - - def stop_remote_registry(self): + else: + print_debug(str(e), sys.exc_info()) + + def reg_query_handler(self, args): + if args.list: + self.enum_subkeys(args.key) + elif args.value: + self.enum_key_value(args.key) + elif args.key: + try: + self.enum_key_value(args.key) + self.enum_subkeys(args.key) + except Exception as e: + if "ERROR_FILE_NOT_FOUND" in str(e): + print_bad("Registry key does not exist") + else: + print_bad("Error querying registry key: " + str(e)) + print_debug(str(e), sys.exc_info()) + + #elif args.command == "regset": + + def stop_remote_registry(self, args): """ Stops the Remote Registry service. @@ -122,8 +136,7 @@ def stop_remote_registry(self): Returns: None """ - if self.dce_transport is None: - self.dce_transport = DCETransport(self.host, self.username, self.port, self.conn) + self.setup_dce_transport() self.dce_transport._connect('svcctl') try: self.registry_used = True @@ -134,7 +147,7 @@ def stop_remote_registry(self): else: print_bad("Failed to stop Remote Registry service") except Exception as e: - print_debug(str(e)) + print_debug(str(e), sys.exc_info()) if "ERROR_SERVICE_NOT_ACTIVE" in str(e): print_warning("RemoteRegistry Service already stopped") return @@ -151,8 +164,7 @@ def enum_subkeys(self, keyName, return_list=False): list or None: The subkeys as a list if `return_list` is True, otherwise None. """ self.registry_used = True - if self.dce_transport is None: - self.dce_transport = DCETransport(self.host, self.username, self.port, self.conn) + self.setup_dce_transport() self.dce_transport._connect('winreg') subkeys = self.dce_transport._enum_subkeys(keyName) if not return_list: @@ -161,7 +173,7 @@ def enum_subkeys(self, keyName, return_list=False): else: return subkeys - def ipconfig(self): + def ipconfig(self, args): """ Retrieves and prints the IP configuration information for the current host. @@ -178,8 +190,7 @@ def ipconfig(self): \tDhcpDomain:\t{DhcpDomain} """ - if self.dce_transport is None: - self.dce_transport = DCETransport(self.host, self.username, self.port, self.conn) + self.setup_dce_transport() self.dce_transport._connect('winreg') print_info("Enumerating IP Configuration...") subkeys = self.enum_subkeys(self.reg_interface, return_list=True) @@ -200,7 +211,7 @@ def ipconfig(self): _iface = iface.split("\\")[-1] print_log(iface_banner.format(interface=_iface, **values)) - def hostname(self): + def hostname(self, args): """ Retrieves the hostname from the Windows registry. @@ -211,14 +222,16 @@ def hostname(self): Exception: If there is an error retrieving the hostname. """ self.registry_used = True - if self.dce_transport is None: - self.dce_transport = DCETransport(self.host, self.username, self.port, self.conn) + self.setup_dce_transport() self.dce_transport._connect('winreg') hKey = self.dce_transport._get_key_handle(self.reg_tcpip) ans = self.dce_transport._get_key_values(hKey) values = extract_reg_values(ans, ["Hostname"]) print_log("Hostname:\t" + values["Hostname"]) + def add_reg_value_handler(self, args): + self.add_reg_value(args.key, args.value, args.data, args.type) + def add_reg_value(self, keyName, valueName, valueData, valueType="REG_SZ"): """ Adds a registry value to the specified key. @@ -233,8 +246,7 @@ def add_reg_value(self, keyName, valueName, valueData, valueType="REG_SZ"): None """ - if self.dce_transport is None: - self.dce_transport = DCETransport(self.host, self.username, self.port, self.conn) + self.setup_dce_transport() self.dce_transport._connect('winreg') try: @@ -244,6 +256,7 @@ def add_reg_value(self, keyName, valueName, valueData, valueType="REG_SZ"): else: print_bad(f"Failed to Add Value {valueName} to {keyName}") except Exception as e: + print_debug(str(e), sys.exc_info()) if "ERROR_FILE_NOT_FOUND" in str(e): print_warning(f"Key {keyName} does not exist") return @@ -254,7 +267,7 @@ def add_reg_value(self, keyName, valueName, valueData, valueType="REG_SZ"): - def show_fw_rules(self): + def show_fw_rules(self, args): """ Retrieves and prints the firewall rules for the current host. @@ -300,7 +313,6 @@ def parse_firewall_rules(rule_list): - # MSDTC-KTMRM-In-TCP REG_SZ v2.22|Action=Allow|Active=FALSE|Dir=In|Protocol=6|LPort=RPC|App=%SystemRoot%\system32\svchost.exe|Svc=ktmrm|Name=@FirewallAPI.dll,-33511|Desc=@FirewallAPI.dll,-33512|EmbedCtxt=@FirewallAPI.dll,-33502| ans = self.enum_key_value(self.fwrule, return_val=True) fwrules_list = ans.splitlines() #print(fwrules_list) @@ -312,7 +324,7 @@ def parse_firewall_rules(rule_list): print(tabulate(parsed_rules, headers="keys")) - def reg_create_key(self, keyName): + def reg_create_key(self, args): """ Creates a registry key. @@ -322,9 +334,12 @@ def reg_create_key(self, keyName): Returns: None """ + try: + keyName = args.key + except AttributeError: + keyName = args self.registry_used = True - if self.dce_transport is None: - self.dce_transport = DCETransport(self.host, self.username, self.port, self.conn) + self.setup_dce_transport() self.dce_transport._connect('winreg') ans = self.dce_transport._reg_create_key(keyName) if ans: @@ -344,8 +359,7 @@ def reg_delete_key(self, keyName): None """ self.registry_used = True - if self.dce_transport is None: - self.dce_transport = DCETransport(self.host, self.username, self.port, self.conn) + self.setup_dce_transport() self.dce_transport._connect('winreg') try: ans = self.dce_transport._reg_delete_key(keyName) @@ -354,6 +368,7 @@ def reg_delete_key(self, keyName): else: print_bad(f"Failed to Delete Key {keyName}") except Exception as e: + print_debug(str(e), sys.exc_info()) if "ERROR_FILE_NOT_FOUND" in str(e): print_warning(f"Key {keyName} does not exist") return @@ -361,7 +376,14 @@ def reg_delete_key(self, keyName): print_bad(f"Failed to Delete Key {keyName}") print_debug("Failed to Delete Key", sys.exc_info()) return - + + def reg_delete_handler(self, args): + if args.value: + self.del_reg_key_value(args.key, args.value) + elif args.key: + self.reg_delete_key(args.key) + else: + print_warning("Invalid arguments. Usage: regdel -k [-v ]") def del_reg_key_value(self, keyName, keyValue): """ @@ -374,12 +396,12 @@ def del_reg_key_value(self, keyName, keyValue): None """ self.registry_used = True - if self.dce_transport is None: - self.dce_transport = DCETransport(self.host, self.username, self.port, self.conn) + self.setup_dce_transport() self.dce_transport._connect('winreg') try: ans = self.dce_transport._reg_delete_value(keyName, keyValue) except Exception as e: + print_debug(str(e), sys.exc_info()) if "ERROR_FILE_NOT_FOUND" in str(e): print_warning(f"Key {keyName} and value {keyValue} combination does not exist") print_debug(f"Key {keyName} and value {keyValue} combination does not exist", sys.exc_info()) @@ -390,7 +412,7 @@ def del_reg_key_value(self, keyName, keyValue): print_bad(f"Failed to Delete Value {keyValue} from {keyName}") - def does_key_exist(self, keyName): + def does_key_exist(self, args): """ Checks if the specified registry key exists. @@ -400,18 +422,25 @@ def does_key_exist(self, keyName): Returns: bool: True if the key exists, False otherwise. """ + keyName = args.key try: _ = self.enum_key_value(keyName, return_val=True) print_debug(f"Key {keyName} exists") return True except Exception as e: - print_debug(f"Key {keyName} does not exist") + print_debug(f"Key {keyName} does not exist", sys.exc_info()) if "ERROR_FILE_NOT_FOUND" in str(e): return False else: print_debug("Unable to check if key exists", sys.exc_info()) return False + def port_fwd_rules_handler(self, args): + if args.load: + self.load_port_fwd_rules() + else: + self.print_portfwd_rules() + def print_portfwd_rules(self): """ Prints the current port forwarding rules. @@ -427,8 +456,7 @@ def add_port_fwd_rule(self, local, remote): Adds a port forwarding rule. Args: - listen_port (int): The port to listen on. - connect_port (int): The port to connect to. + listen_addr (str): The port to listen on. connect_addr (str): The address to connect to. Returns: @@ -439,6 +467,7 @@ def add_port_fwd_rule(self, local, remote): listen_addr, listen_port = local.split(":") connect_addr, connect_port = remote.split(":") except Exception as e: + print_debug(str(e), sys.exc_info()) print_bad("Invalid Local or Remote Address") return @@ -476,7 +505,9 @@ def del_port_fwd_rule(self, local): print_debug("Searching in:" + key) subkeys = self.enum_subkeys(key, return_list=True) if "v4tov4" not in " ".join(subkeys): - print_warning("No Port Forwarding Rules Created") + print_warning("No Port Forwarding Rules Found") + #set the active_portfwd_rules to empty + self.active_portfwd_rules = [] return else: key = self.portproxy_root + "v4tov4\\tcp\\" @@ -484,6 +515,8 @@ def del_port_fwd_rule(self, local): values = self.enum_key_value(key, return_val=True) if len(values) == 0: print_warning("No Port Forwarding Rules Found") + #set the active_portfwd_rules to empty + self.active_portfwd_rules = [] return else: values = values.splitlines() @@ -495,7 +528,7 @@ def del_port_fwd_rule(self, local): _listen_addr = rule_list[0].split("/")[0] _listen_port = rule_list[0].split("/")[1] print_info(f"Found Rule: {_listen_addr}:{_listen_port}") - print_info(f"Searching for Rule: {listen_addr}:{listen_port}") + print_info(f"Searching for Rule: {listen_addr}/{listen_port}") if _listen_addr == listen_addr and _listen_port == listen_port: #print("Found Rule") #print(rule) @@ -521,16 +554,37 @@ def check_port_fwd_rules(self): #print(subkeys) if "v4tov4" not in subkeys: print_debug("No Port Forwarding Rules Found") + #set the active_portfwd_rules to empty + self.active_portfwd_rules = [] return False else: values = self.enum_key_value(self.portproxy_root + "v4tov4\\tcp", return_val=True) #print(values) if len(values) == 0: print_debug("No Port Forwarding Rules Found") + #set the active_portfwd_rules to empty + self.active_portfwd_rules = [] return False else: return True + def port_fwd_handler(self, args): + if args.list: + self.print_portfwd_rules() + elif args.remove: + self.del_port_fwd_rule(args.local) + elif args.add: + if not args.local or not args.remote: + print_warning("Invalid arguments. Usage: portfwd -l|-d|-a ") + return + self.add_port_fwd_rule(args.local, args.remote) + elif args.load: + r = self.load_port_fwd_rules() + if r: + self.print_portfwd_rules() + else: + print_warning("Invalid arguments. Usage: portfwd -l|-d|-a ") + def load_port_fwd_rules(self): # get the current rule set from the regsitry and load it into the active_portfwd_rules list # check if portproxy\v4tov4 key exists @@ -539,7 +593,9 @@ def load_port_fwd_rules(self): if self.check_port_fwd_rules() == False: print_warning("No Port Forwarding Rules Found") - return + #set the active_portfwd_rules to empty + self.active_portfwd_rules = [] + return False else: key = self.portproxy_root + "v4tov4\\tcp\\" values = self.enum_key_value(key, return_val=True) @@ -547,11 +603,14 @@ def load_port_fwd_rules(self): #subkeys = self.enum_subkeys(self.portproxy_root + "v4tov4\\tcp\\", return_list=True) if len(values) == 0: print_warning("No Port Forwarding Rules Found") - return + #set the active_portfwd_rules to empty + self.active_portfwd_rules = [] + return False else: values = values.splitlines() for rule in values: rule = rule.strip().split() listen_addr, listen_port = rule[0].split("/") connect_addr, connect_port = rule[2].split("/") - self.active_portfwd_rules.append({"Listen Address": listen_addr+":"+listen_port, "Connect Address": connect_addr+":"+connect_port}) \ No newline at end of file + self.active_portfwd_rules.append({"Listen Address": listen_addr+":"+listen_port, "Connect Address": connect_addr+":"+connect_port}) + return True \ No newline at end of file diff --git a/slinger/plugins/my_plugin copy.py b/slinger/plugins/my_plugin copy.py new file mode 100644 index 0000000..6da149c --- /dev/null +++ b/slinger/plugins/my_plugin copy.py @@ -0,0 +1,35 @@ +# my_plugin.py +from slinger.lib.plugin_base import PluginBase +import argparse +from slinger.lib.dcetransport import * + +class MyPlugin(PluginBase): + + def get_parser(self): + # define a new subparser to return to merge with the main parser + parser = argparse.ArgumentParser(add_help=False) + subparsers = parser.add_subparsers(dest='command') + plugincmd_parser = subparsers.add_parser("anotherplugincmd", help="My plugin subparser") + plugincmd_parser.set_defaults(func=self.run) + return parser + + def run(self, args): + print(f"Executing {args.command}") + + # example of using the SlingerClient object + self.client.info() + + # example of using the DCETransport object + self.client.setup_dce_transport() + self.client.dce_transport._connect('srvsvc') + response = self.client.dce_transport._enum_info() + #print_log(response.dump()) + print_info("Server Info:") + info = response['InfoStruct']['ServerInfo101'] + print_log(f"Server name: {info['sv101_name']}") + print_log(f"Server platform id: {info['sv101_platform_id']}") + print_log(f"Server version: {info['sv101_version_major']}.{info['sv101_version_minor']}") + print_log(f"Server type: {info['sv101_type']}") + print_log(f"Server comment: {info['sv101_comment']}") + print_info("Server Disk Info:") + self.client.enum_server_disk() diff --git a/slinger/plugins/my_plugin.py b/slinger/plugins/my_plugin.py index b8b5e95..d6d3d83 100644 --- a/slinger/plugins/my_plugin.py +++ b/slinger/plugins/my_plugin.py @@ -11,6 +11,7 @@ def get_parser(self): subparsers = parser.add_subparsers(dest='command') plugincmd_parser = subparsers.add_parser("plugincmd", help="My plugin subparser") plugincmd_parser.add_argument("--plugincmd", help="My plugin argument") + plugincmd_parser.set_defaults(func=self.run) return parser def run(self, args): @@ -20,6 +21,7 @@ def run(self, args): self.client.info() # example of using the DCETransport object + self.client.setup_dce_transport() self.client.dce_transport._connect('srvsvc') response = self.client.dce_transport._enum_info() #print_log(response.dump()) diff --git a/slinger/slingerclient.py b/slinger/slingerclient.py index 25d5b29..0185841 100755 --- a/slinger/slingerclient.py +++ b/slinger/slingerclient.py @@ -49,20 +49,47 @@ def __init__(self, host, username, password, domain, port=445, ntlm_hash=None, u self.wkssvc_pipe = None - + def setup_dce_transport(self): + if self.dce_transport is None: + self.dce_transport = DCETransport(self.host, self.username, self.port, self.conn) def login(self): - self.conn = smbconnection.SMBConnection(self.host, self.host, sess_port=self.port) - + print_info(f"Connecting to {self.host}:{self.port}...") + try: + self.conn = smbconnection.SMBConnection(self.host, self.host, sess_port=self.port, timeout=15) + except Exception as e: + print_debug(str(e), sys.exc_info()) + if "Connection error" in str(e): + print_bad(f"Failed to connect to {self.host}:{self.port}") + sys.exit() + if self.conn is None or self.conn == "": self.is_logged_in = False raise Exception("Failed to create SMB connection.") - if self.use_kerberos: - self.conn.kerberosLogin(self.username, self.password, domain=self.domain, lmhash='', nthash='', aesKey='', TGT=None, TGS=None) - elif self.ntlm_hash: - self.conn.loginWithHash(self.username, self.ntlm_hash, domain=self.domain) - else: - self.conn.login(self.username, self.password, domain=self.domain) + try: + if self.use_kerberos: + self.conn.kerberosLogin(self.username, self.password, domain=self.domain, lmhash='', nthash='', aesKey='', TGT=None, TGS=None) + elif self.ntlm_hash: + self.conn.loginWithHash(self.username, self.ntlm_hash, domain=self.domain) + else: + self.conn.login(self.username, self.password, domain=self.domain) + print_good(f"Successfully logged in to {self.host}:{self.port}") + GRN_BLD = '\033[1;32m' + RST = '\033[0m' + print("\nStart Time: " + GRN_BLD + str(self.session_start_time) + RST + "\n") + except Exception as e: + print_debug(str(e), sys.exc_info()) + if "STATUS_LOGON_FAILURE" in str(e): + print_bad(f"Authentication Failed {self.host}:{self.port}") + sys.exit() + elif "STATUS_ACCOUNT_RESTRICTION" in str(e): + print_good("Login Successful") + print_bad(f"Account is restricted {self.host}:{self.port}") + sys.exit() + else: + print_bad(f"Login Failed {self.host}:{self.port}") + print_log(str(e) + "\n" + str(sys.exc_info())) + sys.exit() #set a large timeout self.conn.timeout = config.smb_conn_timeout self.is_logged_in = True @@ -82,6 +109,10 @@ def exit(self): try: self.dce_transport._disconnect() self.conn.logoff() + GRN_BLD = '\033[1;32m' + RST = '\033[0m' + CURRENT_TIME = datetime.datetime.now() + print("\nStop Time: " + GRN_BLD + str(CURRENT_TIME) + RST + "\n") except: pass @@ -91,7 +122,7 @@ def is_connected_to_remote_share(self): return self.conn and self.is_connected_to_share - def info(self): + def info(self, args=None): dialect_mapping = { 0x02FF: "SMB 1.0", 0x031F: "SMB 2.0.2", @@ -115,11 +146,10 @@ def info(self): - def who(self): + def who(self, args=None): try: - if self.dce_transport is None: - self.dce_transport = DCETransport(self.host, self.username, self.port, self.conn) + self.setup_dce_transport() self.dce_transport._connect('srvsvc') resp = self.dce_transport._who() for session in resp['InfoStruct']['SessionInfo']['Level10']['Buffer']: @@ -127,13 +157,13 @@ def who(self): session['sesi10_cname'][:-1], session['sesi10_username'][:-1], session['sesi10_time'], session['sesi10_idle_time'])) except DCERPCException as e: + print_debug(str(e), sys.exc_info()) print_bad(f"Failed to list sessions: {e}") raise e - def enum_server_disk(self): + def enum_server_disk(self, args=None): try: - if self.dce_transport is None: - self.dce_transport = DCETransport(self.host, self.username, self.port, self.conn) + self.setup_dce_transport() self.dce_transport._connect('srvsvc') response = self.dce_transport._enum_server_disk() @@ -146,21 +176,20 @@ def enum_server_disk(self): else: print_log(f"Error: {response['ErrorCode']}") except Exception as e: + print_debug(str(e), sys.exc_info()) print_log(f"An error occurred: {str(e)}") raise e - def enum_logons(self): - if self.dce_transport is None: - self.dce_transport = DCETransport(self.host, self.username, self.port, self.conn) + def enum_logons(self, args=None): + self.setup_dce_transport() self.dce_transport._connect('wkssvc') response = self.dce_transport._enum_logons() print_info("Logged on Users:") for user_info in response['UserInfo']['WkstaUserInfo']['Level1']['Buffer']: print_log(f"Username: {user_info['wkui1_username']}") - def enum_sys(self): - if self.dce_transport is None: - self.dce_transport = DCETransport(self.host, self.username, self.port, self.conn) + def enum_sys(self, args=None): + self.setup_dce_transport() self.dce_transport._connect('wkssvc') response = self.dce_transport._enum_sys() # Assuming you have a response from NetrWkstaGetInfo @@ -175,9 +204,8 @@ def enum_sys(self): print_log(f"Logged-on Users: {info['wki102_logged_on_users']}") - def enum_transport(self): - if self.dce_transport is None: - self.dce_transport = DCETransport(self.host, self.username, self.port, self.conn) + def enum_transport(self, args=None): + self.setup_dce_transport() self.dce_transport._connect('wkssvc') response = self.dce_transport._enum_transport() transports = response['TransportInfo']['WkstaTransportInfo']['Level0']['Buffer'] @@ -198,9 +226,8 @@ def enum_transport(self): print_log(f"WAN ISH: {transport['wkti0_wan_ish']}") print_log() - def enum_info(self): - if self.dce_transport is None: - self.dce_transport = DCETransport(self.host, self.username, self.port, self.conn) + def enum_info(self, args=None): + self.setup_dce_transport() self.dce_transport._connect('srvsvc') response = self.dce_transport._enum_info() #print_log(response.dump()) @@ -215,7 +242,7 @@ def enum_info(self): self.enum_server_disk() - def get_server_time(self): + def get_server_time(self, args=None): #print local date and time time = datetime.datetime.now() date = datetime.date.today() @@ -223,8 +250,7 @@ def get_server_time(self): print_info(f"Local Date: {date.month}/{date.day}/{date.year}") try: - if self.dce_transport is None: - self.dce_transport = DCETransport(self.host, self.username, self.port, self.conn) + self.setup_dce_transport() self.dce_transport._connect('srvsvc') response = self.dce_transport._fetch_server_time() if response['ErrorCode'] == 0: # Checking for successful response @@ -256,6 +282,7 @@ def get_server_time(self): else: print_log(f"Error: {response['ErrorCode']}") except Exception as e: + print_debug(str(e), sys.exc_info()) print_log(f"An error occurred: {str(e)}") raise e diff --git a/slinger/utils/cli.py b/slinger/utils/cli.py index 2841a27..7281ee9 100755 --- a/slinger/utils/cli.py +++ b/slinger/utils/cli.py @@ -1,6 +1,5 @@ import argparse from .printlib import * -from prompt_toolkit import prompt from prompt_toolkit.completion import Completer, Completion from slinger.var.config import version, program_name from itertools import zip_longest @@ -90,7 +89,8 @@ def show_command_help(parser, command): else: print(f"Command '{command}' not found.") -def setup_cli_parser(): + +def setup_cli_parser(slingerClient): parser = CustomArgumentParser(prog=program_name, description='In App Commands') parser.add_argument('-v', '--version', action='version', version='%(prog)s '+version, help='Show the version number and exit') @@ -99,25 +99,30 @@ def setup_cli_parser(): # Subparser for 'use' command parser_use = subparsers.add_parser('use', help='Connect to a specified share', description='Connect to a specific share on the remote server', epilog='Example Usage: use sharename') parser_use.add_argument('share', help='Specify the share name to connect to') + parser_use.set_defaults(func=slingerClient.connect_share) # Subparser for 'ls' command parser_dir = subparsers.add_parser('ls', help='List directory contents', description='List contents of a directory at a specified path', epilog='Example Usage: ls /path/to/directory') parser_dir.add_argument('path', nargs='?', default=".", help='Path to list contents, defaults to current path') + parser_dir.set_defaults(func=slingerClient.dir_list) # Subparser for 'shares' command parser_shares = subparsers.add_parser('shares', help='List all available shares', aliases=['enumshares'], description='List all shares available on the remote server', epilog='Example Usage: shares') + parser_shares.set_defaults(func=slingerClient.list_shares) # Subparser for 'cat' command parser_cat = subparsers.add_parser('cat', help='Display file contents', description='Display the contents of a specified file on the remote server', epilog='Example Usage: cat /path/to/file') parser_cat.add_argument('remote_path', help='Specify the remote file path to display contents') + parser_cat.set_defaults(func=slingerClient.cat) # Subparser for 'cd' command parser_cd = subparsers.add_parser('cd', help='Change directory', description='Change to a different directory on the remote server', epilog='Example Usage: cd /path/to/directory') parser_cd.add_argument('path', nargs='?', default=".", help='Directory path to change to, defaults to current directory') + parser_cd.set_defaults(func=slingerClient.cd_handler) # Subparser for 'pwd' command parser_pwd = subparsers.add_parser('pwd', help='Print working directory', description='Print the current working directory on the remote server', epilog='Example Usage: pwd') - + parser_pwd.set_defaults(func=slingerClient.print_current_path) # Subparser for 'exit' command parser_exit = subparsers.add_parser('exit', help='Exit the program', description='Exit the application', epilog='Example Usage: exit') @@ -127,54 +132,82 @@ def setup_cli_parser(): # Subparser for 'who' command parser_who = subparsers.add_parser('who', help='List current sessions. This is different than the current user logins', description='List the current sessions connected to the target host', epilog='Example Usage: who') + parser_who.set_defaults(func=slingerClient.who) # Subparser for 'enumdisk' command parser_diskenum = subparsers.add_parser('enumdisk', help='Enumerate server disk', description='Enumerate server disk information', epilog='Example Usage: enumdisk') + parser_diskenum.set_defaults(func=slingerClient.enum_server_disk) # Subparser for 'enumlogons' command parser_logonsenum = subparsers.add_parser('enumlogons', help='Enumerate logged on users', description='Enumerate users currently logged on the server', epilog='Example Usage: enumlogons') + parser_logonsenum.set_defaults(func=slingerClient.enum_logons) # Subparser for 'enuminfo' command parser_infoenum = subparsers.add_parser('enuminfo', help='Enumerate remote host information', description='Enumerate detailed information about the remote host', epilog='Example Usage: enuminfo') + parser_infoenum.set_defaults(func=slingerClient.enum_info) # Subparser for 'enumsys' command parser_sysenum = subparsers.add_parser('enumsys', help='Enumerate remote host system information', description='Enumerate system information of the remote host', epilog='Example Usage: enumsys') + parser_sysenum.set_defaults(func=slingerClient.enum_sys) # Subparser for 'enumtransport' command parser_transenum = subparsers.add_parser('enumtransport', help='Enumerate remote host transport information', description='Enumerate transport information of the remote host', epilog='Example Usage: enumtransport') + parser_transenum.set_defaults(func=slingerClient.enum_transport) # Subparser for 'enumservices' command parser_svcenum = subparsers.add_parser('enumservices', help='Enumerate services', description='Enumerate services on the remote host', epilog='Example Usage: enumservices --filter name=spooler OR enumservices --filter state=running OR enumservices -n', - aliases=['servicesenum']) + aliases=['servicesenum','svcenum','services']) parser_svcenum.add_argument('-n', '--new', action='store_true', help='Perform a new enumeration of services even if already enumerated') parser_svcenum.add_argument('--filter', help='Filter services by name or state') + parser_svcenum.set_defaults(func=slingerClient.enum_services) # Subparser for 'serviceshow' command - parser_taskshow = subparsers.add_parser('serviceshow', help='Show details for a service', description='Show details of a specific service on the remote server', epilog='Example Usage: serviceshow -i 123', aliases=['svcshow']) - svcshowgroup = parser_taskshow.add_mutually_exclusive_group(required=True) + parser_svcshow = subparsers.add_parser('serviceshow', help='Show details for a service', description='Show details of a specific service on the remote server', epilog='Example Usage: serviceshow -i 123', aliases=['svcshow','showservice']) + parser_svcshow.set_defaults(func=slingerClient.show_service_handler) + svcshowgroup = parser_svcshow.add_mutually_exclusive_group(required=True) svcshowgroup.add_argument('-i', '--serviceid', type=int, help='Specify the ID of the service to show details for') svcshowgroup.add_argument('service_name', type=str, nargs='?', help='Specify the name of the service to show') - - parser_svcstart = subparsers.add_parser('servicestart', help='Start a service', description='Start a specified service on the remote server', epilog='Example Usage: servicestart -i 123 OR svcstart Spooler', aliases=['svcstart']) + + # Subparser for 'servicestart' command + parser_svcstart = subparsers.add_parser('servicestart', help='Start a service', description='Start a specified service on the remote server', epilog='Example Usage: servicestart -i 123 OR svcstart Spooler', aliases=['svcstart','servicestart','servicerun']) + parser_svcstart.set_defaults(func=slingerClient.start_service_handler) svcstartgroup = parser_svcstart.add_mutually_exclusive_group(required=True) svcstartgroup.add_argument('-i', '--serviceid', type=int, help='Specify the ID of the service to start') svcstartgroup.add_argument('service_name', type=str, nargs='?', help='Specify the name of the service to start') - parser_svcstop = subparsers.add_parser('servicestop', help='Stop a service', description='Stop a specified service on the remote server', epilog='Example Usage: servicestop -i 123 OR svcstop Spooler', aliases=['svcstop']) + # Subparser for 'servicestop' command + parser_svcstop = subparsers.add_parser('servicestop', help='Stop a service', description='Stop a specified service on the remote server', epilog='Example Usage: servicestop -i 123 OR svcstop Spooler', aliases=['svcstop','servicestop']) + parser_svcstop.set_defaults(func=slingerClient.service_stop_handler) svcstopgroup = parser_svcstop.add_mutually_exclusive_group(required=True) svcstopgroup.add_argument('-i', '--serviceid', type=int, help='Specify the ID of the service to stop') svcstopgroup.add_argument('service_name', type=str, nargs='?', help='Specify the name of the service to stop') + # Subparser for 'servicedel' command + parser_svcdelete = subparsers.add_parser('servicedel', help='Delete a service', description='Delete a specified service on the remote server', epilog='Example Usage: servicedelete -i 123 OR svcdelete Spooler', aliases=['svcdelete','servicedelete']) + svcdeletegroup = parser_svcdelete.add_mutually_exclusive_group(required=True) + svcdeletegroup.add_argument('-i', '--serviceid', type=int, help='Specify the ID of the service to delete') + svcdeletegroup.add_argument('service_name', type=str, nargs='?', help='Specify the name of the service to delete') + parser_svcdelete.set_defaults(func=slingerClient.service_del_handler) + + # Subparser for 'servicecreate' command + parser_svccreate = subparsers.add_parser('serviceadd', help='Create a new service', description='Create a new service on the remote server', epilog=r'Example Usage: -b "C:\nc.exe 10.0.0.26 8080 -e cmd.exe"', aliases=['svcadd','servicecreate','svccreate'], formatter_class=argparse.RawDescriptionHelpFormatter) + parser_svccreate.add_argument('-n', '--servicename', required=True, help='Specify the name of the new service') + parser_svccreate.add_argument('-b', '--binarypath', required=True, help='Specify the binary path of the new service') + parser_svccreate.add_argument('-d', '--displayname', required=True, help='Specify the display name of the new service') + parser_svccreate.add_argument('-s', '--starttype', choices=['auto','demand','system'], default="demand", required=True, help='Specify the start type of the new service') + parser_svccreate.set_defaults(func=slingerClient.create_service) + # Subparser for 'enumtasks' command parser_taskenum = subparsers.add_parser('enumtasks', help='Enumerate scheduled tasks', description='Enumerate scheduled tasks on the remote server', epilog='Example Usage: enumtasks', aliases=['tasksenum','taskenum']) - + parser_taskenum.set_defaults(func=slingerClient.enum_task_folders_recursive) # Subparser for 'tasksshow' command - parser_taskshow = subparsers.add_parser('tasksshow', help='Show task details', description='Show details of a specific task on the remote server', epilog='Example Usage: tasksshow -i 123', aliases=['taskshow']) + parser_taskshow = subparsers.add_parser('tasksshow', help='Show task details', description='Show details of a specific task on the remote server', epilog='Example Usage: tasksshow -i 123', aliases=['taskshow','showtask']) taskshowgroup = parser_taskshow.add_mutually_exclusive_group(required=True) taskshowgroup.add_argument('-i', '--taskid', type=int, help='Specify the ID of the task to show') taskshowgroup.add_argument('task_path', type=str, nargs='?', help='Specify the full path of the task to show') - taskshowgroup.add_argument('-f', '--folder', type=str, nargs='?', help='Specify the folder to show tasks from') + #taskshowgroup.add_argument('-f', '--folder', type=str, nargs='?', help='Specify the folder to show tasks from') + parser_taskshow.set_defaults(func=slingerClient.task_show_handler) # Subparser for 'taskcreate' command parser_taskcreate = subparsers.add_parser('taskcreate', help='Create a new task', description='Create a new scheduled task on the remote server', epilog="Example Usage: taskcreate -n newtask -p cmd.exe -a '/c ipconfig /all > C:\\test' -f \\\\Windows", aliases=['taskadd'], formatter_class=argparse.RawDescriptionHelpFormatter) @@ -182,27 +215,33 @@ def setup_cli_parser(): parser_taskcreate.add_argument('-p', '--program', required=True, help='Specify the program to run in the task') parser_taskcreate.add_argument('-a', '--arguments', required=True, help='Specify the arguments to pass to the program') parser_taskcreate.add_argument('-f', '--folder', required=True, default="\\", help='Specify the folder to create the task in') + parser_taskcreate.set_defaults(func=slingerClient.task_create) # Subparser for 'taskrun' command - parser_taskrun = subparsers.add_parser('taskrun', help='Run a task', description='Run a specified task on the remote server', epilog='Example Usage: taskrun /path/to/task', aliases=['taskexec']) + parser_taskrun = subparsers.add_parser('taskrun', help='Run a task', description='Run a specified task on the remote server', epilog='Example Usage: taskrun \\\\Windows\\\\newtask', aliases=['taskexec']) parser_taskrun.add_argument('task_path', type=str, help='Specify the full path of the task to run') + parser_taskrun.set_defaults(func=slingerClient.task_run) # Subparser for 'taskdelete' command parser_taskdelete = subparsers.add_parser('taskdelete', help='Delete a task', description='Delete a specified task on the remote server', epilog='Example Usage: taskdelete -i 123', aliases=['taskdel','taskrm']) taskdeletegroup = parser_taskdelete.add_mutually_exclusive_group(required=True) taskdeletegroup.add_argument('task_path', type=str, nargs='?', help='Specify the full path of the task to delete') taskdeletegroup.add_argument('-i', '--taskid', type=int, help='Specify the ID of the task to delete') + parser_taskdelete.set_defaults(func=slingerClient.task_delete_handler) # Subparser for 'enumtime' command parser_time = subparsers.add_parser('enumtime', help='Get server time', description='Get the current time on the server', epilog='Example Usage: enumtime') + parser_time.set_defaults(func=slingerClient.get_server_time) # Subparser for 'upload' command parser_upload = subparsers.add_parser('upload', aliases=['put'], help='Upload a file', description='Upload a file to the remote server', epilog='Example Usage: upload /local/path /remote/path') + parser_upload.set_defaults(func=slingerClient.upload_handler) parser_upload.add_argument('local_path', help='Specify the local file path to upload') parser_upload.add_argument('remote_path', nargs='?', help='Specify the remote file path to upload to, optional') # Subparser for 'download' command parser_download = subparsers.add_parser('download', aliases=['get'], help='Download a file', description='Download a file from the remote server', epilog='Example Usage: download /remote/path /local/path') + parser_download.set_defaults(func=slingerClient.download_handler) parser_download.add_argument('remote_path', help='Specify the remote file path to download') parser_download.add_argument('local_path', nargs='?', help='Specify the local file path to download to, optional', default=None) @@ -213,18 +252,21 @@ def setup_cli_parser(): parser_mget.add_argument('-r', action='store_true', help='Recurse into directories') parser_mget.add_argument('-p', metavar='regex', help='Specify a regex pattern to match filenames') parser_mget.add_argument('-d', type=int, default=2, help='Specify folder depth count for recursion') + parser_mget.set_defaults(func=slingerClient.mget_handler) # Subparser for 'mkdir' command parser_mkdir = subparsers.add_parser('mkdir', help='Create a new directory', description='Create a new directory on the remote server', epilog='Example Usage: mkdir /path/to/new/directory') parser_mkdir.add_argument('path', help='Specify the path of the directory to create') + parser_mkdir.set_defaults(func=slingerClient.mkdir) # Subparser for 'rmdir' command parser_rmdir = subparsers.add_parser('rmdir', help='Remove a directory', description='Remove a directory on the remote server', epilog='Example Usage: rmdir /path/to/remote/directory') parser_rmdir.add_argument('remote_path', help='Specify the remote path of the directory to remove') - + parser_rmdir.set_defaults(func=slingerClient.rmdir) # Subparser for 'rm' command parser_rm = subparsers.add_parser('rm', help='Delete a file', description='Delete a file on the remote server', epilog='Example Usage: rm /path/to/remote/file') parser_rm.add_argument('remote_path', help='Specify the remote file path to delete') + parser_rm.set_defaults(func=slingerClient.rm_handler) # Subparser for '#shell' command parser_shell = subparsers.add_parser('#shell', help='Enter local terminal mode', description='Enter local terminal mode for command execution', epilog='Example Usage: #shell') @@ -236,52 +278,62 @@ def setup_cli_parser(): # Subparser for 'info' command parser_info = subparsers.add_parser('info', help='Display session status', description='Display the status of the current session', epilog='Example Usage: info') - # No arguments needed for info + parser_info.set_defaults(func=slingerClient.info) + + parser_regstart = subparsers.add_parser('reguse', aliases=['regstart'],help='Connect to the remote registry', description='Connect to a remote registry on the remote server', epilog='Example Usage: reguse') + parser_regstart.set_defaults(func=slingerClient.setup_remote_registry) - parser_regstart = subparsers.add_parser('reguse', help='Connect to the remote registry', description='Connect to a remote registry on the remote server', epilog='Example Usage: reguse') parser_regstop = subparsers.add_parser('regstop', help='Disconnect from the remote registry', description='Disconnect from a remote registry on the remote server', epilog='Example Usage: regstop') - + parser_regstop.set_defaults(func=slingerClient.stop_remote_registry) + parser_regquery = subparsers.add_parser('regquery', help='Query a registry key', description='Query a registry key on the remote server', epilog='Example Usage: regquery HKLM\\\\SOFTWARE\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Run') parser_regquery.add_argument('key', help='Specify the registry key to query') - parser_regquery.add_argument('-l', '--list', help='List all values in the registry key', action='store_true') + parser_regquery.add_argument('-l', '--list', help='List all subkeys in the registry key', action='store_true') parser_regquery.add_argument('-v', '--value', help='Enumerate the value of the specified registry key', action='store_true') + parser_regquery.set_defaults(func=slingerClient.reg_query_handler) parser_regset = subparsers.add_parser('regset', help='Set a registry value', description='Set a registry value on the remote server', epilog='Example Usage: regset -k HKLM\\\\SOFTWARE\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Run\\\\ -v test -d "C:\\test.exe"') parser_regset.add_argument('-k', '--key', help='Specify the registry key to set', required=True) parser_regset.add_argument('-v', '--value', help='Specify the registry value to set', required=True) parser_regset.add_argument('-d', '--data', help='Specify the registry data to set', required=True) parser_regset.add_argument('-t', '--type', help='Specify the registry type to set', default="REG_SZ", required=False) + parser_regset.set_defaults(func=slingerClient.add_reg_value_handler) parser_regdel = subparsers.add_parser('regdel', help='Delete a registry value', description='Delete a registry value on the remote server', epilog='Example Usage: regdel -k HKLM\\\\SOFTWARE\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Run\\\\ -v test') parser_regdel.add_argument('-k', '--key', help='Specify the registry key to delete', required=True) parser_regdel.add_argument('-v', '--value', help='Specify the registry value to delete', required=False) + parser_regdel.set_defaults(func=slingerClient.reg_delete_handler) parser_regcreate = subparsers.add_parser('regcreate', help='Create a registry key', description='Create a registry key on the remote server', epilog='Example Usage: regcreate -k HKLM\\\\SOFTWARE\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Run\\\\test') parser_regcreate.add_argument('key', help='Specify the registry key to create') + parser_regcreate.set_defaults(func=slingerClient.reg_create_key) parser_regcheck = subparsers.add_parser('regcheck', help='Check if a registry key exists', description='Check if a registry key exists on the remote server. This is really just an exposed helper function.', epilog='Example Usage: regcheck HKLM\\\\SOFTWARE\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Run\\\\test') parser_regcheck.add_argument('key', help='Specify the registry key to check') + parser_regcheck.set_defaults(func=slingerClient.does_key_exist) parser_portfwd = subparsers.add_parser('portfwd', help='Forward a local port to a remote port', description='Forward a local port to a remote port on the remote server', epilog='Example Usage: portfwd (-a|-d) [lhost]:[lport] [rhost]:[rport]') - parser_portfwdgroup = parser_portfwd.add_mutually_exclusive_group(required=False) + parser_portfwd.set_defaults(func=slingerClient.port_fwd_handler) + parser_portfwdgroup = parser_portfwd.add_mutually_exclusive_group(required=True) - parser_portfwd.add_argument('local', help='Specify the local host and port to forward from') - parser_portfwd.add_argument('remote', help='Specify the remote host and port to forward to') + parser_portfwd.add_argument('local', help='Specify the local host and port to forward from', default=None) + parser_portfwd.add_argument('remote', help='Specify the remote host and port to forward to', default=None) parser_portfwdgroup.add_argument('-d', '--remove', help='Remove a port forwarding rule', action='store_true') parser_portfwdgroup.add_argument('-a', '--add', help='Add a port forwarding rule', action='store_true') parser_portfwdgroup.add_argument('-l', '--list', help='List all port forwarding rules', action='store_true') + parser_portfwdgroup.add_argument('-c', '--clear', help='Clear all port forwarding rules', action='store_true') + parser_portfwdgroup.add_argument('--load', help='Load all port forwarding rules from the registry', action='store_true') - - parser_portfwdrules = subparsers.add_parser('portfwdrules', help='Display port forwarding rules', description='Display port forwarding rules on the remote server', epilog='Example Usage: portfwdrules') - parser_portfwdrules.add_argument('-l', '--load', help='Load all port forwarding rules from the registry', action='store_true', required=False) parser_ifconfig = subparsers.add_parser('ifconfig', help='Display network interfaces', aliases=["ipconfig", "enuminterfaces"], description='Display network interfaces on the remote server', epilog='Example Usage: ifconfig') - parser_hostname = subparsers.add_parser('hostname', help='Display hostname', description='Display the hostname of the remote server', epilog='Example Usage: hostname') + parser_ifconfig.set_defaults(func=slingerClient.ipconfig) + parser_hostname = subparsers.add_parser('hostname', help='Display hostname', description='Display the hostname of the remote server', epilog='Example Usage: hostname') + parser_hostname.set_defaults(func=slingerClient.hostname) parser_fwrules = subparsers.add_parser('fwrules', help='Display firewall rules', description='Display firewall rules on the remote server', epilog='Example Usage: fwrules') - + parser_fwrules.set_defaults(func=slingerClient.show_fw_rules) parser_setvar = subparsers.add_parser('set', help='Set a variable', description='Set a variable for use in the application', epilog='Example Usage: set varname value') @@ -292,8 +344,16 @@ def setup_cli_parser(): return parser -# A function to extract all possible commands and arguments from argparse - +def get_subparser_aliases(command, parser): + for action in parser._actions: + if isinstance(action, argparse._SubParsersAction): + for name, subparser in action.choices.items(): + if command == name: + # Inspect the subparser to find aliases + for alias, alias_parser in action.choices.items(): + if alias != name and alias_parser is subparser: + return [alias for alias in action.choices if alias != name and action.choices[alias] is subparser] + return [] # Custom Completer class using argparse commands and arguments diff --git a/slinger/utils/common.py b/slinger/utils/common.py index 5cf3338..2a6cdbf 100755 --- a/slinger/utils/common.py +++ b/slinger/utils/common.py @@ -4,7 +4,7 @@ import xml.etree.ElementTree as ET import re from impacket.dcerpc.v5 import rrp, srvs, wkst, tsch, scmr -from slinger.utils.printlib import print_bad, print_debug, print_good, print_info, print_log, print_warning +from slinger.utils.printlib import * from slinger.var.config import config_vars from tabulate import tabulate @@ -101,9 +101,9 @@ def get_config_value(key): for c in config_vars: if c["Name"].lower() == key.lower(): return c["Value"] - print_warning(f"Config variable {key} does not exist") + print_warning(f"Config variable '{key}' does not exist") except KeyError: - print_warning(f"Config variable {key} does not exist") + print_warning(f"Config variable '{key}' does not exist") return # function to set a value in the config dictionary @@ -117,16 +117,16 @@ def set_config_value(key, value): try: c["Value"] = int(value) except ValueError: - print_warning(f"Invalid value for {key}, needs to be an integer") + print_warning(f"Invalid value for '{key}', needs to be an integer") else: c["Value"] = value print_log(f"{key} --> {str(c['Value'])}") return - print_warning(f"Config variable {key} does not exist") + print_warning(f"Config variable '{key}' does not exist") except KeyError: - print_warning(f"Config variable {key} does not exist") + print_warning(f"Config variable '{key}' does not exist") return