From 55eb30ce49e79cb5b17f083e7d04d7940d324c88 Mon Sep 17 00:00:00 2001 From: "Remi GASCOU (Podalirius)" <79218792+p0dalirius@users.noreply.github.com> Date: Fri, 21 Jun 2024 22:42:00 +0200 Subject: [PATCH 1/2] -h Signed-off-by: Remi GASCOU (Podalirius) <79218792+p0dalirius@users.noreply.github.com> --- smbclientng/__main__.py | 4 + smbclientng/core/Config.py | 2 + smbclientng/core/InteractiveShell.py | 270 +++++++++++++-------------- 3 files changed, 139 insertions(+), 137 deletions(-) diff --git a/smbclientng/__main__.py b/smbclientng/__main__.py index c5d1897..f347141 100644 --- a/smbclientng/__main__.py +++ b/smbclientng/__main__.py @@ -28,6 +28,9 @@ def parseArgs(): parser.add_argument("--debug", dest="debug", action="store_true", default=False, help="Debug mode.") parser.add_argument("--no-colors", dest="no_colors", action="store_true", default=False, help="No colors mode.") parser.add_argument("--target", action="store", metavar="ip address", required=True, type=str, help="IP Address of the SMB Server to connect to.") + + parser.add_argument("--script", metavar="script", required=False, type=str, help="File containing the list of commands to be typed at start of the console.") + parser.add_argument("-N", "--not-interactive", dest="not_interactive", required=False, action="store_true", default=False, help="Non interactive mode.") authconn = parser.add_argument_group("Authentication & connection") authconn.add_argument("--kdcHost", dest="kdcHost", action="store", metavar="FQDN KDC", help="FQDN of KDC for Kerberos.") @@ -94,6 +97,7 @@ def main(): config = Config() config.debug = options.debug config.no_colors = options.no_colors + config.not_interactive = options.not_interactive smbSession = SMBSession( address=options.target, diff --git a/smbclientng/core/Config.py b/smbclientng/core/Config.py index a8ff644..a68373d 100644 --- a/smbclientng/core/Config.py +++ b/smbclientng/core/Config.py @@ -23,6 +23,8 @@ class Config(object): no_colors: Property to get or set the colored output preference. """ + not_interactive = False + def __init__(self, debug=False, no_colors=None): self._debug = debug diff --git a/smbclientng/core/InteractiveShell.py b/smbclientng/core/InteractiveShell.py index 003ec8c..5472842 100644 --- a/smbclientng/core/InteractiveShell.py +++ b/smbclientng/core/InteractiveShell.py @@ -77,6 +77,9 @@ class InteractiveShell(object): __init__(self, smbSession, debug=False): Initializes the InteractiveShell with the given SMB session and debug mode. run(self): Starts the command line interface loop, processing user input until exit. """ + + running = True + modules = {} def __init__(self, smbSession, config): # Objects @@ -87,42 +90,13 @@ def __init__(self, smbSession, config): readline.parse_and_bind("tab: complete") readline.set_completer_delims("\n") # Additional modules - self.modules = {} self.__load_modules() def run(self): - running = True - while running: + while self.running: try: user_input = input(self.__prompt()).strip() - tokens = shlex.split(user_input) - - if len(tokens) == 0: - command = "" - arguments = [] - elif len(tokens) == 1: - command = tokens[0].lower() - arguments = [] - else: - command = tokens[0].lower() - arguments = tokens[1:] - - # Exit the command line - if command == "exit": - running = False - # Skip - elif command.strip() == "": - pass - # Execute the command - elif command in self.commandCompleterObject.commands.keys(): - self.process_command( - command=command, - arguments=arguments - ) - # Fallback to unknown command - else: - print("Unknown command. Type \"help\" for help.") - + self.process_line(commandLine=user_input) except KeyboardInterrupt as e: print() @@ -135,138 +109,160 @@ def run(self): traceback.print_exc() print("[!] Error: %s" % str(e)) - def process_command(self, command, arguments=[]): + def process_line(self, commandLine): + # Split and parse the commandLine + tokens = shlex.split(commandLine) + if len(tokens) == 0: + command = "" + arguments = [] + elif len(tokens) == 1: + command = tokens[0].lower() + arguments = [] + else: + command = tokens[0].lower() + arguments = tokens[1:] + # Skip if command.strip() == "": pass - - # Display help - elif command == "help": - self.command_help(arguments, command) - - # Cat the contents of a file - elif command == "bat": - self.command_bat(arguments, command) - - # Cat the contents of a file - elif command == "cat": - self.command_cat(arguments, command) - - # Closes the current SMB session - elif command == "close": - self.command_close(arguments, command) - - # Change directory in the current share - elif command == "cd": - self.command_cd(arguments, command) - - # debug - elif command == "debug": - self.command_debug(arguments, command) + # Execute the command + elif command in self.commandCompleterObject.commands.keys(): + + # Exit the command line + if command in ["exit", "quit"]: + self.running = False + + # Display help + elif command == "help": + self.command_help(arguments, command) + + # Cat the contents of a file + elif command == "bat": + self.command_bat(arguments, command) - # Get a file - elif command == "get": - self.command_get(arguments, command) + # Cat the contents of a file + elif command == "cat": + self.command_cat(arguments, command) - # SMB server info - elif command == "info": - self.command_info(arguments, command) + # Closes the current SMB session + elif command == "close": + self.command_close(arguments, command) + + # Change directory in the current share + elif command == "cd": + self.command_cd(arguments, command) + + # debug + elif command == "debug": + self.command_debug(arguments, command) - # List directory contents in a share - elif command in ["ls", "dir"]: - self.command_ls(arguments, command) + # Get a file + elif command == "get": + self.command_get(arguments, command) - # Creates a new remote directory - elif command == "mkdir": - self.command_mkdir(arguments, command) + # SMB server info + elif command == "info": + self.command_info(arguments, command) - # Put a file - elif command == "put": - self.command_put(arguments, command) + # List directory contents in a share + elif command in ["ls", "dir"]: + self.command_ls(arguments, command) - # Shows the content of a local file - elif command == "lcat": - self.command_lcat(arguments, command) + # Creates a new remote directory + elif command == "mkdir": + self.command_mkdir(arguments, command) - # Changes the current local directory - elif command == "lcd": - self.command_lcd(arguments, command) + # Put a file + elif command == "put": + self.command_put(arguments, command) - # Creates a copy of a local file - elif command == "lcp": - self.command_lcp(arguments, command) + # Shows the content of a local file + elif command == "lcat": + self.command_lcat(arguments, command) - # Pretty prints the content of a local file - elif command == "lbat": - self.command_lbat(arguments, command) + # Changes the current local directory + elif command == "lcd": + self.command_lcd(arguments, command) - # Lists the contents of the current local directory - elif command == "lls": - self.command_lls(arguments, command) + # Creates a copy of a local file + elif command == "lcp": + self.command_lcp(arguments, command) - # Creates a new local directory - elif command == "lmkdir": - self.command_lmkdir(arguments, command) + # Pretty prints the content of a local file + elif command == "lbat": + self.command_lbat(arguments, command) - # Shows the current local directory - elif command == "lpwd": - self.command_lpwd(arguments, command) + # Lists the contents of the current local directory + elif command == "lls": + self.command_lls(arguments, command) - # Renames a local file - elif command == "lrename": - self.command_lrename(arguments, command) - - # Removes a local file - elif command == "lrm": - self.command_lrm(arguments, command) + # Creates a new local directory + elif command == "lmkdir": + self.command_lmkdir(arguments, command) - # Removes a local directory - elif command == "lrmdir": - self.command_lrmdir(arguments, command) + # Shows the current local directory + elif command == "lpwd": + self.command_lpwd(arguments, command) - # Shows the current local directory - elif command == "ltree": - self.command_ltree(arguments, command) + # Renames a local file + elif command == "lrename": + self.command_lrename(arguments, command) + + # Removes a local file + elif command == "lrm": + self.command_lrm(arguments, command) - # Modules - elif command == "module": - self.command_module(arguments, command) + # Removes a local directory + elif command == "lrmdir": + self.command_lrmdir(arguments, command) - # Creates a mount point of the remote share on the local machine - elif command == "mount": - self.command_mount(arguments, command) + # Shows the current local directory + elif command == "ltree": + self.command_ltree(arguments, command) - # Reconnects the current SMB session - elif command in ["connect", "reconnect"]: - self.command_reconnect(arguments, command) + # Modules + elif command == "module": + self.command_module(arguments, command) - # Reset the TTY output - elif command == "reset": - self.command_reset(arguments, command) + # Creates a mount point of the remote share on the local machine + elif command == "mount": + self.command_mount(arguments, command) - # Removes a remote file - elif command == "rm": - self.command_rm(arguments, command) - - # Removes a remote directory - elif command == "rmdir": - self.command_rmdir(arguments, command) + # Reconnects the current SMB session + elif command in ["connect", "reconnect"]: + self.command_reconnect(arguments, command) - # List shares - elif command == "sizeof": - self.command_sizeof(arguments, command) + # Reset the TTY output + elif command == "reset": + self.command_reset(arguments, command) - # List shares - elif command == "shares": - self.command_shares(arguments, command) - - # Displays a tree view of the CWD - elif command == "tree": - self.command_tree(arguments, command) + # Removes a remote file + elif command == "rm": + self.command_rm(arguments, command) + + # Removes a remote directory + elif command == "rmdir": + self.command_rmdir(arguments, command) + + # List shares + elif command == "sizeof": + self.command_sizeof(arguments, command) + + # List shares + elif command == "shares": + self.command_shares(arguments, command) + + # Displays a tree view of the CWD + elif command == "tree": + self.command_tree(arguments, command) + + # Use a share + elif command == "use": + self.command_use(arguments, command) - # Use a share - elif command == "use": - self.command_use(arguments, command) + # Fallback to unknown command + else: + print("Unknown command. Type \"help\" for help.") # Commands ================================================================ From a6c04b3c7c75ac746dcbd7290299db26aec520fd Mon Sep 17 00:00:00 2001 From: "Remi GASCOU (Podalirius)" <79218792+p0dalirius@users.noreply.github.com> Date: Fri, 21 Jun 2024 22:49:51 +0200 Subject: [PATCH 2/2] Implemented startup scripts, Fixes #50 --- smbclientng/__main__.py | 8 ++++- smbclientng/core/Config.py | 1 + smbclientng/core/InteractiveShell.py | 46 ++++++++++++++++++++-------- 3 files changed, 41 insertions(+), 14 deletions(-) diff --git a/smbclientng/__main__.py b/smbclientng/__main__.py index f347141..db7ab5c 100644 --- a/smbclientng/__main__.py +++ b/smbclientng/__main__.py @@ -29,7 +29,7 @@ def parseArgs(): parser.add_argument("--no-colors", dest="no_colors", action="store_true", default=False, help="No colors mode.") parser.add_argument("--target", action="store", metavar="ip address", required=True, type=str, help="IP Address of the SMB Server to connect to.") - parser.add_argument("--script", metavar="script", required=False, type=str, help="File containing the list of commands to be typed at start of the console.") + parser.add_argument("-S", "--startup-script", metavar="startup_script", required=False, type=str, help="File containing the list of commands to be typed at start of the console.") parser.add_argument("-N", "--not-interactive", dest="not_interactive", required=False, action="store_true", default=False, help="Non interactive mode.") authconn = parser.add_argument_group("Authentication & connection") @@ -51,6 +51,11 @@ def parseArgs(): options = parser.parse_args() + if options.not_interactive and options.startup_script is None: + print("[+] Option --not-interactive without --startup-script does not make any sense.") + parser.print_help() + sys.exit(1) + if options.auth_username is not None and (options.auth_password is None and options.no_pass == False and options.auth_hashes is None): print("[+] No password or hashes provided and --no-pass is '%s'" % options.no_pass) from getpass import getpass @@ -98,6 +103,7 @@ def main(): config.debug = options.debug config.no_colors = options.no_colors config.not_interactive = options.not_interactive + config.startup_script = options.startup_script smbSession = SMBSession( address=options.target, diff --git a/smbclientng/core/Config.py b/smbclientng/core/Config.py index a68373d..b4a0004 100644 --- a/smbclientng/core/Config.py +++ b/smbclientng/core/Config.py @@ -24,6 +24,7 @@ class Config(object): """ not_interactive = False + startup_script = None def __init__(self, debug=False, no_colors=None): self._debug = debug diff --git a/smbclientng/core/InteractiveShell.py b/smbclientng/core/InteractiveShell.py index 5472842..b16fb53 100644 --- a/smbclientng/core/InteractiveShell.py +++ b/smbclientng/core/InteractiveShell.py @@ -93,21 +93,41 @@ def __init__(self, smbSession, config): self.__load_modules() def run(self): - while self.running: - try: - user_input = input(self.__prompt()).strip() - self.process_line(commandLine=user_input) - except KeyboardInterrupt as e: - print() + # Read commands from script file first + if self.config.script: + f = open(self.config.script, 'r') + for line in f.readlines(): + try: + self.process_line(commandLine=line.strip()) + except KeyboardInterrupt as e: + print() - except EOFError as e: - print() - running = False + except EOFError as e: + print() + running = False + + except Exception as e: + if self.config.debug: + traceback.print_exc() + print("[!] Error: %s" % str(e)) + + # Then interactive console + if not self.config.not_interactive: + while self.running: + try: + user_input = input(self.__prompt()).strip() + self.process_line(commandLine=user_input) + except KeyboardInterrupt as e: + print() - except Exception as e: - if self.config.debug: - traceback.print_exc() - print("[!] Error: %s" % str(e)) + except EOFError as e: + print() + running = False + + except Exception as e: + if self.config.debug: + traceback.print_exc() + print("[!] Error: %s" % str(e)) def process_line(self, commandLine): # Split and parse the commandLine