Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[enhancement] Add option to read interactive commands from a file #54

Merged
merged 3 commits into from
Jun 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion smbclientng/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,10 @@ 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("-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.")
parser.add_argument("--port", action="store", metavar="port of smb service", type=int, default='445', help="Port of the SMB Server to connect to.")


authconn = parser.add_argument_group("Authentication & connection")
authconn.add_argument("--kdcHost", dest="kdcHost", action="store", metavar="FQDN KDC", help="FQDN of KDC for Kerberos.")
authconn.add_argument("-d", "--domain", dest="auth_domain", metavar="DOMAIN", action="store", default='.', help="(FQDN) domain to authenticate to.")
Expand All @@ -50,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
Expand Down Expand Up @@ -96,6 +102,8 @@ def main():
config = Config()
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,
Expand Down
3 changes: 3 additions & 0 deletions smbclientng/core/Config.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ class Config(object):
no_colors: Property to get or set the colored output preference.
"""

not_interactive = False
startup_script = None

def __init__(self, debug=False, no_colors=None):
self._debug = debug

Expand Down
308 changes: 162 additions & 146 deletions smbclientng/core/InteractiveShell.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -87,186 +90,199 @@ 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:
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":
# 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
# 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.")

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
# 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

def process_command(self, command, arguments=[]):
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
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():

# Get a file
elif command == "get":
self.command_get(arguments, command)
# Exit the command line
if command in ["exit", "quit"]:
self.running = False

# Display help
elif command == "help":
self.command_help(arguments, command)

# SMB server info
elif command == "info":
self.command_info(arguments, command)
# Cat the contents of a file
elif command == "bat":
self.command_bat(arguments, command)

# List directory contents in a share
elif command in ["ls", "dir"]:
self.command_ls(arguments, command)
# Cat the contents of a file
elif command == "cat":
self.command_cat(arguments, command)

# Creates a new remote directory
elif command == "mkdir":
self.command_mkdir(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)

# Put a file
elif command == "put":
self.command_put(arguments, command)
# Get a file
elif command == "get":
self.command_get(arguments, command)

# Shows the content of a local file
elif command == "lcat":
self.command_lcat(arguments, command)
# SMB server info
elif command == "info":
self.command_info(arguments, command)

# Changes the current local directory
elif command == "lcd":
self.command_lcd(arguments, command)
# List directory contents in a share
elif command in ["ls", "dir"]:
self.command_ls(arguments, command)

# Creates a copy of a local file
elif command == "lcp":
self.command_lcp(arguments, command)
# Creates a new remote directory
elif command == "mkdir":
self.command_mkdir(arguments, command)

# Pretty prints the content of a local file
elif command == "lbat":
self.command_lbat(arguments, command)
# Put a file
elif command == "put":
self.command_put(arguments, command)

# Lists the contents of the current local directory
elif command == "lls":
self.command_lls(arguments, command)
# Shows the content of a local file
elif command == "lcat":
self.command_lcat(arguments, command)

# Creates a new local directory
elif command == "lmkdir":
self.command_lmkdir(arguments, command)
# Changes the current local directory
elif command == "lcd":
self.command_lcd(arguments, command)

# Shows the current local directory
elif command == "lpwd":
self.command_lpwd(arguments, command)
# Creates a copy of a local file
elif command == "lcp":
self.command_lcp(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)
# Pretty prints the content of a local file
elif command == "lbat":
self.command_lbat(arguments, command)

# Removes a local directory
elif command == "lrmdir":
self.command_lrmdir(arguments, command)
# Lists the contents of the current local directory
elif command == "lls":
self.command_lls(arguments, command)

# Shows the current local directory
elif command == "ltree":
self.command_ltree(arguments, command)
# Creates a new local directory
elif command == "lmkdir":
self.command_lmkdir(arguments, command)

# Modules
elif command == "module":
self.command_module(arguments, command)
# Shows the current local directory
elif command == "lpwd":
self.command_lpwd(arguments, command)

# Creates a mount point of the remote share on the local machine
elif command == "mount":
self.command_mount(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)

# Reconnects the current SMB session
elif command in ["connect", "reconnect"]:
self.command_reconnect(arguments, command)
# Removes a local directory
elif command == "lrmdir":
self.command_lrmdir(arguments, command)

# Reset the TTY output
elif command == "reset":
self.command_reset(arguments, command)
# Shows the current local directory
elif command == "ltree":
self.command_ltree(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)
# Modules
elif command == "module":
self.command_module(arguments, command)

# List shares
elif command == "sizeof":
self.command_sizeof(arguments, command)
# Creates a mount point of the remote share on the local machine
elif command == "mount":
self.command_mount(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)
# Reconnects the current SMB session
elif command in ["connect", "reconnect"]:
self.command_reconnect(arguments, command)

# Reset the TTY output
elif command == "reset":
self.command_reset(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 ================================================================

Expand Down
Loading