From 509ed1e96f9ae188c54deab1fc03e86f5735cfcf Mon Sep 17 00:00:00 2001 From: "Remi GASCOU (Podalirius)" <79218792+p0dalirius@users.noreply.github.com> Date: Sun, 2 Jun 2024 16:10:29 +0200 Subject: [PATCH] Added documentation --- documentation/index.html | 7 + documentation/search.js | 46 + documentation/smbclientng.html | 245 ++ documentation/smbclientng/core.html | 244 ++ .../smbclientng/core/CommandCompleter.html | 1547 +++++++ documentation/smbclientng/core/Config.html | 453 ++ .../smbclientng/core/InteractiveShell.html | 2601 ++++++++++++ .../smbclientng/core/LocalFileIO.html | 945 +++++ documentation/smbclientng/core/Module.html | 529 +++ .../core/ModuleArgumentParser.html | 425 ++ .../smbclientng/core/SMBSession.html | 3716 +++++++++++++++++ documentation/smbclientng/core/utils.html | 1055 +++++ documentation/smbclientng/modules.html | 246 ++ documentation/smbclientng/modules/Find.html | 935 +++++ run-tests.py | 28 - smbclientng/__main__.py | 16 + smbclientng/core/Config.py | 15 + smbclientng/core/InteractiveShell.py | 13 + smbclientng/core/ModuleArgumentParser.py | 27 +- smbclientng/core/utils.py | 35 +- smbclientng/modules/Find.py | 12 + smbclientng/tests/__init__.py | 5 - smbclientng/tests/common.py | 65 - smbclientng/tests/run-tests.py | 28 - smbclientng/tests/test_SMBSession.py | 22 - .../tests/test_SMBSession_path_isdir.py | 43 - .../tests/test_SMBSession_path_isfile.py | 43 - 27 files changed, 13108 insertions(+), 238 deletions(-) create mode 100644 documentation/index.html create mode 100644 documentation/search.js create mode 100644 documentation/smbclientng.html create mode 100644 documentation/smbclientng/core.html create mode 100644 documentation/smbclientng/core/CommandCompleter.html create mode 100644 documentation/smbclientng/core/Config.html create mode 100644 documentation/smbclientng/core/InteractiveShell.html create mode 100644 documentation/smbclientng/core/LocalFileIO.html create mode 100644 documentation/smbclientng/core/Module.html create mode 100644 documentation/smbclientng/core/ModuleArgumentParser.html create mode 100644 documentation/smbclientng/core/SMBSession.html create mode 100644 documentation/smbclientng/core/utils.html create mode 100644 documentation/smbclientng/modules.html create mode 100644 documentation/smbclientng/modules/Find.html delete mode 100644 run-tests.py delete mode 100644 smbclientng/tests/__init__.py delete mode 100644 smbclientng/tests/common.py delete mode 100644 smbclientng/tests/run-tests.py delete mode 100644 smbclientng/tests/test_SMBSession.py delete mode 100644 smbclientng/tests/test_SMBSession_path_isdir.py delete mode 100644 smbclientng/tests/test_SMBSession_path_isfile.py diff --git a/documentation/index.html b/documentation/index.html new file mode 100644 index 0000000..d4780a8 --- /dev/null +++ b/documentation/index.html @@ -0,0 +1,7 @@ + + +
+ + + + diff --git a/documentation/search.js b/documentation/search.js new file mode 100644 index 0000000..e403dc1 --- /dev/null +++ b/documentation/search.js @@ -0,0 +1,46 @@ +window.pdocSearch = (function(){ +/** elasticlunr - http://weixsong.github.io * Copyright (C) 2017 Oliver Nightingale * Copyright (C) 2017 Wei Song * MIT Licensed */!function(){function e(e){if(null===e||"object"!=typeof e)return e;var t=e.constructor();for(var n in e)e.hasOwnProperty(n)&&(t[n]=e[n]);return t}var t=function(e){var n=new t.Index;return n.pipeline.add(t.trimmer,t.stopWordFilter,t.stemmer),e&&e.call(n,n),n};t.version="0.9.5",lunr=t,t.utils={},t.utils.warn=function(e){return function(t){e.console&&console.warn&&console.warn(t)}}(this),t.utils.toString=function(e){return void 0===e||null===e?"":e.toString()},t.EventEmitter=function(){this.events={}},t.EventEmitter.prototype.addListener=function(){var e=Array.prototype.slice.call(arguments),t=e.pop(),n=e;if("function"!=typeof t)throw new TypeError("last argument must be a function");n.forEach(function(e){this.hasHandler(e)||(this.events[e]=[]),this.events[e].push(t)},this)},t.EventEmitter.prototype.removeListener=function(e,t){if(this.hasHandler(e)){var n=this.events[e].indexOf(t);-1!==n&&(this.events[e].splice(n,1),0==this.events[e].length&&delete this.events[e])}},t.EventEmitter.prototype.emit=function(e){if(this.hasHandler(e)){var t=Array.prototype.slice.call(arguments,1);this.events[e].forEach(function(e){e.apply(void 0,t)},this)}},t.EventEmitter.prototype.hasHandler=function(e){return e in this.events},t.tokenizer=function(e){if(!arguments.length||null===e||void 0===e)return[];if(Array.isArray(e)){var n=e.filter(function(e){return null===e||void 0===e?!1:!0});n=n.map(function(e){return t.utils.toString(e).toLowerCase()});var i=[];return n.forEach(function(e){var n=e.split(t.tokenizer.seperator);i=i.concat(n)},this),i}return e.toString().trim().toLowerCase().split(t.tokenizer.seperator)},t.tokenizer.defaultSeperator=/[\s\-]+/,t.tokenizer.seperator=t.tokenizer.defaultSeperator,t.tokenizer.setSeperator=function(e){null!==e&&void 0!==e&&"object"==typeof e&&(t.tokenizer.seperator=e)},t.tokenizer.resetSeperator=function(){t.tokenizer.seperator=t.tokenizer.defaultSeperator},t.tokenizer.getSeperator=function(){return t.tokenizer.seperator},t.Pipeline=function(){this._queue=[]},t.Pipeline.registeredFunctions={},t.Pipeline.registerFunction=function(e,n){n in t.Pipeline.registeredFunctions&&t.utils.warn("Overwriting existing registered function: "+n),e.label=n,t.Pipeline.registeredFunctions[n]=e},t.Pipeline.getRegisteredFunction=function(e){return e in t.Pipeline.registeredFunctions!=!0?null:t.Pipeline.registeredFunctions[e]},t.Pipeline.warnIfFunctionNotRegistered=function(e){var n=e.label&&e.label in this.registeredFunctions;n||t.utils.warn("Function is not registered with pipeline. This may cause problems when serialising the index.\n",e)},t.Pipeline.load=function(e){var n=new t.Pipeline;return e.forEach(function(e){var i=t.Pipeline.getRegisteredFunction(e);if(!i)throw new Error("Cannot load un-registered function: "+e);n.add(i)}),n},t.Pipeline.prototype.add=function(){var e=Array.prototype.slice.call(arguments);e.forEach(function(e){t.Pipeline.warnIfFunctionNotRegistered(e),this._queue.push(e)},this)},t.Pipeline.prototype.after=function(e,n){t.Pipeline.warnIfFunctionNotRegistered(n);var i=this._queue.indexOf(e);if(-1===i)throw new Error("Cannot find existingFn");this._queue.splice(i+1,0,n)},t.Pipeline.prototype.before=function(e,n){t.Pipeline.warnIfFunctionNotRegistered(n);var i=this._queue.indexOf(e);if(-1===i)throw new Error("Cannot find existingFn");this._queue.splice(i,0,n)},t.Pipeline.prototype.remove=function(e){var t=this._queue.indexOf(e);-1!==t&&this._queue.splice(t,1)},t.Pipeline.prototype.run=function(e){for(var t=[],n=e.length,i=this._queue.length,o=0;n>o;o++){for(var r=e[o],s=0;i>s&&(r=this._queue[s](r,o,e),void 0!==r&&null!==r);s++);void 0!==r&&null!==r&&t.push(r)}return t},t.Pipeline.prototype.reset=function(){this._queue=[]},t.Pipeline.prototype.get=function(){return this._queue},t.Pipeline.prototype.toJSON=function(){return this._queue.map(function(e){return t.Pipeline.warnIfFunctionNotRegistered(e),e.label})},t.Index=function(){this._fields=[],this._ref="id",this.pipeline=new t.Pipeline,this.documentStore=new t.DocumentStore,this.index={},this.eventEmitter=new t.EventEmitter,this._idfCache={},this.on("add","remove","update",function(){this._idfCache={}}.bind(this))},t.Index.prototype.on=function(){var e=Array.prototype.slice.call(arguments);return this.eventEmitter.addListener.apply(this.eventEmitter,e)},t.Index.prototype.off=function(e,t){return this.eventEmitter.removeListener(e,t)},t.Index.load=function(e){e.version!==t.version&&t.utils.warn("version mismatch: current "+t.version+" importing "+e.version);var n=new this;n._fields=e.fields,n._ref=e.ref,n.documentStore=t.DocumentStore.load(e.documentStore),n.pipeline=t.Pipeline.load(e.pipeline),n.index={};for(var i in e.index)n.index[i]=t.InvertedIndex.load(e.index[i]);return n},t.Index.prototype.addField=function(e){return this._fields.push(e),this.index[e]=new t.InvertedIndex,this},t.Index.prototype.setRef=function(e){return this._ref=e,this},t.Index.prototype.saveDocument=function(e){return this.documentStore=new t.DocumentStore(e),this},t.Index.prototype.addDoc=function(e,n){if(e){var n=void 0===n?!0:n,i=e[this._ref];this.documentStore.addDoc(i,e),this._fields.forEach(function(n){var o=this.pipeline.run(t.tokenizer(e[n]));this.documentStore.addFieldLength(i,n,o.length);var r={};o.forEach(function(e){e in r?r[e]+=1:r[e]=1},this);for(var s in r){var u=r[s];u=Math.sqrt(u),this.index[n].addToken(s,{ref:i,tf:u})}},this),n&&this.eventEmitter.emit("add",e,this)}},t.Index.prototype.removeDocByRef=function(e){if(e&&this.documentStore.isDocStored()!==!1&&this.documentStore.hasDoc(e)){var t=this.documentStore.getDoc(e);this.removeDoc(t,!1)}},t.Index.prototype.removeDoc=function(e,n){if(e){var n=void 0===n?!0:n,i=e[this._ref];this.documentStore.hasDoc(i)&&(this.documentStore.removeDoc(i),this._fields.forEach(function(n){var o=this.pipeline.run(t.tokenizer(e[n]));o.forEach(function(e){this.index[n].removeToken(e,i)},this)},this),n&&this.eventEmitter.emit("remove",e,this))}},t.Index.prototype.updateDoc=function(e,t){var t=void 0===t?!0:t;this.removeDocByRef(e[this._ref],!1),this.addDoc(e,!1),t&&this.eventEmitter.emit("update",e,this)},t.Index.prototype.idf=function(e,t){var n="@"+t+"/"+e;if(Object.prototype.hasOwnProperty.call(this._idfCache,n))return this._idfCache[n];var i=this.index[t].getDocFreq(e),o=1+Math.log(this.documentStore.length/(i+1));return this._idfCache[n]=o,o},t.Index.prototype.getFields=function(){return this._fields.slice()},t.Index.prototype.search=function(e,n){if(!e)return[];e="string"==typeof e?{any:e}:JSON.parse(JSON.stringify(e));var i=null;null!=n&&(i=JSON.stringify(n));for(var o=new t.Configuration(i,this.getFields()).get(),r={},s=Object.keys(e),u=0;uA class to handle command completion for the smbclient-ng shell.
\n\nThis class provides a command completion feature that suggests possible command names based on the current input.\nIt uses a dictionary to store commands and their descriptions, which helps in providing hints during the command line\ninteraction in the smbclient-ng shell.
\n\nAttributes:\n smbSession (SMBSession): An instance of SMBSession which maintains the current SMB session.\n commands (dict): A dictionary containing command names as keys and their descriptions and subcommands as values.
\n\nMethods:\n __init__(self, smbSession): Initializes the CommandCompleter with an SMBSession.
\n"}, {"fullname": "smbclientng.core.CommandCompleter.CommandCompleter.__init__", "modulename": "smbclientng.core.CommandCompleter", "qualname": "CommandCompleter.__init__", "kind": "function", "doc": "\n", "signature": "(smbSession, config)"}, {"fullname": "smbclientng.core.CommandCompleter.CommandCompleter.commands", "modulename": "smbclientng.core.CommandCompleter", "qualname": "CommandCompleter.commands", "kind": "variable", "doc": "\n", "default_value": "{'cd': {'description': ['Change the current working directory.', "Syntax: 'cd <directory>'"], 'subcommands': []}, 'close': {'description': ['Closes the SMB connection to the remote machine.', "Syntax: 'close'"], 'subcommands': []}, 'connect': {'description': ['Connect to the remote machine (useful if connection timed out).', "Syntax: 'connect'"], 'subcommands': []}, 'dir': {'description': ['List the contents of the current working directory.', "Syntax: 'dir'"], 'subcommands': []}, 'exit': {'description': ['Exits the smbclient-ng script.', "Syntax: 'exit'"], 'subcommands': []}, 'get': {'description': ['Get a remote file.', "Syntax: 'get [-r] <directory or file>'"], 'subcommands': []}, 'help': {'description': ['Displays this help message.', "Syntax: 'help'"], 'subcommands': ['format']}, 'info': {'description': ['Get information about the server and or the share.', "Syntax: 'info [server|share]'"], 'subcommands': ['server', 'share']}, 'lcd': {'description': ['Changes the current local directory.', "Syntax: 'lcd <directory>'"], 'subcommands': []}, 'lls': {'description': ['Lists the contents of the current local directory.', "Syntax: 'lls'"], 'subcommands': []}, 'lmkdir': {'description': ['Creates a new local directory.', "Syntax: 'lmkdir <directory>'"], 'subcommands': []}, 'lpwd': {'description': ['Shows the current local directory.', "Syntax: 'lpwd'"], 'subcommands': []}, 'lrm': {'description': ['Removes a local file.', "Syntax: 'lrm <file>'"], 'subcommands': []}, 'lrmdir': {'description': ['Removes a local directory.', "Syntax: 'lrmdir <directory>'"], 'subcommands': []}, 'ls': {'description': ['List the contents of the current remote working directory.', "Syntax: 'ls'"], 'subcommands': []}, 'ltree': {'description': ['Displays a tree view of the local directories.', "Syntax: 'ltree [directory]'"], 'subcommands': []}, 'mkdir': {'description': ['Creates a new remote directory.', "Syntax: 'mkdir <directory>'"], 'subcommands': []}, 'module': {'description': ['Loads a specific module for additional functionalities.', "Syntax: 'module <name>'"], 'subcommands': []}, 'put': {'description': ['Put a local file or directory in a remote directory.', "Syntax: 'put [-r] <directory or file>'"], 'subcommands': []}, 'reconnect': {'description': ['Reconnect to the remote machine (useful if connection timed out).', "Syntax: 'reconnect'"], 'subcommands': []}, 'reset': {'description': ['Reset the TTY output, useful if it was broken after printing a binary file on stdout.', "Syntax: 'reset'"], 'subcommands': []}, 'rmdir': {'description': ['Removes a remote directory.', "Syntax: 'rmdir <directory>'"], 'subcommands': []}, 'rm': {'description': ['Removes a remote file.', "Syntax: 'rm <file>'"], 'subcommands': []}, 'shares': {'description': ['Lists the SMB shares served by the remote machine.', "Syntax: 'shares'"], 'subcommands': []}, 'use': {'description': ['Use a SMB share.', "Syntax: 'use <sharename>'"], 'subcommands': []}, 'tree': {'description': ['Displays a tree view of the remote directories.', "Syntax: 'tree [directory]'"], 'subcommands': []}}"}, {"fullname": "smbclientng.core.CommandCompleter.CommandCompleter.smbSession", "modulename": "smbclientng.core.CommandCompleter", "qualname": "CommandCompleter.smbSession", "kind": "variable", "doc": "\n"}, {"fullname": "smbclientng.core.CommandCompleter.CommandCompleter.config", "modulename": "smbclientng.core.CommandCompleter", "qualname": "CommandCompleter.config", "kind": "variable", "doc": "\n"}, {"fullname": "smbclientng.core.CommandCompleter.CommandCompleter.complete", "modulename": "smbclientng.core.CommandCompleter", "qualname": "CommandCompleter.complete", "kind": "function", "doc": "Function to handle command completion in the LDAP console.
\n\nThis function completes the user\"s input based on the available options for commands in the LDAP console.
\n\nArgs:\n text (str): The current text input by the user.\n state (int): The current state of completion.
\n\nReturns:\n str: The next completion suggestion based on the user\"s input state.
\n", "signature": "(self, text, state):", "funcdef": "def"}, {"fullname": "smbclientng.core.CommandCompleter.CommandCompleter.print_help", "modulename": "smbclientng.core.CommandCompleter", "qualname": "CommandCompleter.print_help", "kind": "function", "doc": "Prints help information for a specific command or all commands if no command is specified.
\n\nThis method displays the help information for the command passed as an argument. If no command is specified,\nit prints the help information for all available commands. The help information includes the command syntax,\ndescription, and any subcommands associated with it. This method is designed to provide users with the necessary\nguidance on how to use the commands in the smbclient-ng shell.
\n\nArgs:\n command (str, optional): The command to display help information for. If None, help for all commands is displayed.
\n\nReturns:\n None
\n", "signature": "(self, command=None):", "funcdef": "def"}, {"fullname": "smbclientng.core.CommandCompleter.CommandCompleter.print_help_format", "modulename": "smbclientng.core.CommandCompleter", "qualname": "CommandCompleter.print_help_format", "kind": "function", "doc": "Prints the help information for the 'format' used in remote 'ls' and 'dir' commands.
\n\nThis function displays the format of file attributes used in the smbclient-ng shell. It explains the meaning\nof each character in the file attribute string, such as whether a file is read-only, hidden, or a directory.
\n", "signature": "(self):", "funcdef": "def"}, {"fullname": "smbclientng.core.Config", "modulename": "smbclientng.core.Config", "kind": "module", "doc": "\n"}, {"fullname": "smbclientng.core.Config.Config", "modulename": "smbclientng.core.Config", "qualname": "Config", "kind": "class", "doc": "Configuration handler for smbclientng.
\n\nThis class manages the configuration settings for the smbclientng tool, including debug and color output settings.\nIt provides a structured way to access and modify these settings throughout the application.
\n\nAttributes:\n _debug (bool): Flag to enable or disable debug mode.\n _no_colors (bool): Flag to enable or disable colored output, depending on the platform.
\n\nMethods:\n debug: Property to get or set the debug mode.\n no_colors: Property to get or set the colored output preference.
\n"}, {"fullname": "smbclientng.core.Config.Config.__init__", "modulename": "smbclientng.core.Config", "qualname": "Config.__init__", "kind": "function", "doc": "\n", "signature": "(debug=False, no_colors=None)"}, {"fullname": "smbclientng.core.Config.Config.debug", "modulename": "smbclientng.core.Config", "qualname": "Config.debug", "kind": "variable", "doc": "\n"}, {"fullname": "smbclientng.core.Config.Config.no_colors", "modulename": "smbclientng.core.Config", "qualname": "Config.no_colors", "kind": "variable", "doc": "\n"}, {"fullname": "smbclientng.core.InteractiveShell", "modulename": "smbclientng.core.InteractiveShell", "kind": "module", "doc": "\n"}, {"fullname": "smbclientng.core.InteractiveShell.command_arguments_required", "modulename": "smbclientng.core.InteractiveShell", "qualname": "command_arguments_required", "kind": "function", "doc": "\n", "signature": "(func):", "funcdef": "def"}, {"fullname": "smbclientng.core.InteractiveShell.active_smb_connection_needed", "modulename": "smbclientng.core.InteractiveShell", "qualname": "active_smb_connection_needed", "kind": "function", "doc": "\n", "signature": "(func):", "funcdef": "def"}, {"fullname": "smbclientng.core.InteractiveShell.smb_share_is_set", "modulename": "smbclientng.core.InteractiveShell", "qualname": "smb_share_is_set", "kind": "function", "doc": "\n", "signature": "(func):", "funcdef": "def"}, {"fullname": "smbclientng.core.InteractiveShell.InteractiveShell", "modulename": "smbclientng.core.InteractiveShell", "qualname": "InteractiveShell", "kind": "class", "doc": "Class InteractiveShell is designed to manage the interactive command line interface for smbclient-ng.
\n\nThis class handles user input, executes commands, and manages the state of the SMB session. It provides\na command line interface for users to interact with SMB shares, execute commands like directory listing,\nfile transfer, and more.
\n\nAttributes:\n smbSession (SMBConnection): The active SMB connection session.\n debug (bool): Flag to enable or disable debug mode.\n smb_share (str): The current SMB share in use.\n smb_path (str): The current path within the SMB share.\n commandCompleterObject (CommandCompleter): Object to handle command completion and help generation.
\n\nMethods:\n __init__(self, smbSession, debug=False): Initializes the InteractiveShell with the given SMB session and debug mode.\n run(self): Starts the command line interface loop, processing user input until exit.
\n"}, {"fullname": "smbclientng.core.InteractiveShell.InteractiveShell.__init__", "modulename": "smbclientng.core.InteractiveShell", "qualname": "InteractiveShell.__init__", "kind": "function", "doc": "\n", "signature": "(smbSession, config)"}, {"fullname": "smbclientng.core.InteractiveShell.InteractiveShell.smbSession", "modulename": "smbclientng.core.InteractiveShell", "qualname": "InteractiveShell.smbSession", "kind": "variable", "doc": "\n"}, {"fullname": "smbclientng.core.InteractiveShell.InteractiveShell.config", "modulename": "smbclientng.core.InteractiveShell", "qualname": "InteractiveShell.config", "kind": "variable", "doc": "\n"}, {"fullname": "smbclientng.core.InteractiveShell.InteractiveShell.commandCompleterObject", "modulename": "smbclientng.core.InteractiveShell", "qualname": "InteractiveShell.commandCompleterObject", "kind": "variable", "doc": "\n"}, {"fullname": "smbclientng.core.InteractiveShell.InteractiveShell.modules", "modulename": "smbclientng.core.InteractiveShell", "qualname": "InteractiveShell.modules", "kind": "variable", "doc": "\n"}, {"fullname": "smbclientng.core.InteractiveShell.InteractiveShell.run", "modulename": "smbclientng.core.InteractiveShell", "qualname": "InteractiveShell.run", "kind": "function", "doc": "\n", "signature": "(self):", "funcdef": "def"}, {"fullname": "smbclientng.core.InteractiveShell.InteractiveShell.process_command", "modulename": "smbclientng.core.InteractiveShell", "qualname": "InteractiveShell.process_command", "kind": "function", "doc": "\n", "signature": "(self, command, arguments=[]):", "funcdef": "def"}, {"fullname": "smbclientng.core.InteractiveShell.InteractiveShell.command_cd", "modulename": "smbclientng.core.InteractiveShell", "qualname": "InteractiveShell.command_cd", "kind": "function", "doc": "\n", "signature": "(*args, **kwargs):", "funcdef": "def"}, {"fullname": "smbclientng.core.InteractiveShell.InteractiveShell.command_close", "modulename": "smbclientng.core.InteractiveShell", "qualname": "InteractiveShell.command_close", "kind": "function", "doc": "\n", "signature": "(self, arguments, command):", "funcdef": "def"}, {"fullname": "smbclientng.core.InteractiveShell.InteractiveShell.command_get", "modulename": "smbclientng.core.InteractiveShell", "qualname": "InteractiveShell.command_get", "kind": "function", "doc": "\n", "signature": "(*args, **kwargs):", "funcdef": "def"}, {"fullname": "smbclientng.core.InteractiveShell.InteractiveShell.command_help", "modulename": "smbclientng.core.InteractiveShell", "qualname": "InteractiveShell.command_help", "kind": "function", "doc": "\n", "signature": "(self, arguments, command):", "funcdef": "def"}, {"fullname": "smbclientng.core.InteractiveShell.InteractiveShell.command_info", "modulename": "smbclientng.core.InteractiveShell", "qualname": "InteractiveShell.command_info", "kind": "function", "doc": "\n", "signature": "(*args, **kwargs):", "funcdef": "def"}, {"fullname": "smbclientng.core.InteractiveShell.InteractiveShell.command_lcd", "modulename": "smbclientng.core.InteractiveShell", "qualname": "InteractiveShell.command_lcd", "kind": "function", "doc": "\n", "signature": "(*args, **kwargs):", "funcdef": "def"}, {"fullname": "smbclientng.core.InteractiveShell.InteractiveShell.command_lls", "modulename": "smbclientng.core.InteractiveShell", "qualname": "InteractiveShell.command_lls", "kind": "function", "doc": "\n", "signature": "(self, arguments, command):", "funcdef": "def"}, {"fullname": "smbclientng.core.InteractiveShell.InteractiveShell.command_lmkdir", "modulename": "smbclientng.core.InteractiveShell", "qualname": "InteractiveShell.command_lmkdir", "kind": "function", "doc": "\n", "signature": "(*args, **kwargs):", "funcdef": "def"}, {"fullname": "smbclientng.core.InteractiveShell.InteractiveShell.command_lpwd", "modulename": "smbclientng.core.InteractiveShell", "qualname": "InteractiveShell.command_lpwd", "kind": "function", "doc": "\n", "signature": "(self, arguments, command):", "funcdef": "def"}, {"fullname": "smbclientng.core.InteractiveShell.InteractiveShell.command_lrm", "modulename": "smbclientng.core.InteractiveShell", "qualname": "InteractiveShell.command_lrm", "kind": "function", "doc": "\n", "signature": "(*args, **kwargs):", "funcdef": "def"}, {"fullname": "smbclientng.core.InteractiveShell.InteractiveShell.command_lrmdir", "modulename": "smbclientng.core.InteractiveShell", "qualname": "InteractiveShell.command_lrmdir", "kind": "function", "doc": "\n", "signature": "(*args, **kwargs):", "funcdef": "def"}, {"fullname": "smbclientng.core.InteractiveShell.InteractiveShell.command_ltree", "modulename": "smbclientng.core.InteractiveShell", "qualname": "InteractiveShell.command_ltree", "kind": "function", "doc": "\n", "signature": "(self, arguments, command):", "funcdef": "def"}, {"fullname": "smbclientng.core.InteractiveShell.InteractiveShell.command_ls", "modulename": "smbclientng.core.InteractiveShell", "qualname": "InteractiveShell.command_ls", "kind": "function", "doc": "\n", "signature": "(*args, **kwargs):", "funcdef": "def"}, {"fullname": "smbclientng.core.InteractiveShell.InteractiveShell.command_mkdir", "modulename": "smbclientng.core.InteractiveShell", "qualname": "InteractiveShell.command_mkdir", "kind": "function", "doc": "\n", "signature": "(*args, **kwargs):", "funcdef": "def"}, {"fullname": "smbclientng.core.InteractiveShell.InteractiveShell.command_module", "modulename": "smbclientng.core.InteractiveShell", "qualname": "InteractiveShell.command_module", "kind": "function", "doc": "\n", "signature": "(*args, **kwargs):", "funcdef": "def"}, {"fullname": "smbclientng.core.InteractiveShell.InteractiveShell.command_put", "modulename": "smbclientng.core.InteractiveShell", "qualname": "InteractiveShell.command_put", "kind": "function", "doc": "\n", "signature": "(*args, **kwargs):", "funcdef": "def"}, {"fullname": "smbclientng.core.InteractiveShell.InteractiveShell.command_reconnect", "modulename": "smbclientng.core.InteractiveShell", "qualname": "InteractiveShell.command_reconnect", "kind": "function", "doc": "\n", "signature": "(self, arguments, command):", "funcdef": "def"}, {"fullname": "smbclientng.core.InteractiveShell.InteractiveShell.command_reset", "modulename": "smbclientng.core.InteractiveShell", "qualname": "InteractiveShell.command_reset", "kind": "function", "doc": "\n", "signature": "(self, arguments, command):", "funcdef": "def"}, {"fullname": "smbclientng.core.InteractiveShell.InteractiveShell.command_rm", "modulename": "smbclientng.core.InteractiveShell", "qualname": "InteractiveShell.command_rm", "kind": "function", "doc": "\n", "signature": "(*args, **kwargs):", "funcdef": "def"}, {"fullname": "smbclientng.core.InteractiveShell.InteractiveShell.command_rmdir", "modulename": "smbclientng.core.InteractiveShell", "qualname": "InteractiveShell.command_rmdir", "kind": "function", "doc": "\n", "signature": "(*args, **kwargs):", "funcdef": "def"}, {"fullname": "smbclientng.core.InteractiveShell.InteractiveShell.command_shares", "modulename": "smbclientng.core.InteractiveShell", "qualname": "InteractiveShell.command_shares", "kind": "function", "doc": "\n", "signature": "(*args, **kwargs):", "funcdef": "def"}, {"fullname": "smbclientng.core.InteractiveShell.InteractiveShell.command_tree", "modulename": "smbclientng.core.InteractiveShell", "qualname": "InteractiveShell.command_tree", "kind": "function", "doc": "\n", "signature": "(*args, **kwargs):", "funcdef": "def"}, {"fullname": "smbclientng.core.InteractiveShell.InteractiveShell.command_use", "modulename": "smbclientng.core.InteractiveShell", "qualname": "InteractiveShell.command_use", "kind": "function", "doc": "\n", "signature": "(*args, **kwargs):", "funcdef": "def"}, {"fullname": "smbclientng.core.LocalFileIO", "modulename": "smbclientng.core.LocalFileIO", "kind": "module", "doc": "\n"}, {"fullname": "smbclientng.core.LocalFileIO.LocalFileIO", "modulename": "smbclientng.core.LocalFileIO", "qualname": "LocalFileIO", "kind": "class", "doc": "Class LocalFileIO is designed to handle local file input/output operations within the smbclient-ng tool.\nIt provides functionalities to open, read, write, and manage progress of file operations based on the expected size of the file.
\n\nAttributes:\n mode (str): The mode in which the file should be opened (e.g., 'rb', 'wb').\n path (str): The path to the file that needs to be handled.\n expected_size (int, optional): The expected size of the file in bytes. This is used to display progress.\n debug (bool): Flag to enable debug mode which provides additional output during operations.
\n\nMethods:\n __init__(self, mode, path=None, expected_size=None, debug=False): Initializes the LocalFileIO instance.\n write(self, data): Writes data to the file and updates the progress bar if expected size is provided.\n read(self, size): Reads data from the file up to the specified size and updates the progress bar if expected size is provided.
\n"}, {"fullname": "smbclientng.core.LocalFileIO.LocalFileIO.__init__", "modulename": "smbclientng.core.LocalFileIO", "qualname": "LocalFileIO.__init__", "kind": "function", "doc": "\n", "signature": "(\tmode,\tpath=None,\texpected_size=None,\tkeepRemotePath=False,\tdebug=False)"}, {"fullname": "smbclientng.core.LocalFileIO.LocalFileIO.mode", "modulename": "smbclientng.core.LocalFileIO", "qualname": "LocalFileIO.mode", "kind": "variable", "doc": "\n"}, {"fullname": "smbclientng.core.LocalFileIO.LocalFileIO.path", "modulename": "smbclientng.core.LocalFileIO", "qualname": "LocalFileIO.path", "kind": "variable", "doc": "\n"}, {"fullname": "smbclientng.core.LocalFileIO.LocalFileIO.dir", "modulename": "smbclientng.core.LocalFileIO", "qualname": "LocalFileIO.dir", "kind": "variable", "doc": "\n"}, {"fullname": "smbclientng.core.LocalFileIO.LocalFileIO.debug", "modulename": "smbclientng.core.LocalFileIO", "qualname": "LocalFileIO.debug", "kind": "variable", "doc": "\n"}, {"fullname": "smbclientng.core.LocalFileIO.LocalFileIO.expected_size", "modulename": "smbclientng.core.LocalFileIO", "qualname": "LocalFileIO.expected_size", "kind": "variable", "doc": "\n"}, {"fullname": "smbclientng.core.LocalFileIO.LocalFileIO.keepRemotePath", "modulename": "smbclientng.core.LocalFileIO", "qualname": "LocalFileIO.keepRemotePath", "kind": "variable", "doc": "\n"}, {"fullname": "smbclientng.core.LocalFileIO.LocalFileIO.write", "modulename": "smbclientng.core.LocalFileIO", "qualname": "LocalFileIO.write", "kind": "function", "doc": "Writes data to the file.
\n\nThis method writes the specified data to the file and updates the progress bar with the amount of data written if the expected size is set.
\n\nArgs:\n data (bytes): The data to be written to the file.
\n\nReturns:\n int: The number of bytes written.
\n", "signature": "(self, data):", "funcdef": "def"}, {"fullname": "smbclientng.core.LocalFileIO.LocalFileIO.read", "modulename": "smbclientng.core.LocalFileIO", "qualname": "LocalFileIO.read", "kind": "function", "doc": "Reads a specified amount of data from the file.
\n\nThis method reads data from the file based on the size specified. It also updates the progress bar with the amount of data read if the expected size is set.
\n\nArgs:\n size (int): The number of bytes to read from the file.
\n\nReturns:\n bytes: The data read from the file.
\n", "signature": "(self, size):", "funcdef": "def"}, {"fullname": "smbclientng.core.LocalFileIO.LocalFileIO.close", "modulename": "smbclientng.core.LocalFileIO", "qualname": "LocalFileIO.close", "kind": "function", "doc": "Closes the file descriptor and optionally removes the file.
\n\nThis method ensures that the file descriptor is properly closed and the file is removed if specified.\nIt also stops the progress bar if it was initiated and cleans up the object by deleting it.
\n\nArgs:\n remove (bool): If True, the file at the path will be removed after closing the file descriptor.
\n", "signature": "(self, remove=False):", "funcdef": "def"}, {"fullname": "smbclientng.core.LocalFileIO.LocalFileIO.set_error", "modulename": "smbclientng.core.LocalFileIO", "qualname": "LocalFileIO.set_error", "kind": "function", "doc": "Sets an error message in the progress bar's description and modifies the progress bar to show only essential columns.
\n\nThis method is used to communicate error states or important messages directly in the progress bar interface.\nIt updates the task description with the provided message and simplifies the progress bar to show only the text\nand download columns, removing other elements like speed and time remaining which may not be relevant in an error state.
\n\nArgs:\n message (str): The error or status message to display in the progress bar.
\n", "signature": "(self, message):", "funcdef": "def"}, {"fullname": "smbclientng.core.Module", "modulename": "smbclientng.core.Module", "kind": "module", "doc": "\n"}, {"fullname": "smbclientng.core.Module.Module", "modulename": "smbclientng.core.Module", "qualname": "Module", "kind": "class", "doc": "A parent class for all modules in the smbclient-ng tool.
\n\nThis class provides common attributes and methods that are shared among different modules.
\n"}, {"fullname": "smbclientng.core.Module.Module.__init__", "modulename": "smbclientng.core.Module", "qualname": "Module.__init__", "kind": "function", "doc": "\n", "signature": "(smbSession, config)"}, {"fullname": "smbclientng.core.Module.Module.name", "modulename": "smbclientng.core.Module", "qualname": "Module.name", "kind": "variable", "doc": "\n", "default_value": "''"}, {"fullname": "smbclientng.core.Module.Module.description", "modulename": "smbclientng.core.Module", "qualname": "Module.description", "kind": "variable", "doc": "\n", "default_value": "''"}, {"fullname": "smbclientng.core.Module.Module.smbSession", "modulename": "smbclientng.core.Module", "qualname": "Module.smbSession", "kind": "variable", "doc": "\n", "default_value": "None"}, {"fullname": "smbclientng.core.Module.Module.options", "modulename": "smbclientng.core.Module", "qualname": "Module.options", "kind": "variable", "doc": "\n", "default_value": "None"}, {"fullname": "smbclientng.core.Module.Module.config", "modulename": "smbclientng.core.Module", "qualname": "Module.config", "kind": "variable", "doc": "\n"}, {"fullname": "smbclientng.core.Module.Module.parseArgs", "modulename": "smbclientng.core.Module", "qualname": "Module.parseArgs", "kind": "function", "doc": "\n", "signature": "(self):", "funcdef": "def"}, {"fullname": "smbclientng.core.Module.Module.run", "modulename": "smbclientng.core.Module", "qualname": "Module.run", "kind": "function", "doc": "Placeholder method for running the module.
\n\nThis method should be implemented by subclasses to define the specific behavior of the module.
\n", "signature": "(self):", "funcdef": "def"}, {"fullname": "smbclientng.core.Module.Module.processArguments", "modulename": "smbclientng.core.Module", "qualname": "Module.processArguments", "kind": "function", "doc": "\n", "signature": "(self, parser, arguments):", "funcdef": "def"}, {"fullname": "smbclientng.core.ModuleArgumentParser", "modulename": "smbclientng.core.ModuleArgumentParser", "kind": "module", "doc": "\n"}, {"fullname": "smbclientng.core.ModuleArgumentParser.ModuleArgumentParser", "modulename": "smbclientng.core.ModuleArgumentParser", "qualname": "ModuleArgumentParser", "kind": "class", "doc": "A custom argument parser for handling module-specific command-line arguments in the smbclientng application.
\n\nThis class extends the argparse.ArgumentParser and provides custom error handling specific to the needs of smbclientng modules.\nIt is designed to provide clear and user-friendly command-line interfaces for various modules within the smbclientng suite.
\n\nAttributes:\n None
\n\nMethods:\n error(message: str):\n Overrides the default error handling to provide a more informative error message and display the help text.
\n", "bases": "argparse.ArgumentParser"}, {"fullname": "smbclientng.core.ModuleArgumentParser.ModuleArgumentParser.error", "modulename": "smbclientng.core.ModuleArgumentParser", "qualname": "ModuleArgumentParser.error", "kind": "function", "doc": "Overrides the default error handling of argparse.ArgumentParser to provide a custom error message and help display.
\n\nThis method is called when ArgumentParser encounters an error. It writes the error message to stderr,\ndisplays the help message, and then exits the program with a status code of 2.
\n\nArgs:\n message (str): The error message to be displayed.
\n", "signature": "(self, message):", "funcdef": "def"}, {"fullname": "smbclientng.core.SMBSession", "modulename": "smbclientng.core.SMBSession", "kind": "module", "doc": "\n"}, {"fullname": "smbclientng.core.SMBSession.SMBSession", "modulename": "smbclientng.core.SMBSession", "qualname": "SMBSession", "kind": "class", "doc": "Class SMBSession is designed to handle the session management for SMB (Server Message Block) protocol connections.\nIt provides functionalities to connect to an SMB server, authenticate using either NTLM or Kerberos, and manage SMB shares.
\n\nAttributes:\n address (str): The IP address or hostname of the SMB server.\n domain (str): The domain name for SMB server authentication.\n username (str): The username for SMB server authentication.\n password (str): The password for SMB server authentication.\n lmhash (str): The LM hash of the user's password, if available.\n nthash (str): The NT hash of the user's password, if available.\n use_kerberos (bool): A flag to determine whether to use Kerberos for authentication.\n kdcHost (str): The Key Distribution Center (KDC) host for Kerberos authentication.\n debug (bool): A flag to enable debug output.\n smbClient (object): The SMB client object used for the connection.\n connected (bool): A flag to check the status of the connection.\n smb_share (str): The current SMB share in use.\n smb_path (str): The current path within the SMB share.
\n\nMethods:\n __init__(address, domain, username, password, lmhash, nthash, use_kerberos=False, kdcHost=None, debug=False):\n Initializes the SMBSession with the specified parameters.\n init_smb_session():\n Initializes the SMB session by connecting to the server and authenticating using the specified method.
\n"}, {"fullname": "smbclientng.core.SMBSession.SMBSession.__init__", "modulename": "smbclientng.core.SMBSession", "qualname": "SMBSession.__init__", "kind": "function", "doc": "\n", "signature": "(\taddress,\tdomain,\tusername,\tpassword,\tlmhash,\tnthash,\tuse_kerberos=False,\tkdcHost=None,\tconfig=None)"}, {"fullname": "smbclientng.core.SMBSession.SMBSession.config", "modulename": "smbclientng.core.SMBSession", "qualname": "SMBSession.config", "kind": "variable", "doc": "\n"}, {"fullname": "smbclientng.core.SMBSession.SMBSession.address", "modulename": "smbclientng.core.SMBSession", "qualname": "SMBSession.address", "kind": "variable", "doc": "\n"}, {"fullname": "smbclientng.core.SMBSession.SMBSession.domain", "modulename": "smbclientng.core.SMBSession", "qualname": "SMBSession.domain", "kind": "variable", "doc": "\n"}, {"fullname": "smbclientng.core.SMBSession.SMBSession.username", "modulename": "smbclientng.core.SMBSession", "qualname": "SMBSession.username", "kind": "variable", "doc": "\n"}, {"fullname": "smbclientng.core.SMBSession.SMBSession.password", "modulename": "smbclientng.core.SMBSession", "qualname": "SMBSession.password", "kind": "variable", "doc": "\n"}, {"fullname": "smbclientng.core.SMBSession.SMBSession.lmhash", "modulename": "smbclientng.core.SMBSession", "qualname": "SMBSession.lmhash", "kind": "variable", "doc": "\n"}, {"fullname": "smbclientng.core.SMBSession.SMBSession.nthash", "modulename": "smbclientng.core.SMBSession", "qualname": "SMBSession.nthash", "kind": "variable", "doc": "\n"}, {"fullname": "smbclientng.core.SMBSession.SMBSession.use_kerberos", "modulename": "smbclientng.core.SMBSession", "qualname": "SMBSession.use_kerberos", "kind": "variable", "doc": "\n"}, {"fullname": "smbclientng.core.SMBSession.SMBSession.kdcHost", "modulename": "smbclientng.core.SMBSession", "qualname": "SMBSession.kdcHost", "kind": "variable", "doc": "\n"}, {"fullname": "smbclientng.core.SMBSession.SMBSession.smbClient", "modulename": "smbclientng.core.SMBSession", "qualname": "SMBSession.smbClient", "kind": "variable", "doc": "\n"}, {"fullname": "smbclientng.core.SMBSession.SMBSession.connected", "modulename": "smbclientng.core.SMBSession", "qualname": "SMBSession.connected", "kind": "variable", "doc": "\n"}, {"fullname": "smbclientng.core.SMBSession.SMBSession.available_shares", "modulename": "smbclientng.core.SMBSession", "qualname": "SMBSession.available_shares", "kind": "variable", "doc": "\n"}, {"fullname": "smbclientng.core.SMBSession.SMBSession.smb_share", "modulename": "smbclientng.core.SMBSession", "qualname": "SMBSession.smb_share", "kind": "variable", "doc": "\n"}, {"fullname": "smbclientng.core.SMBSession.SMBSession.smb_cwd", "modulename": "smbclientng.core.SMBSession", "qualname": "SMBSession.smb_cwd", "kind": "variable", "doc": "\n"}, {"fullname": "smbclientng.core.SMBSession.SMBSession.init_smb_session", "modulename": "smbclientng.core.SMBSession", "qualname": "SMBSession.init_smb_session", "kind": "function", "doc": "Initializes and establishes a session with the SMB server.
\n\nThis method sets up the SMB connection using either Kerberos or NTLM authentication based on the configuration.\nIt attempts to connect to the SMB server specified by the address
attribute and authenticate using the credentials provided during the object's initialization.
The method will print debug information if the debug
attribute is set to True. Upon successful connection and authentication, it sets the connected
attribute to True.
Returns:\n bool: True if the connection and authentication are successful, False otherwise.
\n", "signature": "(self):", "funcdef": "def"}, {"fullname": "smbclientng.core.SMBSession.SMBSession.close_smb_session", "modulename": "smbclientng.core.SMBSession", "qualname": "SMBSession.close_smb_session", "kind": "function", "doc": "Closes the current SMB session by disconnecting the SMB client.
\n\nThis method ensures that the SMB client connection is properly closed. It checks if the client is connected\nand if so, it closes the connection and resets the connection status.
\n\nRaises:\n Exception: If the SMB client is not initialized or if there's an error during the disconnection process.
\n", "signature": "(self):", "funcdef": "def"}, {"fullname": "smbclientng.core.SMBSession.SMBSession.get_file", "modulename": "smbclientng.core.SMBSession", "qualname": "SMBSession.get_file", "kind": "function", "doc": "Retrieves a file from the specified path on the SMB share.
\n\nThis method attempts to retrieve a file from the given path within the currently connected SMB share.\nIf the path points to a directory, it skips the retrieval. It handles file retrieval by creating a local\nfile object and writing the contents of the remote file to it using the SMB client's getFile method.
\n\nParameters:\n path (str): The path of the file to retrieve. If None, uses the current smb_path.
\n\nReturns:\n None
\n", "signature": "(self, path=None, keepRemotePath=False):", "funcdef": "def"}, {"fullname": "smbclientng.core.SMBSession.SMBSession.get_file_recursively", "modulename": "smbclientng.core.SMBSession", "qualname": "SMBSession.get_file_recursively", "kind": "function", "doc": "Recursively retrieves files from a specified path on the SMB share.
\n\nThis method navigates through all directories starting from the given path,\nand downloads all files found. It handles directories recursively, ensuring\nthat all nested files are retrieved. The method skips over directory entries\nand handles errors gracefully, attempting to continue the operation where possible.
\n\nParameters:\n path (str): The initial directory path from which to start the recursive file retrieval.\n If None, it starts from the root of the configured SMB share.
\n", "signature": "(self, path=None):", "funcdef": "def"}, {"fullname": "smbclientng.core.SMBSession.SMBSession.info", "modulename": "smbclientng.core.SMBSession", "qualname": "SMBSession.info", "kind": "function", "doc": "Displays information about the server and optionally the shares.
\n\nThis method prints detailed information about the server's characteristics such as NetBIOS names, DNS details, OS information, and SMB capabilities. If the share
parameter is set to True and a share is currently set, it will also attempt to display information about the share.
Parameters:\n share (bool): If True, display information about the current share.\n server (bool): If True, display information about the server.
\n\nReturns:\n None
\n", "signature": "(self, share=True, server=True):", "funcdef": "def"}, {"fullname": "smbclientng.core.SMBSession.SMBSession.list_contents", "modulename": "smbclientng.core.SMBSession", "qualname": "SMBSession.list_contents", "kind": "function", "doc": "Lists the contents of a specified directory on the SMB share.
\n\nThis method retrieves the contents of a directory specified by shareName
and path
. If shareName
or path
\nis not provided, it defaults to the instance's current SMB share or path. The method returns a dictionary with\nthe long names of the files and directories as keys and their respective SMB entry objects as values.
Args:\n shareName (str, optional): The name of the SMB share. Defaults to the current SMB share if None.\n path (str, optional): The directory path to list contents from. Defaults to the current path if None.
\n\nReturns:\n dict: A dictionary with file and directory names as keys and their SMB entry objects as values.
\n", "signature": "(self, path=None):", "funcdef": "def"}, {"fullname": "smbclientng.core.SMBSession.SMBSession.list_shares", "modulename": "smbclientng.core.SMBSession", "qualname": "SMBSession.list_shares", "kind": "function", "doc": "Lists all the shares available on the connected SMB server.
\n\nThis method queries the SMB server to retrieve a list of all available shares. It populates the shares
dictionary\nwith key-value pairs where the key is the share name and the value is a dictionary containing details about the share\nsuch as its name, type, raw type, and any comments associated with the share.
Returns:\n dict: A dictionary containing information about each share available on the server.
\n", "signature": "(self):", "funcdef": "def"}, {"fullname": "smbclientng.core.SMBSession.SMBSession.mkdir", "modulename": "smbclientng.core.SMBSession", "qualname": "SMBSession.mkdir", "kind": "function", "doc": "Creates a directory at the specified path on the SMB share.
\n\nThis method takes a path and attempts to create the directory structure on the SMB share. If the path includes\nnested directories, it will create each directory in the sequence. If a directory already exists, it will skip\nthe creation for that directory without raising an error.
\n\nArgs:\n path (str, optional): The full path of the directory to create on the SMB share. Defaults to None.
\n\nNote:\n The path should use forward slashes ('/') which will be converted to backslashes (ntpath.sep) for SMB compatibility.
\n", "signature": "(self, path=None):", "funcdef": "def"}, {"fullname": "smbclientng.core.SMBSession.SMBSession.path_exists", "modulename": "smbclientng.core.SMBSession", "qualname": "SMBSession.path_exists", "kind": "function", "doc": "Checks if the specified path exists on the SMB share.
\n\nThis method determines if a given path exists on the SMB share by attempting to list the contents of the path.\nIf the path listing is successful and returns one or more entries, the path is considered to exist.
\n\nArgs:\n path (str, optional): The path to check on the SMB share. Defaults to None.
\n\nReturns:\n bool: True if the path exists, False otherwise or if an error occurs.
\n", "signature": "(self, path=None):", "funcdef": "def"}, {"fullname": "smbclientng.core.SMBSession.SMBSession.path_isdir", "modulename": "smbclientng.core.SMBSession", "qualname": "SMBSession.path_isdir", "kind": "function", "doc": "Checks if the specified path is a directory on the SMB share.
\n\nThis method determines if a given path corresponds to a directory on the SMB share. It does this by listing the\ncontents of the path and filtering for entries that match the basename of the path and are marked as directories.
\n\nArgs:\n path (str, optional): The path to check on the SMB share. Defaults to None.
\n\nReturns:\n bool: True if the path is a directory, False otherwise or if an error occurs.
\n", "signature": "(self, pathFromRoot=None):", "funcdef": "def"}, {"fullname": "smbclientng.core.SMBSession.SMBSession.path_isfile", "modulename": "smbclientng.core.SMBSession", "qualname": "SMBSession.path_isfile", "kind": "function", "doc": "Checks if the specified path is a file on the SMB share.
\n\nThis method determines if a given path corresponds to a file on the SMB share. It does this by listing the\ncontents of the path and filtering for entries that match the basename of the path and are not marked as directories.
\n\nArgs:\n path (str, optional): The path to check on the SMB share. Defaults to None.
\n\nReturns:\n bool: True if the path is a file, False otherwise or if an error occurs.
\n", "signature": "(self, path=None):", "funcdef": "def"}, {"fullname": "smbclientng.core.SMBSession.SMBSession.ping_smb_session", "modulename": "smbclientng.core.SMBSession", "qualname": "SMBSession.ping_smb_session", "kind": "function", "doc": "Tests the connectivity to the SMB server by sending an echo command.
\n\nThis method attempts to send an echo command to the SMB server to check if the session is still active.\nIt updates the connected
attribute of the class based on the success or failure of the echo command.
Returns:\n bool: True if the echo command succeeds (indicating the session is active), False otherwise.
\n", "signature": "(self):", "funcdef": "def"}, {"fullname": "smbclientng.core.SMBSession.SMBSession.put_file", "modulename": "smbclientng.core.SMBSession", "qualname": "SMBSession.put_file", "kind": "function", "doc": "Uploads a single file to the SMB share.
\n\nThis method takes a local file path, opens the file, and uploads it to the SMB share at the specified path.\nIt handles exceptions such as broken pipe errors or keyboard interrupts by closing and reinitializing the SMB session.\nGeneral exceptions are caught and logged, with a traceback provided if debugging is enabled.
\n\nArgs:\n localpath (str, optional): The local file path of the file to be uploaded. Defaults to None.
\n", "signature": "(self, localpath=None):", "funcdef": "def"}, {"fullname": "smbclientng.core.SMBSession.SMBSession.put_file_recursively", "modulename": "smbclientng.core.SMBSession", "qualname": "SMBSession.put_file_recursively", "kind": "function", "doc": "Recursively uploads files from a specified local directory to the SMB share.
\n\nThis method walks through the given local directory and all its subdirectories, uploading each file to the\ncorresponding directory structure on the SMB share. It first checks if the local path is a directory. If it is,\nit iterates over all files and directories within the local path, creating necessary directories on the SMB share\nand uploading files. If the local path is not a directory, it prints an error message.
\n\nArgs:\n localpath (str, optional): The local directory path from which files will be uploaded. Defaults to None.
\n", "signature": "(self, localpath=None):", "funcdef": "def"}, {"fullname": "smbclientng.core.SMBSession.SMBSession.rmdir", "modulename": "smbclientng.core.SMBSession", "qualname": "SMBSession.rmdir", "kind": "function", "doc": "Removes a directory from the SMB share at the specified path.
\n\nThis method attempts to delete a directory located at the given path on the SMB share. If the operation fails,\nit prints an error message indicating the failure and the reason. If debugging is enabled, it also prints\nthe stack trace of the exception.
\n\nArgs:\n path (str, optional): The path of the directory to be removed on the SMB share. Defaults to None.
\n", "signature": "(self, path=None):", "funcdef": "def"}, {"fullname": "smbclientng.core.SMBSession.SMBSession.rm", "modulename": "smbclientng.core.SMBSession", "qualname": "SMBSession.rm", "kind": "function", "doc": "Removes a file from the SMB share at the specified path.
\n\nThis method attempts to delete a file located at the given path on the SMB share. If the operation fails,\nit prints an error message indicating the failure and the reason. If debugging is enabled, it also prints\nthe stack trace of the exception.
\n\nArgs:\n path (str, optional): The path of the file to be removed on the SMB share. Defaults to None.
\n", "signature": "(self, path=None):", "funcdef": "def"}, {"fullname": "smbclientng.core.SMBSession.SMBSession.tree", "modulename": "smbclientng.core.SMBSession", "qualname": "SMBSession.tree", "kind": "function", "doc": "Recursively lists the directory structure of the SMB share starting from the specified path.
\n\nThis function prints a visual representation of the directory tree of the remote SMB share. It uses\nrecursion to navigate through directories and lists all files and subdirectories in each directory.\nThe output is color-coded and formatted to enhance readability, with directories highlighted in cyan.
\n\nArgs:\n path (str, optional): The starting path on the SMB share from which to begin listing the tree.\n Defaults to the root of the current share.
\n", "signature": "(self, path=None):", "funcdef": "def"}, {"fullname": "smbclientng.core.SMBSession.SMBSession.set_share", "modulename": "smbclientng.core.SMBSession", "qualname": "SMBSession.set_share", "kind": "function", "doc": "Sets the current SMB share to the specified share name.
\n\nThis method updates the SMB session to use the specified share name. It checks if the share name is valid\nand updates the smb_share attribute of the SMBSession instance.
\n\nParameters:\n shareName (str): The name of the share to set as the current SMB share.
\n\nRaises:\n ValueError: If the shareName is None or an empty string.
\n", "signature": "(self, shareName):", "funcdef": "def"}, {"fullname": "smbclientng.core.SMBSession.SMBSession.set_cwd", "modulename": "smbclientng.core.SMBSession", "qualname": "SMBSession.set_cwd", "kind": "function", "doc": "Sets the current working directory on the SMB share to the specified path.
\n\nThis method updates the current working directory (cwd) of the SMB session to the given path if it is a valid directory.\nIf the specified path is not a directory, the cwd remains unchanged.
\n\nParameters:\n path (str): The path to set as the current working directory.
\n\nRaises:\n ValueError: If the specified path is not a directory.
\n", "signature": "(self, path=None):", "funcdef": "def"}, {"fullname": "smbclientng.core.utils", "modulename": "smbclientng.core.utils", "kind": "module", "doc": "\n"}, {"fullname": "smbclientng.core.utils.parse_lm_nt_hashes", "modulename": "smbclientng.core.utils", "qualname": "parse_lm_nt_hashes", "kind": "function", "doc": "Parse the input string containing LM and NT hash values and return them separately.
\n\nThis function takes a string containing LM and NT hash values, typically separated by a colon (:).\nIt returns the LM and NT hash values as separate strings. If only one hash value is provided, it is\nassumed to be the NT hash and the LM hash is set to its default value. If no valid hash values are\nfound, both return values are empty strings.
\n\nArgs:\n lm_nt_hashes_string (str): A string containing LM and NT hash values separated by a colon.
\n\nReturns:\n tuple: A tuple containing two strings (lm_hash_value, nt_hash_value).\n - lm_hash_value: The LM hash value or its default if not provided.\n - nt_hash_value: The NT hash value or its default if not provided.
\n\nExtracted from p0dalirius/sectools library\nSrc: https://github.com/p0dalirius/sectools/blob/7bb3f5cb7815ad4d4845713c8739e2e2b0ea4e75/sectools/windows/crypto.py#L11-L24
\n", "signature": "(lm_nt_hashes_string):", "funcdef": "def"}, {"fullname": "smbclientng.core.utils.b_filesize", "modulename": "smbclientng.core.utils", "qualname": "b_filesize", "kind": "function", "doc": "Convert a file size from bytes to a more readable format using the largest appropriate unit.
\n\nThis function takes an integer representing a file size in bytes and converts it to a human-readable\nstring using the largest appropriate unit from bytes (B) to petabytes (PB). The result is rounded to\ntwo decimal places.
\n\nArgs:\n l (int): The file size in bytes.
\n\nReturns:\n str: A string representing the file size in a more readable format, including the appropriate unit.
\n", "signature": "(l):", "funcdef": "def"}, {"fullname": "smbclientng.core.utils.unix_permissions", "modulename": "smbclientng.core.utils", "qualname": "unix_permissions", "kind": "function", "doc": "Generate a string representing the Unix-style permissions for a given file or directory.
\n\nThis function uses the os.lstat() method to retrieve the status of the specified file or directory,\nthen constructs a string that represents the Unix-style permissions based on the mode of the file.
\n\nArgs:\n entryname (str): The path to the file or directory for which permissions are being determined.
\n\nReturns:\n str: A string of length 10 representing the Unix-style permissions (e.g., '-rwxr-xr--').\n The first character is either 'd' (directory), '-' (not a directory), followed by\n three groups of 'r', 'w', 'x' (read, write, execute permissions) for owner, group,\n and others respectively.
\n", "signature": "(entryname):", "funcdef": "def"}, {"fullname": "smbclientng.core.utils.STYPE_MASK", "modulename": "smbclientng.core.utils", "qualname": "STYPE_MASK", "kind": "function", "doc": "Extracts the share type flags from a given share type value.
\n\nThis function uses bitwise operations to determine which share type flags are set in the provided stype_value
.\nIt checks against known share type flags and returns a list of the flags that are set.
Parameters:\n stype_value (int): The share type value to analyze, typically obtained from SMB share properties.
\n\nReturns:\n list: A list of strings, where each string represents a share type flag that is set in the input value.
\n", "signature": "(stype_value):", "funcdef": "def"}, {"fullname": "smbclientng.core.utils.windows_ls_entry", "modulename": "smbclientng.core.utils", "qualname": "windows_ls_entry", "kind": "function", "doc": "This function generates a metadata string based on the attributes of the provided entry object.
\n\nParameters:\n entry (object): An object representing a file or directory entry.
\n\nReturns:\n str: A string representing the metadata of the entry, including attributes like directory, archive, compressed, hidden, normal, readonly, system, and temporary.
\n", "signature": "(entry, config, pathToPrint=None):", "funcdef": "def"}, {"fullname": "smbclientng.core.utils.local_tree", "modulename": "smbclientng.core.utils", "qualname": "local_tree", "kind": "function", "doc": "This function recursively lists the contents of a directory in a tree-like format.
\n\nParameters:\n path (str): The path to the directory to list.\n config (object): Configuration settings which may affect the output, such as whether to use colors.
\n\nReturns:\n None: This function does not return anything but prints the directory tree to the console.
\n", "signature": "(path, config):", "funcdef": "def"}, {"fullname": "smbclientng.modules", "modulename": "smbclientng.modules", "kind": "module", "doc": "\n"}, {"fullname": "smbclientng.modules.Find", "modulename": "smbclientng.modules.Find", "kind": "module", "doc": "\n"}, {"fullname": "smbclientng.modules.Find.Find", "modulename": "smbclientng.modules.Find", "qualname": "Find", "kind": "class", "doc": "A class to search for files in a directory hierarchy.
\n\nThis class provides functionality to search for files based on various criteria in a directory hierarchy.
\n", "bases": "smbclientng.core.Module.Module"}, {"fullname": "smbclientng.modules.Find.Find.name", "modulename": "smbclientng.modules.Find", "qualname": "Find.name", "kind": "variable", "doc": "\n", "default_value": "'find'"}, {"fullname": "smbclientng.modules.Find.Find.description", "modulename": "smbclientng.modules.Find", "qualname": "Find.description", "kind": "variable", "doc": "\n", "default_value": "'Search for files in a directory hierarchy'"}, {"fullname": "smbclientng.modules.Find.Find.parseArgs", "modulename": "smbclientng.modules.Find", "qualname": "Find.parseArgs", "kind": "function", "doc": "Parses the command line arguments provided to the module.
\n\nThis method initializes the argument parser with the module's name and description, and defines all the necessary arguments that the module accepts. It then parses the provided command line arguments based on these definitions.
\n\nArgs:\n arguments (str): A string of command line arguments.
\n\nReturns:\n ModuleArgumentParser.Namespace | None: The parsed arguments as a Namespace object if successful, None if there are no arguments or help is requested.
\n", "signature": "(self, arguments):", "funcdef": "def"}, {"fullname": "smbclientng.modules.Find.Find.run", "modulename": "smbclientng.modules.Find", "qualname": "Find.run", "kind": "function", "doc": "This function recursively searches for files in a directory hierarchy and prints the results based on specified criteria.
\n\nArgs:\n base_dir (str): The base directory to start the search from.\n paths (list): List of paths to search within the base directory.\n depth (int): The current depth level in the directory hierarchy.
\n\nReturns:\n None
\n", "signature": "(self, arguments):", "funcdef": "def"}]; + + // mirrored in build-search-index.js (part 1) + // Also split on html tags. this is a cheap heuristic, but good enough. + elasticlunr.tokenizer.setSeperator(/[\s\-.;&_'"=,()]+|<[^>]*>/); + + let searchIndex; + if (docs._isPrebuiltIndex) { + console.info("using precompiled search index"); + searchIndex = elasticlunr.Index.load(docs); + } else { + console.time("building search index"); + // mirrored in build-search-index.js (part 2) + searchIndex = elasticlunr(function () { + this.pipeline.remove(elasticlunr.stemmer); + this.pipeline.remove(elasticlunr.stopWordFilter); + this.addField("qualname"); + this.addField("fullname"); + this.addField("annotation"); + this.addField("default_value"); + this.addField("signature"); + this.addField("bases"); + this.addField("doc"); + this.setRef("fullname"); + }); + for (let doc of docs) { + searchIndex.addDoc(doc); + } + console.timeEnd("building search index"); + } + + return (term) => searchIndex.search(term, { + fields: { + qualname: {boost: 4}, + fullname: {boost: 2}, + annotation: {boost: 2}, + default_value: {boost: 2}, + signature: {boost: 2}, + bases: {boost: 2}, + doc: {boost: 1}, + }, + expand: true + }); +})(); \ No newline at end of file diff --git a/documentation/smbclientng.html b/documentation/smbclientng.html new file mode 100644 index 0000000..7d766cd --- /dev/null +++ b/documentation/smbclientng.html @@ -0,0 +1,245 @@ + + + + + + +1#!/usr/bin/env python3 + 2# -*- coding: utf-8 -*- + 3# File name : CommandCompleter.py + 4# Author : Podalirius (@podalirius_) + 5# Date created : 20 may 2024 + 6 + 7 + 8import ntpath + 9import os + 10 + 11 + 12class CommandCompleter(object): + 13 """ + 14 A class to handle command completion for the smbclient-ng shell. + 15 + 16 This class provides a command completion feature that suggests possible command names based on the current input. + 17 It uses a dictionary to store commands and their descriptions, which helps in providing hints during the command line + 18 interaction in the smbclient-ng shell. + 19 + 20 Attributes: + 21 smbSession (SMBSession): An instance of SMBSession which maintains the current SMB session. + 22 commands (dict): A dictionary containing command names as keys and their descriptions and subcommands as values. + 23 + 24 Methods: + 25 __init__(self, smbSession): Initializes the CommandCompleter with an SMBSession. + 26 """ + 27 + 28 commands = { + 29 "cd": { + 30 "description": [ + 31 "Change the current working directory.", + 32 "Syntax: 'cd <directory>'" + 33 ], + 34 "subcommands": [] + 35 }, + 36 "close": { + 37 "description": [ + 38 "Closes the SMB connection to the remote machine.", + 39 "Syntax: 'close'" + 40 ], + 41 "subcommands": [] + 42 }, + 43 "connect": { + 44 "description": [ + 45 "Connect to the remote machine (useful if connection timed out).", + 46 "Syntax: 'connect'" + 47 ], + 48 "subcommands": [] + 49 }, + 50 "dir": { + 51 "description": [ + 52 "List the contents of the current working directory.", + 53 "Syntax: 'dir'" + 54 ], + 55 "subcommands": [] + 56 }, + 57 "exit": { + 58 "description": [ + 59 "Exits the smbclient-ng script.", + 60 "Syntax: 'exit'" + 61 ], + 62 "subcommands": [] + 63 }, + 64 "get": { + 65 "description": [ + 66 "Get a remote file.", + 67 "Syntax: 'get [-r] <directory or file>'" + 68 ], + 69 "subcommands": [] + 70 }, + 71 "help": { + 72 "description": [ + 73 "Displays this help message.", + 74 "Syntax: 'help'" + 75 ], + 76 "subcommands": ["format"] + 77 }, + 78 "info": { + 79 "description": [ + 80 "Get information about the server and or the share.", + 81 "Syntax: 'info [server|share]'" + 82 ], + 83 "subcommands": ["server", "share"] + 84 }, + 85 "lcd": { + 86 "description": [ + 87 "Changes the current local directory.", + 88 "Syntax: 'lcd <directory>'" + 89 ], + 90 "subcommands": [] + 91 }, + 92 "lls": { + 93 "description": [ + 94 "Lists the contents of the current local directory.", + 95 "Syntax: 'lls'" + 96 ], + 97 "subcommands": [] + 98 }, + 99 "lmkdir": { +100 "description": [ +101 "Creates a new local directory.", +102 "Syntax: 'lmkdir <directory>'" +103 ], +104 "subcommands": [] +105 }, +106 "lpwd": { +107 "description": [ +108 "Shows the current local directory.", +109 "Syntax: 'lpwd'" +110 ], +111 "subcommands": [] +112 }, +113 "lrm": { +114 "description": [ +115 "Removes a local file.", +116 "Syntax: 'lrm <file>'" +117 ], +118 "subcommands": [] +119 }, +120 "lrmdir": { +121 "description": [ +122 "Removes a local directory.", +123 "Syntax: 'lrmdir <directory>'" +124 ], +125 "subcommands": [] +126 }, +127 "ls": { +128 "description": [ +129 "List the contents of the current remote working directory.", +130 "Syntax: 'ls'" +131 ], +132 "subcommands": [] +133 }, +134 "ltree": { +135 "description": [ +136 "Displays a tree view of the local directories.", +137 "Syntax: 'ltree [directory]'" +138 ], +139 "subcommands": [] +140 }, +141 "mkdir": { +142 "description": [ +143 "Creates a new remote directory.", +144 "Syntax: 'mkdir <directory>'" +145 ], +146 "subcommands": [] +147 }, +148 "module": { +149 "description": [ +150 "Loads a specific module for additional functionalities.", +151 "Syntax: 'module <name>'" +152 ], +153 "subcommands": [] +154 }, +155 "put": { +156 "description": [ +157 "Put a local file or directory in a remote directory.", +158 "Syntax: 'put [-r] <directory or file>'" +159 ], +160 "subcommands": [] +161 }, +162 "reconnect": { +163 "description": [ +164 "Reconnect to the remote machine (useful if connection timed out).", +165 "Syntax: 'reconnect'" +166 ], +167 "subcommands": [] +168 }, +169 "reset": { +170 "description": [ +171 "Reset the TTY output, useful if it was broken after printing a binary file on stdout.", +172 "Syntax: 'reset'" +173 ], +174 "subcommands": [] +175 }, +176 "rmdir": { +177 "description": [ +178 "Removes a remote directory.", +179 "Syntax: 'rmdir <directory>'" +180 ], +181 "subcommands": [] +182 }, +183 "rm": { +184 "description": [ +185 "Removes a remote file.", +186 "Syntax: 'rm <file>'" +187 ], +188 "subcommands": [] +189 }, +190 "shares": { +191 "description": [ +192 "Lists the SMB shares served by the remote machine.", +193 "Syntax: 'shares'" +194 ], +195 "subcommands": [] +196 }, +197 "use": { +198 "description": [ +199 "Use a SMB share.", +200 "Syntax: 'use <sharename>'" +201 ], +202 "subcommands": [] +203 }, +204 "tree": { +205 "description": [ +206 "Displays a tree view of the remote directories.", +207 "Syntax: 'tree [directory]'" +208 ], +209 "subcommands": [] +210 }, +211 } +212 +213 def __init__(self, smbSession, config): +214 # Objects +215 self.smbSession = smbSession +216 self.config = config +217 # Pre computing for some commands +218 self.commands["help"]["subcommands"] = ["format"] + list(self.commands.keys()) +219 self.commands["help"]["subcommands"].remove("help") +220 +221 def complete(self, text, state): +222 """ +223 Function to handle command completion in the LDAP console. +224 +225 This function completes the user"s input based on the available options for commands in the LDAP console. +226 +227 Args: +228 text (str): The current text input by the user. +229 state (int): The current state of completion. +230 +231 Returns: +232 str: The next completion suggestion based on the user"s input state. +233 """ +234 +235 if state == 0: +236 +237 # No text typed yet, need the list of commands available +238 if len(text) == 0: +239 self.matches = [s for s in self.commands.keys()] +240 +241 elif len(text) != 0: +242 # This is for the main command +243 if text.count(" ") == 0: +244 self.matches = [s for s in self.commands.keys() if s and s.startswith(text)] +245 +246 # This is for subcommands +247 elif text.count(" ") >= 1: +248 command, remainder = text.split(" ", 1) +249 if command in self.commands.keys(): +250 if command == "use": +251 # Choose SMB Share to connect to +252 self.matches = [ +253 command + " " + s.lower() +254 for s in self.smbSession.list_shares().keys() +255 if s.lower().startswith(remainder.lower()) +256 ] +257 +258 elif command in ["cd", "dir", "ls", "mkdir", "rmdir", "tree"]: +259 # Choose remote directory +260 path = "" +261 if '\\' in remainder.strip() or '/' in remainder.strip(): +262 path = remainder.strip().replace('/', ntpath.sep) +263 path = ntpath.sep.join(path.split(ntpath.sep)[:-1]) +264 +265 directory_contents = self.smbSession.list_contents(path=path).items() +266 +267 matching_entries = [] +268 for _, entry in directory_contents: +269 if entry.is_directory() and entry.get_longname() not in [".",".."]: +270 if len(path) != 0: +271 matching_entries.append(path + ntpath.sep + entry.get_longname() + ntpath.sep) +272 else: +273 matching_entries.append(entry.get_longname() + ntpath.sep) +274 +275 self.matches = [ +276 command + " " + s +277 for s in matching_entries +278 if s.lower().startswith(remainder.lower()) +279 ] +280 +281 elif command in ["get", "rm"]: +282 # Choose local files and directories +283 path = "" +284 if '\\' in remainder.strip() or '/' in remainder.strip(): +285 path = remainder.strip().replace('/', ntpath.sep) +286 path = ntpath.sep.join(path.split(ntpath.sep)[:-1]) +287 +288 directory_contents = self.smbSession.list_contents(path=path).items() +289 +290 matching_entries = [] +291 for _, entry in directory_contents: +292 if entry.get_longname() not in [".",".."]: +293 if len(path) != 0: +294 if entry.is_directory(): +295 matching_entries.append(path + ntpath.sep + entry.get_longname() + ntpath.sep) +296 else: +297 matching_entries.append(path + ntpath.sep + entry.get_longname()) +298 else: +299 if entry.is_directory(): +300 matching_entries.append(entry.get_longname() + ntpath.sep) +301 else: +302 matching_entries.append(entry.get_longname()) +303 +304 self.matches = [ +305 command + " " + s +306 for s in matching_entries +307 if s.lower().startswith(remainder.lower()) +308 ] +309 +310 elif command in ["lcd", "lls", "put", "lmkdir", "lrm", "lrmdir"]: +311 # Choose directory +312 path = "" +313 if os.path.sep in remainder.strip(): +314 path = path.split(os.path.sep)[:-1] +315 path = os.path.sep.join(path) +316 +317 # Current dir +318 if len(path.strip()) == 0: +319 path = "." +320 +321 directory_contents = os.listdir(path=path + os.path.sep) +322 matching_entries = [] +323 for entry in directory_contents: +324 if entry not in [".",".."]: +325 entry_path = path + os.path.sep + entry +326 if os.path.isdir(entry_path): +327 matching_entries.append(entry_path + os.path.sep) +328 else: +329 matching_entries.append(entry_path) +330 +331 self.matches = [ +332 command + " " + s +333 for s in matching_entries +334 if s.startswith(remainder) +335 ] +336 +337 else: +338 # Generic case for subcommands +339 self.matches = [ +340 command + " " + s +341 for s in self.commands[command]["subcommands"] +342 if s.startswith(remainder) +343 ] +344 else: +345 # Unknown subcommand, skipping autocomplete +346 pass +347 else: +348 self.matches = [] +349 else: +350 self.matches = self.commands.keys()[:] +351 +352 try: +353 return self.matches[state] + " " +354 except IndexError: +355 return None +356 +357 def print_help(self, command=None): +358 """ +359 Prints help information for a specific command or all commands if no command is specified. +360 +361 This method displays the help information for the command passed as an argument. If no command is specified, +362 it prints the help information for all available commands. The help information includes the command syntax, +363 description, and any subcommands associated with it. This method is designed to provide users with the necessary +364 guidance on how to use the commands in the smbclient-ng shell. +365 +366 Args: +367 command (str, optional): The command to display help information for. If None, help for all commands is displayed. +368 +369 Returns: +370 None +371 """ +372 +373 if command is not None: +374 if command not in list(self.commands.keys())+["format"]: +375 command = None +376 +377 # Print help for a specific command +378 if command is not None: +379 if command == "format": +380 self.print_help_format() +381 else: +382 print("│") +383 if self.config.no_colors: +384 command_str = command + "─"* (15 - len(command)) +385 if len(self.commands[command]["description"]) == 0: +386 print("│ ■ %s┤ " % command_str) +387 elif len(self.commands[command]["description"]) == 1: +388 print("│ ■ %s┤ %s " % (command_str, self.commands[command]["description"][0])) +389 else: +390 print("│ ■ %s┤ %s " % (command_str, self.commands[command]["description"][0])) +391 for line in self.commands[command]["description"][1:]: +392 print("│ %s│ %s " % (" "*(15+3), line)) +393 else: +394 command_str = command + " \x1b[90m" + "─"* (15 - len(command)) + "\x1b[0m" +395 if len(self.commands[command]["description"]) == 0: +396 print("│ ■ %s\x1b[90m┤\x1b[0m " % command_str) +397 elif len(self.commands[command]["description"]) == 1: +398 print("│ ■ %s\x1b[90m┤\x1b[0m %s " % (command_str, self.commands[command]["description"][0])) +399 else: +400 print("│ ■ %s\x1b[90m┤\x1b[0m %s " % (command_str, self.commands[command]["description"][0])) +401 for line in self.commands[command]["description"][1:]: +402 print("│ %s\x1b[90m│\x1b[0m %s " % (" "*(15+2), line)) +403 print("│") +404 # Generic help +405 else: +406 print("│") +407 commands = sorted(self.commands.keys()) +408 for command in commands: +409 if self.config.no_colors: +410 command_str = command + "─"* (15 - len(command)) +411 if len(self.commands[command]["description"]) == 0: +412 print("│ ■ %s┤ " % command_str) +413 elif len(self.commands[command]["description"]) == 1: +414 print("│ ■ %s┤ %s " % (command_str, self.commands[command]["description"][0])) +415 else: +416 print("│ ■ %s┤ %s " % (command_str, self.commands[command]["description"][0])) +417 for line in self.commands[command]["description"][1:]: +418 print("│ %s│ %s " % (" "*(15+2), line)) +419 else: +420 command_str = command + " \x1b[90m" + "─"* (15 - len(command)) + "\x1b[0m" +421 if len(self.commands[command]["description"]) == 0: +422 print("│ ■ %s\x1b[90m┤\x1b[0m " % command_str) +423 elif len(self.commands[command]["description"]) == 1: +424 print("│ ■ %s\x1b[90m┤\x1b[0m %s " % (command_str, self.commands[command]["description"][0])) +425 else: +426 print("│ ■ %s\x1b[90m┤\x1b[0m %s " % (command_str, self.commands[command]["description"][0])) +427 for line in self.commands[command]["description"][1:]: +428 print("│ %s\x1b[90m│\x1b[0m %s " % (" "*(15+3), line)) +429 print("│") +430 +431 def print_help_format(self): +432 """ +433 Prints the help information for the 'format' used in remote 'ls' and 'dir' commands. +434 +435 This function displays the format of file attributes used in the smbclient-ng shell. It explains the meaning +436 of each character in the file attribute string, such as whether a file is read-only, hidden, or a directory. +437 """ +438 +439 print("File attributes format:\n") +440 print("\x1b[1mdachnrst\x1b[0m") +441 print("\x1b[90m│││││││└──>\x1b[0m Temporary") +442 print("\x1b[90m││││││└───>\x1b[0m System") +443 print("\x1b[90m│││││└────>\x1b[0m Read-Only") +444 print("\x1b[90m││││└─────>\x1b[0m Normal") +445 print("\x1b[90m│││└──────>\x1b[0m Hidden") +446 print("\x1b[90m││└───────>\x1b[0m Compressed") +447 print("\x1b[90m│└────────>\x1b[0m Archived") +448 print("\x1b[90m└─────────>\x1b[0m Directory") +
13class CommandCompleter(object): + 14 """ + 15 A class to handle command completion for the smbclient-ng shell. + 16 + 17 This class provides a command completion feature that suggests possible command names based on the current input. + 18 It uses a dictionary to store commands and their descriptions, which helps in providing hints during the command line + 19 interaction in the smbclient-ng shell. + 20 + 21 Attributes: + 22 smbSession (SMBSession): An instance of SMBSession which maintains the current SMB session. + 23 commands (dict): A dictionary containing command names as keys and their descriptions and subcommands as values. + 24 + 25 Methods: + 26 __init__(self, smbSession): Initializes the CommandCompleter with an SMBSession. + 27 """ + 28 + 29 commands = { + 30 "cd": { + 31 "description": [ + 32 "Change the current working directory.", + 33 "Syntax: 'cd <directory>'" + 34 ], + 35 "subcommands": [] + 36 }, + 37 "close": { + 38 "description": [ + 39 "Closes the SMB connection to the remote machine.", + 40 "Syntax: 'close'" + 41 ], + 42 "subcommands": [] + 43 }, + 44 "connect": { + 45 "description": [ + 46 "Connect to the remote machine (useful if connection timed out).", + 47 "Syntax: 'connect'" + 48 ], + 49 "subcommands": [] + 50 }, + 51 "dir": { + 52 "description": [ + 53 "List the contents of the current working directory.", + 54 "Syntax: 'dir'" + 55 ], + 56 "subcommands": [] + 57 }, + 58 "exit": { + 59 "description": [ + 60 "Exits the smbclient-ng script.", + 61 "Syntax: 'exit'" + 62 ], + 63 "subcommands": [] + 64 }, + 65 "get": { + 66 "description": [ + 67 "Get a remote file.", + 68 "Syntax: 'get [-r] <directory or file>'" + 69 ], + 70 "subcommands": [] + 71 }, + 72 "help": { + 73 "description": [ + 74 "Displays this help message.", + 75 "Syntax: 'help'" + 76 ], + 77 "subcommands": ["format"] + 78 }, + 79 "info": { + 80 "description": [ + 81 "Get information about the server and or the share.", + 82 "Syntax: 'info [server|share]'" + 83 ], + 84 "subcommands": ["server", "share"] + 85 }, + 86 "lcd": { + 87 "description": [ + 88 "Changes the current local directory.", + 89 "Syntax: 'lcd <directory>'" + 90 ], + 91 "subcommands": [] + 92 }, + 93 "lls": { + 94 "description": [ + 95 "Lists the contents of the current local directory.", + 96 "Syntax: 'lls'" + 97 ], + 98 "subcommands": [] + 99 }, +100 "lmkdir": { +101 "description": [ +102 "Creates a new local directory.", +103 "Syntax: 'lmkdir <directory>'" +104 ], +105 "subcommands": [] +106 }, +107 "lpwd": { +108 "description": [ +109 "Shows the current local directory.", +110 "Syntax: 'lpwd'" +111 ], +112 "subcommands": [] +113 }, +114 "lrm": { +115 "description": [ +116 "Removes a local file.", +117 "Syntax: 'lrm <file>'" +118 ], +119 "subcommands": [] +120 }, +121 "lrmdir": { +122 "description": [ +123 "Removes a local directory.", +124 "Syntax: 'lrmdir <directory>'" +125 ], +126 "subcommands": [] +127 }, +128 "ls": { +129 "description": [ +130 "List the contents of the current remote working directory.", +131 "Syntax: 'ls'" +132 ], +133 "subcommands": [] +134 }, +135 "ltree": { +136 "description": [ +137 "Displays a tree view of the local directories.", +138 "Syntax: 'ltree [directory]'" +139 ], +140 "subcommands": [] +141 }, +142 "mkdir": { +143 "description": [ +144 "Creates a new remote directory.", +145 "Syntax: 'mkdir <directory>'" +146 ], +147 "subcommands": [] +148 }, +149 "module": { +150 "description": [ +151 "Loads a specific module for additional functionalities.", +152 "Syntax: 'module <name>'" +153 ], +154 "subcommands": [] +155 }, +156 "put": { +157 "description": [ +158 "Put a local file or directory in a remote directory.", +159 "Syntax: 'put [-r] <directory or file>'" +160 ], +161 "subcommands": [] +162 }, +163 "reconnect": { +164 "description": [ +165 "Reconnect to the remote machine (useful if connection timed out).", +166 "Syntax: 'reconnect'" +167 ], +168 "subcommands": [] +169 }, +170 "reset": { +171 "description": [ +172 "Reset the TTY output, useful if it was broken after printing a binary file on stdout.", +173 "Syntax: 'reset'" +174 ], +175 "subcommands": [] +176 }, +177 "rmdir": { +178 "description": [ +179 "Removes a remote directory.", +180 "Syntax: 'rmdir <directory>'" +181 ], +182 "subcommands": [] +183 }, +184 "rm": { +185 "description": [ +186 "Removes a remote file.", +187 "Syntax: 'rm <file>'" +188 ], +189 "subcommands": [] +190 }, +191 "shares": { +192 "description": [ +193 "Lists the SMB shares served by the remote machine.", +194 "Syntax: 'shares'" +195 ], +196 "subcommands": [] +197 }, +198 "use": { +199 "description": [ +200 "Use a SMB share.", +201 "Syntax: 'use <sharename>'" +202 ], +203 "subcommands": [] +204 }, +205 "tree": { +206 "description": [ +207 "Displays a tree view of the remote directories.", +208 "Syntax: 'tree [directory]'" +209 ], +210 "subcommands": [] +211 }, +212 } +213 +214 def __init__(self, smbSession, config): +215 # Objects +216 self.smbSession = smbSession +217 self.config = config +218 # Pre computing for some commands +219 self.commands["help"]["subcommands"] = ["format"] + list(self.commands.keys()) +220 self.commands["help"]["subcommands"].remove("help") +221 +222 def complete(self, text, state): +223 """ +224 Function to handle command completion in the LDAP console. +225 +226 This function completes the user"s input based on the available options for commands in the LDAP console. +227 +228 Args: +229 text (str): The current text input by the user. +230 state (int): The current state of completion. +231 +232 Returns: +233 str: The next completion suggestion based on the user"s input state. +234 """ +235 +236 if state == 0: +237 +238 # No text typed yet, need the list of commands available +239 if len(text) == 0: +240 self.matches = [s for s in self.commands.keys()] +241 +242 elif len(text) != 0: +243 # This is for the main command +244 if text.count(" ") == 0: +245 self.matches = [s for s in self.commands.keys() if s and s.startswith(text)] +246 +247 # This is for subcommands +248 elif text.count(" ") >= 1: +249 command, remainder = text.split(" ", 1) +250 if command in self.commands.keys(): +251 if command == "use": +252 # Choose SMB Share to connect to +253 self.matches = [ +254 command + " " + s.lower() +255 for s in self.smbSession.list_shares().keys() +256 if s.lower().startswith(remainder.lower()) +257 ] +258 +259 elif command in ["cd", "dir", "ls", "mkdir", "rmdir", "tree"]: +260 # Choose remote directory +261 path = "" +262 if '\\' in remainder.strip() or '/' in remainder.strip(): +263 path = remainder.strip().replace('/', ntpath.sep) +264 path = ntpath.sep.join(path.split(ntpath.sep)[:-1]) +265 +266 directory_contents = self.smbSession.list_contents(path=path).items() +267 +268 matching_entries = [] +269 for _, entry in directory_contents: +270 if entry.is_directory() and entry.get_longname() not in [".",".."]: +271 if len(path) != 0: +272 matching_entries.append(path + ntpath.sep + entry.get_longname() + ntpath.sep) +273 else: +274 matching_entries.append(entry.get_longname() + ntpath.sep) +275 +276 self.matches = [ +277 command + " " + s +278 for s in matching_entries +279 if s.lower().startswith(remainder.lower()) +280 ] +281 +282 elif command in ["get", "rm"]: +283 # Choose local files and directories +284 path = "" +285 if '\\' in remainder.strip() or '/' in remainder.strip(): +286 path = remainder.strip().replace('/', ntpath.sep) +287 path = ntpath.sep.join(path.split(ntpath.sep)[:-1]) +288 +289 directory_contents = self.smbSession.list_contents(path=path).items() +290 +291 matching_entries = [] +292 for _, entry in directory_contents: +293 if entry.get_longname() not in [".",".."]: +294 if len(path) != 0: +295 if entry.is_directory(): +296 matching_entries.append(path + ntpath.sep + entry.get_longname() + ntpath.sep) +297 else: +298 matching_entries.append(path + ntpath.sep + entry.get_longname()) +299 else: +300 if entry.is_directory(): +301 matching_entries.append(entry.get_longname() + ntpath.sep) +302 else: +303 matching_entries.append(entry.get_longname()) +304 +305 self.matches = [ +306 command + " " + s +307 for s in matching_entries +308 if s.lower().startswith(remainder.lower()) +309 ] +310 +311 elif command in ["lcd", "lls", "put", "lmkdir", "lrm", "lrmdir"]: +312 # Choose directory +313 path = "" +314 if os.path.sep in remainder.strip(): +315 path = path.split(os.path.sep)[:-1] +316 path = os.path.sep.join(path) +317 +318 # Current dir +319 if len(path.strip()) == 0: +320 path = "." +321 +322 directory_contents = os.listdir(path=path + os.path.sep) +323 matching_entries = [] +324 for entry in directory_contents: +325 if entry not in [".",".."]: +326 entry_path = path + os.path.sep + entry +327 if os.path.isdir(entry_path): +328 matching_entries.append(entry_path + os.path.sep) +329 else: +330 matching_entries.append(entry_path) +331 +332 self.matches = [ +333 command + " " + s +334 for s in matching_entries +335 if s.startswith(remainder) +336 ] +337 +338 else: +339 # Generic case for subcommands +340 self.matches = [ +341 command + " " + s +342 for s in self.commands[command]["subcommands"] +343 if s.startswith(remainder) +344 ] +345 else: +346 # Unknown subcommand, skipping autocomplete +347 pass +348 else: +349 self.matches = [] +350 else: +351 self.matches = self.commands.keys()[:] +352 +353 try: +354 return self.matches[state] + " " +355 except IndexError: +356 return None +357 +358 def print_help(self, command=None): +359 """ +360 Prints help information for a specific command or all commands if no command is specified. +361 +362 This method displays the help information for the command passed as an argument. If no command is specified, +363 it prints the help information for all available commands. The help information includes the command syntax, +364 description, and any subcommands associated with it. This method is designed to provide users with the necessary +365 guidance on how to use the commands in the smbclient-ng shell. +366 +367 Args: +368 command (str, optional): The command to display help information for. If None, help for all commands is displayed. +369 +370 Returns: +371 None +372 """ +373 +374 if command is not None: +375 if command not in list(self.commands.keys())+["format"]: +376 command = None +377 +378 # Print help for a specific command +379 if command is not None: +380 if command == "format": +381 self.print_help_format() +382 else: +383 print("│") +384 if self.config.no_colors: +385 command_str = command + "─"* (15 - len(command)) +386 if len(self.commands[command]["description"]) == 0: +387 print("│ ■ %s┤ " % command_str) +388 elif len(self.commands[command]["description"]) == 1: +389 print("│ ■ %s┤ %s " % (command_str, self.commands[command]["description"][0])) +390 else: +391 print("│ ■ %s┤ %s " % (command_str, self.commands[command]["description"][0])) +392 for line in self.commands[command]["description"][1:]: +393 print("│ %s│ %s " % (" "*(15+3), line)) +394 else: +395 command_str = command + " \x1b[90m" + "─"* (15 - len(command)) + "\x1b[0m" +396 if len(self.commands[command]["description"]) == 0: +397 print("│ ■ %s\x1b[90m┤\x1b[0m " % command_str) +398 elif len(self.commands[command]["description"]) == 1: +399 print("│ ■ %s\x1b[90m┤\x1b[0m %s " % (command_str, self.commands[command]["description"][0])) +400 else: +401 print("│ ■ %s\x1b[90m┤\x1b[0m %s " % (command_str, self.commands[command]["description"][0])) +402 for line in self.commands[command]["description"][1:]: +403 print("│ %s\x1b[90m│\x1b[0m %s " % (" "*(15+2), line)) +404 print("│") +405 # Generic help +406 else: +407 print("│") +408 commands = sorted(self.commands.keys()) +409 for command in commands: +410 if self.config.no_colors: +411 command_str = command + "─"* (15 - len(command)) +412 if len(self.commands[command]["description"]) == 0: +413 print("│ ■ %s┤ " % command_str) +414 elif len(self.commands[command]["description"]) == 1: +415 print("│ ■ %s┤ %s " % (command_str, self.commands[command]["description"][0])) +416 else: +417 print("│ ■ %s┤ %s " % (command_str, self.commands[command]["description"][0])) +418 for line in self.commands[command]["description"][1:]: +419 print("│ %s│ %s " % (" "*(15+2), line)) +420 else: +421 command_str = command + " \x1b[90m" + "─"* (15 - len(command)) + "\x1b[0m" +422 if len(self.commands[command]["description"]) == 0: +423 print("│ ■ %s\x1b[90m┤\x1b[0m " % command_str) +424 elif len(self.commands[command]["description"]) == 1: +425 print("│ ■ %s\x1b[90m┤\x1b[0m %s " % (command_str, self.commands[command]["description"][0])) +426 else: +427 print("│ ■ %s\x1b[90m┤\x1b[0m %s " % (command_str, self.commands[command]["description"][0])) +428 for line in self.commands[command]["description"][1:]: +429 print("│ %s\x1b[90m│\x1b[0m %s " % (" "*(15+3), line)) +430 print("│") +431 +432 def print_help_format(self): +433 """ +434 Prints the help information for the 'format' used in remote 'ls' and 'dir' commands. +435 +436 This function displays the format of file attributes used in the smbclient-ng shell. It explains the meaning +437 of each character in the file attribute string, such as whether a file is read-only, hidden, or a directory. +438 """ +439 +440 print("File attributes format:\n") +441 print("\x1b[1mdachnrst\x1b[0m") +442 print("\x1b[90m│││││││└──>\x1b[0m Temporary") +443 print("\x1b[90m││││││└───>\x1b[0m System") +444 print("\x1b[90m│││││└────>\x1b[0m Read-Only") +445 print("\x1b[90m││││└─────>\x1b[0m Normal") +446 print("\x1b[90m│││└──────>\x1b[0m Hidden") +447 print("\x1b[90m││└───────>\x1b[0m Compressed") +448 print("\x1b[90m│└────────>\x1b[0m Archived") +449 print("\x1b[90m└─────────>\x1b[0m Directory") +
A class to handle command completion for the smbclient-ng shell.
+ +This class provides a command completion feature that suggests possible command names based on the current input. +It uses a dictionary to store commands and their descriptions, which helps in providing hints during the command line +interaction in the smbclient-ng shell.
+ +Attributes: + smbSession (SMBSession): An instance of SMBSession which maintains the current SMB session. + commands (dict): A dictionary containing command names as keys and their descriptions and subcommands as values.
+ +Methods: + __init__(self, smbSession): Initializes the CommandCompleter with an SMBSession.
+214 def __init__(self, smbSession, config): +215 # Objects +216 self.smbSession = smbSession +217 self.config = config +218 # Pre computing for some commands +219 self.commands["help"]["subcommands"] = ["format"] + list(self.commands.keys()) +220 self.commands["help"]["subcommands"].remove("help") +
222 def complete(self, text, state): +223 """ +224 Function to handle command completion in the LDAP console. +225 +226 This function completes the user"s input based on the available options for commands in the LDAP console. +227 +228 Args: +229 text (str): The current text input by the user. +230 state (int): The current state of completion. +231 +232 Returns: +233 str: The next completion suggestion based on the user"s input state. +234 """ +235 +236 if state == 0: +237 +238 # No text typed yet, need the list of commands available +239 if len(text) == 0: +240 self.matches = [s for s in self.commands.keys()] +241 +242 elif len(text) != 0: +243 # This is for the main command +244 if text.count(" ") == 0: +245 self.matches = [s for s in self.commands.keys() if s and s.startswith(text)] +246 +247 # This is for subcommands +248 elif text.count(" ") >= 1: +249 command, remainder = text.split(" ", 1) +250 if command in self.commands.keys(): +251 if command == "use": +252 # Choose SMB Share to connect to +253 self.matches = [ +254 command + " " + s.lower() +255 for s in self.smbSession.list_shares().keys() +256 if s.lower().startswith(remainder.lower()) +257 ] +258 +259 elif command in ["cd", "dir", "ls", "mkdir", "rmdir", "tree"]: +260 # Choose remote directory +261 path = "" +262 if '\\' in remainder.strip() or '/' in remainder.strip(): +263 path = remainder.strip().replace('/', ntpath.sep) +264 path = ntpath.sep.join(path.split(ntpath.sep)[:-1]) +265 +266 directory_contents = self.smbSession.list_contents(path=path).items() +267 +268 matching_entries = [] +269 for _, entry in directory_contents: +270 if entry.is_directory() and entry.get_longname() not in [".",".."]: +271 if len(path) != 0: +272 matching_entries.append(path + ntpath.sep + entry.get_longname() + ntpath.sep) +273 else: +274 matching_entries.append(entry.get_longname() + ntpath.sep) +275 +276 self.matches = [ +277 command + " " + s +278 for s in matching_entries +279 if s.lower().startswith(remainder.lower()) +280 ] +281 +282 elif command in ["get", "rm"]: +283 # Choose local files and directories +284 path = "" +285 if '\\' in remainder.strip() or '/' in remainder.strip(): +286 path = remainder.strip().replace('/', ntpath.sep) +287 path = ntpath.sep.join(path.split(ntpath.sep)[:-1]) +288 +289 directory_contents = self.smbSession.list_contents(path=path).items() +290 +291 matching_entries = [] +292 for _, entry in directory_contents: +293 if entry.get_longname() not in [".",".."]: +294 if len(path) != 0: +295 if entry.is_directory(): +296 matching_entries.append(path + ntpath.sep + entry.get_longname() + ntpath.sep) +297 else: +298 matching_entries.append(path + ntpath.sep + entry.get_longname()) +299 else: +300 if entry.is_directory(): +301 matching_entries.append(entry.get_longname() + ntpath.sep) +302 else: +303 matching_entries.append(entry.get_longname()) +304 +305 self.matches = [ +306 command + " " + s +307 for s in matching_entries +308 if s.lower().startswith(remainder.lower()) +309 ] +310 +311 elif command in ["lcd", "lls", "put", "lmkdir", "lrm", "lrmdir"]: +312 # Choose directory +313 path = "" +314 if os.path.sep in remainder.strip(): +315 path = path.split(os.path.sep)[:-1] +316 path = os.path.sep.join(path) +317 +318 # Current dir +319 if len(path.strip()) == 0: +320 path = "." +321 +322 directory_contents = os.listdir(path=path + os.path.sep) +323 matching_entries = [] +324 for entry in directory_contents: +325 if entry not in [".",".."]: +326 entry_path = path + os.path.sep + entry +327 if os.path.isdir(entry_path): +328 matching_entries.append(entry_path + os.path.sep) +329 else: +330 matching_entries.append(entry_path) +331 +332 self.matches = [ +333 command + " " + s +334 for s in matching_entries +335 if s.startswith(remainder) +336 ] +337 +338 else: +339 # Generic case for subcommands +340 self.matches = [ +341 command + " " + s +342 for s in self.commands[command]["subcommands"] +343 if s.startswith(remainder) +344 ] +345 else: +346 # Unknown subcommand, skipping autocomplete +347 pass +348 else: +349 self.matches = [] +350 else: +351 self.matches = self.commands.keys()[:] +352 +353 try: +354 return self.matches[state] + " " +355 except IndexError: +356 return None +
Function to handle command completion in the LDAP console.
+ +This function completes the user"s input based on the available options for commands in the LDAP console.
+ +Args: + text (str): The current text input by the user. + state (int): The current state of completion.
+ +Returns: + str: The next completion suggestion based on the user"s input state.
+358 def print_help(self, command=None): +359 """ +360 Prints help information for a specific command or all commands if no command is specified. +361 +362 This method displays the help information for the command passed as an argument. If no command is specified, +363 it prints the help information for all available commands. The help information includes the command syntax, +364 description, and any subcommands associated with it. This method is designed to provide users with the necessary +365 guidance on how to use the commands in the smbclient-ng shell. +366 +367 Args: +368 command (str, optional): The command to display help information for. If None, help for all commands is displayed. +369 +370 Returns: +371 None +372 """ +373 +374 if command is not None: +375 if command not in list(self.commands.keys())+["format"]: +376 command = None +377 +378 # Print help for a specific command +379 if command is not None: +380 if command == "format": +381 self.print_help_format() +382 else: +383 print("│") +384 if self.config.no_colors: +385 command_str = command + "─"* (15 - len(command)) +386 if len(self.commands[command]["description"]) == 0: +387 print("│ ■ %s┤ " % command_str) +388 elif len(self.commands[command]["description"]) == 1: +389 print("│ ■ %s┤ %s " % (command_str, self.commands[command]["description"][0])) +390 else: +391 print("│ ■ %s┤ %s " % (command_str, self.commands[command]["description"][0])) +392 for line in self.commands[command]["description"][1:]: +393 print("│ %s│ %s " % (" "*(15+3), line)) +394 else: +395 command_str = command + " \x1b[90m" + "─"* (15 - len(command)) + "\x1b[0m" +396 if len(self.commands[command]["description"]) == 0: +397 print("│ ■ %s\x1b[90m┤\x1b[0m " % command_str) +398 elif len(self.commands[command]["description"]) == 1: +399 print("│ ■ %s\x1b[90m┤\x1b[0m %s " % (command_str, self.commands[command]["description"][0])) +400 else: +401 print("│ ■ %s\x1b[90m┤\x1b[0m %s " % (command_str, self.commands[command]["description"][0])) +402 for line in self.commands[command]["description"][1:]: +403 print("│ %s\x1b[90m│\x1b[0m %s " % (" "*(15+2), line)) +404 print("│") +405 # Generic help +406 else: +407 print("│") +408 commands = sorted(self.commands.keys()) +409 for command in commands: +410 if self.config.no_colors: +411 command_str = command + "─"* (15 - len(command)) +412 if len(self.commands[command]["description"]) == 0: +413 print("│ ■ %s┤ " % command_str) +414 elif len(self.commands[command]["description"]) == 1: +415 print("│ ■ %s┤ %s " % (command_str, self.commands[command]["description"][0])) +416 else: +417 print("│ ■ %s┤ %s " % (command_str, self.commands[command]["description"][0])) +418 for line in self.commands[command]["description"][1:]: +419 print("│ %s│ %s " % (" "*(15+2), line)) +420 else: +421 command_str = command + " \x1b[90m" + "─"* (15 - len(command)) + "\x1b[0m" +422 if len(self.commands[command]["description"]) == 0: +423 print("│ ■ %s\x1b[90m┤\x1b[0m " % command_str) +424 elif len(self.commands[command]["description"]) == 1: +425 print("│ ■ %s\x1b[90m┤\x1b[0m %s " % (command_str, self.commands[command]["description"][0])) +426 else: +427 print("│ ■ %s\x1b[90m┤\x1b[0m %s " % (command_str, self.commands[command]["description"][0])) +428 for line in self.commands[command]["description"][1:]: +429 print("│ %s\x1b[90m│\x1b[0m %s " % (" "*(15+3), line)) +430 print("│") +
Prints help information for a specific command or all commands if no command is specified.
+ +This method displays the help information for the command passed as an argument. If no command is specified, +it prints the help information for all available commands. The help information includes the command syntax, +description, and any subcommands associated with it. This method is designed to provide users with the necessary +guidance on how to use the commands in the smbclient-ng shell.
+ +Args: + command (str, optional): The command to display help information for. If None, help for all commands is displayed.
+ +Returns: + None
+432 def print_help_format(self): +433 """ +434 Prints the help information for the 'format' used in remote 'ls' and 'dir' commands. +435 +436 This function displays the format of file attributes used in the smbclient-ng shell. It explains the meaning +437 of each character in the file attribute string, such as whether a file is read-only, hidden, or a directory. +438 """ +439 +440 print("File attributes format:\n") +441 print("\x1b[1mdachnrst\x1b[0m") +442 print("\x1b[90m│││││││└──>\x1b[0m Temporary") +443 print("\x1b[90m││││││└───>\x1b[0m System") +444 print("\x1b[90m│││││└────>\x1b[0m Read-Only") +445 print("\x1b[90m││││└─────>\x1b[0m Normal") +446 print("\x1b[90m│││└──────>\x1b[0m Hidden") +447 print("\x1b[90m││└───────>\x1b[0m Compressed") +448 print("\x1b[90m│└────────>\x1b[0m Archived") +449 print("\x1b[90m└─────────>\x1b[0m Directory") +
Prints the help information for the 'format' used in remote 'ls' and 'dir' commands.
+ +This function displays the format of file attributes used in the smbclient-ng shell. It explains the meaning +of each character in the file attribute string, such as whether a file is read-only, hidden, or a directory.
+1#!/usr/bin/env python3 + 2# -*- coding: utf-8 -*- + 3# File name : Config.py + 4# Author : Podalirius (@podalirius_) + 5# Date created : 31 may 2024 + 6 + 7import platform + 8 + 9 +10class Config(object): +11 """ +12 Configuration handler for smbclientng. +13 +14 This class manages the configuration settings for the smbclientng tool, including debug and color output settings. +15 It provides a structured way to access and modify these settings throughout the application. +16 +17 Attributes: +18 _debug (bool): Flag to enable or disable debug mode. +19 _no_colors (bool): Flag to enable or disable colored output, depending on the platform. +20 +21 Methods: +22 debug: Property to get or set the debug mode. +23 no_colors: Property to get or set the colored output preference. +24 """ +25 +26 def __init__(self, debug=False, no_colors=None): +27 self._debug = debug +28 +29 if no_colors is not None: +30 self._no_colors = no_colors +31 else: +32 if platform.system() == "Windows": +33 self._no_colors = False +34 else: +35 self._no_colors = True +36 +37 @property +38 def debug(self): +39 return self._debug +40 +41 @debug.setter +42 def debug(self, value): +43 if isinstance(value, bool): +44 self._debug = value +45 else: +46 raise ValueError("Debug must be a boolean value") +47 +48 @property +49 def no_colors(self): +50 return self._no_colors +51 +52 @no_colors.setter +53 def no_colors(self, value): +54 if isinstance(value, bool): +55 self._no_colors = value +56 else: +57 raise ValueError("Colored output must be a boolean value") +
11class Config(object): +12 """ +13 Configuration handler for smbclientng. +14 +15 This class manages the configuration settings for the smbclientng tool, including debug and color output settings. +16 It provides a structured way to access and modify these settings throughout the application. +17 +18 Attributes: +19 _debug (bool): Flag to enable or disable debug mode. +20 _no_colors (bool): Flag to enable or disable colored output, depending on the platform. +21 +22 Methods: +23 debug: Property to get or set the debug mode. +24 no_colors: Property to get or set the colored output preference. +25 """ +26 +27 def __init__(self, debug=False, no_colors=None): +28 self._debug = debug +29 +30 if no_colors is not None: +31 self._no_colors = no_colors +32 else: +33 if platform.system() == "Windows": +34 self._no_colors = False +35 else: +36 self._no_colors = True +37 +38 @property +39 def debug(self): +40 return self._debug +41 +42 @debug.setter +43 def debug(self, value): +44 if isinstance(value, bool): +45 self._debug = value +46 else: +47 raise ValueError("Debug must be a boolean value") +48 +49 @property +50 def no_colors(self): +51 return self._no_colors +52 +53 @no_colors.setter +54 def no_colors(self, value): +55 if isinstance(value, bool): +56 self._no_colors = value +57 else: +58 raise ValueError("Colored output must be a boolean value") +
Configuration handler for smbclientng.
+ +This class manages the configuration settings for the smbclientng tool, including debug and color output settings. +It provides a structured way to access and modify these settings throughout the application.
+ +Attributes: + _debug (bool): Flag to enable or disable debug mode. + _no_colors (bool): Flag to enable or disable colored output, depending on the platform.
+ +Methods: + debug: Property to get or set the debug mode. + no_colors: Property to get or set the colored output preference.
+1#!/usr/bin/env python3 + 2# -*- coding: utf-8 -*- + 3# File name : InteractiveShell.py + 4# Author : Podalirius (@podalirius_) + 5# Date created : 23 may 2024 + 6 + 7 + 8import datetime + 9import impacket + 10from importlib import import_module + 11import ntpath + 12import os + 13import readline + 14import shutil + 15import sys + 16import traceback + 17from rich.console import Console + 18from rich.table import Table + 19from smbclientng.core.CommandCompleter import CommandCompleter + 20from smbclientng.core.utils import b_filesize, unix_permissions, windows_ls_entry, local_tree + 21 + 22 + 23## Decorators + 24 + 25def command_arguments_required(func): + 26 def wrapper(*args, **kwargs): + 27 self, arguments,command = args[0], args[1], args[2] + 28 if len(arguments) != 0: + 29 return func(*args, **kwargs) + 30 else: + 31 self.commandCompleterObject.print_help(command=command) + 32 return None + 33 return wrapper + 34 + 35def active_smb_connection_needed(func): + 36 def wrapper(*args, **kwargs): + 37 self, arguments,command = args[0], args[1], args[2] + 38 # + 39 self.smbSession.ping_smb_session() + 40 if self.smbSession.connected: + 41 return func(*args, **kwargs) + 42 else: + 43 print("[!] SMB Session is disconnected.") + 44 return None + 45 return wrapper + 46 + 47def smb_share_is_set(func): + 48 def wrapper(*args, **kwargs): + 49 self, arguments,command = args[0], args[1], args[2] + 50 if self.smbSession.smb_share is not None: + 51 return func(*args, **kwargs) + 52 else: + 53 print("[!] You must open a share first, try the 'use <share>' command.") + 54 return None + 55 return wrapper + 56 + 57 + 58class InteractiveShell(object): + 59 """ + 60 Class InteractiveShell is designed to manage the interactive command line interface for smbclient-ng. + 61 + 62 This class handles user input, executes commands, and manages the state of the SMB session. It provides + 63 a command line interface for users to interact with SMB shares, execute commands like directory listing, + 64 file transfer, and more. + 65 + 66 Attributes: + 67 smbSession (SMBConnection): The active SMB connection session. + 68 debug (bool): Flag to enable or disable debug mode. + 69 smb_share (str): The current SMB share in use. + 70 smb_path (str): The current path within the SMB share. + 71 commandCompleterObject (CommandCompleter): Object to handle command completion and help generation. + 72 + 73 Methods: + 74 __init__(self, smbSession, debug=False): Initializes the InteractiveShell with the given SMB session and debug mode. + 75 run(self): Starts the command line interface loop, processing user input until exit. + 76 """ + 77 + 78 def __init__(self, smbSession, config): + 79 # Objects + 80 self.smbSession = smbSession + 81 self.config = config + 82 self.commandCompleterObject = CommandCompleter(smbSession=self.smbSession, config=self.config) + 83 readline.set_completer(self.commandCompleterObject.complete) + 84 readline.parse_and_bind("tab: complete") + 85 readline.set_completer_delims("\n") + 86 # Additional modules + 87 self.modules = {} + 88 self.__load_modules() + 89 + 90 def run(self): + 91 running = True + 92 while running: + 93 try: + 94 user_input = input(self.__prompt()).strip().split(" ") + 95 command, arguments = user_input[0].lower(), user_input[1:] + 96 + 97 # Exit the command line + 98 if command == "exit": + 99 running = False +100 +101 elif command in self.commandCompleterObject.commands.keys(): +102 self.process_command( +103 command=command, +104 arguments=arguments +105 ) +106 +107 elif command.strip() == "": +108 pass +109 +110 # Fallback to unknown command +111 else: +112 print("Unknown command. Type \"help\" for help.") +113 +114 except KeyboardInterrupt as e: +115 print() +116 +117 except EOFError as e: +118 print() +119 running = False +120 +121 except Exception as e: +122 if self.config.debug: +123 traceback.print_exc() +124 print("[!] Error: %s" % str(e)) +125 +126 def process_command(self, command, arguments=[]): +127 # Skip +128 if command.strip() == "": +129 pass +130 +131 # Display help +132 elif command == "help": +133 self.command_help(arguments, command) +134 +135 # Closes the current SMB session +136 elif command == "close": +137 self.command_close(arguments, command) +138 +139 # Change directory in the current share +140 elif command == "cd": +141 self.command_cd(arguments, command) +142 +143 # Get a file +144 elif command == "get": +145 self.command_get(arguments, command) +146 +147 # SMB server info +148 elif command == "info": +149 self.command_info(arguments, command) +150 +151 # List directory contents in a share +152 elif command in ["ls", "dir"]: +153 self.command_ls(arguments, command) +154 +155 # Creates a new remote directory +156 elif command == "mkdir": +157 self.command_mkdir(arguments, command) +158 +159 # Put a file +160 elif command == "put": +161 self.command_put(arguments, command) +162 +163 # Changes the current local directory +164 elif command == "lcd": +165 self.command_lcd(arguments, command) +166 +167 # Lists the contents of the current local directory +168 elif command == "lls": +169 self.command_lls(arguments, command) +170 +171 # Creates a new local directory +172 elif command == "lmkdir": +173 self.command_lmkdir(arguments, command) +174 +175 # Shows the current local directory +176 elif command == "lpwd": +177 self.command_lpwd(arguments, command) +178 +179 # Removes a local file +180 elif command == "lrm": +181 self.command_lrm(arguments, command) +182 +183 # Removes a local directory +184 elif command == "lrmdir": +185 self.command_lrmdir(arguments, command) +186 +187 # Shows the current local directory +188 elif command == "ltree": +189 self.command_ltree(arguments, command) +190 +191 # Modules +192 elif command == "module": +193 self.command_module(arguments, command) +194 +195 # Reconnects the current SMB session +196 elif command in ["connect", "reconnect"]: +197 self.command_reconnect(arguments, command) +198 +199 # Reset the TTY output +200 elif command == "reset": +201 self.command_reset(arguments, command) +202 +203 # Removes a remote file +204 elif command == "rm": +205 self.command_rm(arguments, command) +206 +207 # Removes a remote directory +208 elif command == "rmdir": +209 self.command_rmdir(arguments, command) +210 +211 # List shares +212 elif command == "shares": +213 self.command_shares(arguments, command) +214 +215 # Displays a tree view of the CWD +216 elif command == "tree": +217 self.command_tree(arguments, command) +218 +219 # Use a share +220 elif command == "use": +221 self.command_use(arguments, command) +222 +223 # Commands ================================================================ +224 +225 @command_arguments_required +226 @active_smb_connection_needed +227 @smb_share_is_set +228 def command_cd(self, arguments, command): +229 # Command arguments required : Yes +230 # Active SMB connection needed : Yes +231 # SMB share needed : Yes +232 +233 path = ' '.join(arguments) +234 try: +235 self.smbSession.set_cwd(path=path) +236 except impacket.smbconnection.SessionError as e: +237 print("[!] SMB Error: %s" % e) +238 +239 def command_close(self, arguments, command): +240 # Command arguments required : No +241 # Active SMB connection needed : No +242 # SMB share needed : No +243 +244 self.smbSession.ping_smb_session() +245 if self.smbSession.connected: +246 self.smbSession.close_smb_session() +247 +248 @command_arguments_required +249 @active_smb_connection_needed +250 @smb_share_is_set +251 def command_get(self, arguments, command): +252 # Command arguments required : Yes +253 # Active SMB connection needed : Yes +254 # SMB share needed : Yes +255 +256 # Get files recursively +257 if arguments[0] == "-r": +258 path = ' '.join(arguments[1:]).replace('/', ntpath.sep) +259 try: +260 self.smbSession.get_file_recursively(path=path) +261 except impacket.smbconnection.SessionError as e: +262 print("[!] SMB Error: %s" % e) +263 # Get a single file +264 else: +265 path = ' '.join(arguments).replace('/', ntpath.sep) +266 try: +267 self.smbSession.get_file(path=path) +268 except impacket.smbconnection.SessionError as e: +269 print("[!] SMB Error: %s" % e) +270 +271 def command_help(self, arguments, command): +272 # Command arguments required : No +273 # Active SMB connection needed : No +274 # SMB share needed : No +275 +276 if len(arguments) != 0: +277 self.commandCompleterObject.print_help(command=arguments[0]) +278 else: +279 self.commandCompleterObject.print_help(command=None) +280 +281 @active_smb_connection_needed +282 def command_info(self, arguments, command): +283 # Command arguments required : No +284 # Active SMB connection needed : Yes +285 # SMB share needed : No +286 +287 print_server_info = False +288 print_share_info = False +289 if len(arguments) != 0: +290 print_server_info = (arguments[0].lower() == "server") +291 print_share_info = (arguments[0].lower() == "share") +292 else: +293 print_server_info = True +294 print_share_info = True +295 +296 try: +297 self.smbSession.info( +298 share=print_share_info, +299 server=print_server_info +300 ) +301 except impacket.smbconnection.SessionError as e: +302 print("[!] SMB Error: %s" % e) +303 +304 @command_arguments_required +305 def command_lcd(self, arguments, command): +306 # Command arguments required : Yes +307 # Active SMB connection needed : No +308 # SMB share needed : No +309 +310 path = ' '.join(arguments) +311 if os.path.exists(path=path): +312 if os.path.isdir(s=path): +313 os.chdir(path=path) +314 else: +315 print("[!] Path '%s' is not a directory." % path) +316 else: +317 print("[!] Directory '%s' does not exists." % path) +318 +319 def command_lls(self, arguments, command): +320 # Command arguments required : No +321 # Active SMB connection needed : No +322 # SMB share needed : No +323 +324 if len(arguments) == 0: +325 path = '.' +326 else: +327 path = ' '.join(arguments) +328 +329 # lls <directory> +330 if os.path.isdir(path): +331 directory_contents = os.listdir(path=path) +332 for entryname in sorted(directory_contents): +333 path_to_file = path + os.path.sep + entryname +334 rights_str = unix_permissions(path_to_file) +335 size_str = b_filesize(os.path.getsize(filename=path_to_file)) +336 date_str = datetime.datetime.fromtimestamp(os.path.getmtime(filename=path_to_file)).strftime("%Y-%m-%d %H:%M") +337 +338 if os.path.isdir(s=entryname): +339 if self.config.no_colors: +340 print("%s %10s %s %s%s" % (rights_str, size_str, date_str, entryname, os.path.sep)) +341 else: +342 print("%s %10s %s \x1b[1;96m%s\x1b[0m%s" % (rights_str, size_str, date_str, entryname, os.path.sep)) +343 else: +344 if self.config.no_colors: +345 print("%s %10s %s %s" % (rights_str, size_str, date_str, entryname)) +346 else: +347 print("%s %10s %s \x1b[1m%s\x1b[0m" % (rights_str, size_str, date_str, entryname)) +348 # lls <file> +349 elif os.path.isfile(path): +350 rights_str = unix_permissions(path) +351 size_str = b_filesize(os.path.getsize(filename=path)) +352 date_str = datetime.datetime.fromtimestamp(os.path.getmtime(filename=path)).strftime("%Y-%m-%d %H:%M") +353 if self.config.no_colors: +354 print("%s %10s %s %s" % (rights_str, size_str, date_str, os.path.basename(path))) +355 else: +356 print("%s %10s %s \x1b[1m%s\x1b[0m" % (rights_str, size_str, date_str, os.path.basename(path))) +357 else: +358 print("[!] No such file or directory.") +359 +360 @command_arguments_required +361 def command_lmkdir(self, arguments, command): +362 # Command arguments required : Yes +363 # Active SMB connection needed : No +364 # SMB share needed : No +365 +366 path = ' '.join(arguments) +367 +368 # Split each dir +369 if os.path.sep in path: +370 path = path.strip(os.path.sep).split(os.path.sep) +371 else: +372 path = [path] +373 +374 # Create each dir in the path +375 for depth in range(1, len(path)+1): +376 tmp_path = os.path.sep.join(path[:depth]) +377 if not os.path.exists(tmp_path): +378 os.mkdir(path=tmp_path) +379 +380 def command_lpwd(self, arguments, command): +381 # Command arguments required : No +382 # Active SMB connection needed : No +383 # SMB share needed : No +384 +385 print(os.getcwd()) +386 +387 @command_arguments_required +388 def command_lrm(self, arguments, command): +389 # Command arguments required : Yes +390 # Active SMB connection needed : No +391 # SMB share needed : No +392 +393 path = ' '.join(arguments) +394 if os.path.exists(path): +395 if not os.path.isdir(s=path): +396 try: +397 os.remove(path=path) +398 except Exception as e: +399 print("[!] Error removing file '%s' : %s" % path) +400 else: +401 print("[!] Cannot delete '%s'. It is a directory, use 'lrmdir <directory>' instead." % path) +402 else: +403 print("[!] Path '%s' does not exist." % path) +404 +405 @command_arguments_required +406 def command_lrmdir(self, arguments, command): +407 # Command arguments required : Yes +408 # Active SMB connection needed : No +409 # SMB share needed : No +410 +411 path = ' '.join(arguments) +412 if os.path.exists(path): +413 if os.path.isdir(s=path): +414 try: +415 shutil.rmtree(path=path) +416 except Exception as e: +417 print("[!] Error removing directory '%s' : %s" % path) +418 else: +419 print("[!] Cannot delete '%s'. It is a file, use 'lrm <file>' instead." % path) +420 else: +421 print("[!] Path '%s' does not exist." % path) +422 +423 def command_ltree(self, arguments, command): +424 # Command arguments required : No +425 # Active SMB connection needed : No +426 # SMB share needed : No +427 +428 if len(arguments) == 0: +429 local_tree(path='.', config=self.config) +430 else: +431 local_tree(path=' '.join(arguments), config=self.config) +432 +433 @active_smb_connection_needed +434 @smb_share_is_set +435 def command_ls(self, arguments, command): +436 # Command arguments required : No +437 # Active SMB connection needed : Yes +438 # SMB share needed : Yes +439 +440 # Read the files +441 directory_contents = self.smbSession.list_contents(path=' '.join(arguments)) +442 +443 for longname in sorted(directory_contents.keys(), key=lambda x:x.lower()): +444 windows_ls_entry(directory_contents[longname], self.config) +445 +446 @command_arguments_required +447 @active_smb_connection_needed +448 @smb_share_is_set +449 def command_mkdir(self, arguments, command): +450 # Command arguments required : Yes +451 # Active SMB connection needed : Yes +452 # SMB share needed : Yes +453 +454 path = ' '.join(arguments) +455 self.smbSession.mkdir(path=path) +456 +457 @command_arguments_required +458 @active_smb_connection_needed +459 @smb_share_is_set +460 def command_module(self, arguments, command): +461 module_name = arguments[0] +462 +463 if module_name in self.modules.keys(): +464 module = self.modules[module_name](self.smbSession, self.config) +465 module.run(' '.join(arguments[1:])) +466 else: +467 print("[!] Module '%s' does not exist." % module_name) +468 +469 @command_arguments_required +470 @active_smb_connection_needed +471 @smb_share_is_set +472 def command_put(self, arguments, command): +473 # Command arguments required : Yes +474 # Active SMB connection needed : Yes +475 # SMB share needed : Yes +476 +477 # Put files recursively +478 if arguments[0] == "-r": +479 localpath = ' '.join(arguments[1:]) +480 try: +481 self.smbSession.put_file_recursively(localpath=localpath) +482 except impacket.smbconnection.SessionError as e: +483 print("[!] SMB Error: %s" % e) +484 +485 # Put a single file +486 else: +487 localpath = ' '.join(arguments) +488 try: +489 self.smbSession.put_file(localpath=localpath) +490 except impacket.smbconnection.SessionError as e: +491 print("[!] SMB Error: %s" % e) +492 +493 def command_reconnect(self, arguments, command): +494 # Command arguments required : No +495 # Active SMB connection needed : No +496 # SMB share needed : No +497 +498 self.smbSession.ping_smb_session() +499 if self.smbSession.connected: +500 self.smbSession.close_smb_session() +501 self.smbSession.init_smb_session() +502 else: +503 self.smbSession.init_smb_session() +504 +505 def command_reset(self, arguments, command): +506 # Command arguments required : No +507 # Active SMB connection needed : No +508 # SMB share needed : No +509 sys.stdout.write('\x1b[?25h') # Sets the cursor to on +510 sys.stdout.write('\x1b[v') +511 sys.stdout.write('\x1b[o') # Reset +512 sys.stdout.flush() +513 +514 @command_arguments_required +515 @active_smb_connection_needed +516 @smb_share_is_set +517 def command_rm(self, arguments, command): +518 # Command arguments required : Yes +519 # Active SMB connection needed : Yes +520 # SMB share needed : Yes +521 +522 path = ' '.join(arguments) +523 if self.smbSession.path_exists(path): +524 if self.smbSession.path_isfile(path): +525 try: +526 self.smbSession.rm(path=path) +527 except Exception as e: +528 print("[!] Error removing file '%s' : %s" % path) +529 else: +530 print("[!] Cannot delete '%s': This is a directory, use 'rmdir <directory>' instead." % path) +531 else: +532 print("[!] Remote file '%s' does not exist." % path) +533 +534 @command_arguments_required +535 @active_smb_connection_needed +536 @smb_share_is_set +537 def command_rmdir(self, arguments, command): +538 # Command arguments required : Yes +539 # Active SMB connection needed : Yes +540 # SMB share needed : Yes +541 +542 path = ' '.join(arguments) +543 if self.smbSession.path_exists(path): +544 if self.smbSession.path_isdir(path): +545 try: +546 self.smbSession.rmdir(path=path) +547 except Exception as e: +548 print("[!] Error removing directory '%s' : %s" % path) +549 else: +550 print("[!] Cannot delete '%s': This is a file, use 'rm <file>' instead." % path) +551 else: +552 print("[!] Remote directory '%s' does not exist." % path) +553 +554 @active_smb_connection_needed +555 def command_shares(self, arguments, command): +556 # Command arguments required : No +557 # Active SMB connection needed : Yes +558 # SMB share needed : No +559 +560 shares = self.smbSession.list_shares() +561 if len(shares.keys()) != 0: +562 table = Table(title=None) +563 table.add_column("Share") +564 table.add_column("Hidden") +565 table.add_column("Type") +566 table.add_column("Description", justify="left") +567 +568 for sharename in sorted(shares.keys()): +569 is_hidden = bool(sharename.endswith('$')) +570 types = ', '.join([s.replace("STYPE_","") for s in shares[sharename]["type"]]) +571 if is_hidden: +572 table.add_row(sharename, str(is_hidden), types, shares[sharename]["comment"]) +573 else: +574 table.add_row(sharename, str(is_hidden), types, shares[sharename]["comment"]) +575 +576 Console().print(table) +577 else: +578 print("[!] No share served on '%s'" % self.smbSession.address) +579 +580 @active_smb_connection_needed +581 @smb_share_is_set +582 def command_tree(self, arguments, command): +583 # Command arguments required : No +584 # Active SMB connection needed : Yes +585 # SMB share needed : Yes +586 +587 if len(arguments) == 0: +588 self.smbSession.tree(path='.') +589 else: +590 self.smbSession.tree(path=' '.join(arguments)) +591 +592 @command_arguments_required +593 @active_smb_connection_needed +594 def command_use(self, arguments, command): +595 # Command arguments required : Yes +596 # Active SMB connection needed : Yes +597 # SMB share needed : No +598 +599 sharename = ' '.join(arguments) +600 # Reload the list of shares +601 shares = self.smbSession.list_shares() +602 shares = [s.lower() for s in shares.keys()] +603 if sharename.lower() in shares: +604 self.smbSession.set_share(sharename) +605 else: +606 print("[!] No share named '%s' on '%s'" % (sharename, self.smbSession.address)) +607 +608 # Private functions ======================================================= +609 +610 def __load_modules(self): +611 +612 +613 self.modules.clear() +614 +615 modules_dir = os.path.normpath(os.path.dirname(__file__) + os.path.sep + ".." + os.path.sep + "modules") +616 if self.config.debug: +617 print("[debug] Loading modules from %s ..." % modules_dir) +618 sys.path.extend([modules_dir]) +619 +620 for file in os.listdir(modules_dir): +621 filepath = os.path.normpath(modules_dir + os.path.sep + file) +622 if file.endswith('.py'): +623 if os.path.isfile(filepath) and file not in ["__init__.py"]: +624 try: +625 module_file = import_module('smbclientng.modules.%s' % (file[:-3])) +626 module = module_file.__getattribute__(file[:-3]) +627 self.modules[module.name.lower()] = module +628 except AttributeError as e: +629 pass +630 +631 if self.config.debug: +632 if len(self.modules.keys()) == 0: +633 print("[debug] Loaded 0 modules.") +634 elif len(self.modules.keys()) == 1: +635 print("[debug] Loaded 1 module:") +636 else: +637 print("[debug] Loaded %d modules:" % len(self.modules.keys())) +638 for modulename in sorted(self.modules.keys()): +639 print("[debug] %s : \"%s\"" % (module.name, module.description)) +640 +641 if self.commandCompleterObject is not None: +642 self.commandCompleterObject.commands["module"]["subcommands"] = list(self.modules.keys()) +643 +644 def __prompt(self): +645 """ +646 Prints the command prompt for the interactive shell. +647 +648 This method constructs and returns the command prompt string based on the current state of the SMB session. +649 The prompt indicates the connection status with a visual symbol and displays the current working directory +650 or the SMB share path. The prompt appearance changes based on whether colors are enabled in the configuration. +651 +652 Returns: +653 str: The formatted command prompt string. +654 """ +655 +656 self.smbSession.ping_smb_session() +657 if self.smbSession.connected: +658 if self.config.no_colors: +659 connected_dot = "[v]" +660 else: +661 connected_dot = "\x1b[1;92m⏺\x1b[0m" +662 else: +663 if self.config.no_colors: +664 connected_dot = "[x]" +665 else: +666 connected_dot = "\x1b[1;91m⏺\x1b[0m" +667 +668 if self.smbSession.smb_share is None: +669 if self.config.no_colors: +670 str_prompt = "%s[\\\\%s\\]> " % (connected_dot, self.smbSession.address) +671 else: +672 str_prompt = "%s[\x1b[1;94m\\\\%s\\\x1b[0m]> " % (connected_dot, self.smbSession.address) +673 else: +674 str_path = "\\\\%s\\%s\\%s" % (self.smbSession.address, self.smbSession.smb_share, self.smbSession.smb_cwd.lstrip(ntpath.sep)) +675 if self.config.no_colors: +676 str_prompt = "%s[%s]> " % (connected_dot, str_path) +677 else: +678 str_prompt = "%s[\x1b[1;94m%s\x1b[0m]> " % (connected_dot, str_path) +679 +680 return str_prompt +
36def active_smb_connection_needed(func): +37 def wrapper(*args, **kwargs): +38 self, arguments,command = args[0], args[1], args[2] +39 # +40 self.smbSession.ping_smb_session() +41 if self.smbSession.connected: +42 return func(*args, **kwargs) +43 else: +44 print("[!] SMB Session is disconnected.") +45 return None +46 return wrapper +
59class InteractiveShell(object): + 60 """ + 61 Class InteractiveShell is designed to manage the interactive command line interface for smbclient-ng. + 62 + 63 This class handles user input, executes commands, and manages the state of the SMB session. It provides + 64 a command line interface for users to interact with SMB shares, execute commands like directory listing, + 65 file transfer, and more. + 66 + 67 Attributes: + 68 smbSession (SMBConnection): The active SMB connection session. + 69 debug (bool): Flag to enable or disable debug mode. + 70 smb_share (str): The current SMB share in use. + 71 smb_path (str): The current path within the SMB share. + 72 commandCompleterObject (CommandCompleter): Object to handle command completion and help generation. + 73 + 74 Methods: + 75 __init__(self, smbSession, debug=False): Initializes the InteractiveShell with the given SMB session and debug mode. + 76 run(self): Starts the command line interface loop, processing user input until exit. + 77 """ + 78 + 79 def __init__(self, smbSession, config): + 80 # Objects + 81 self.smbSession = smbSession + 82 self.config = config + 83 self.commandCompleterObject = CommandCompleter(smbSession=self.smbSession, config=self.config) + 84 readline.set_completer(self.commandCompleterObject.complete) + 85 readline.parse_and_bind("tab: complete") + 86 readline.set_completer_delims("\n") + 87 # Additional modules + 88 self.modules = {} + 89 self.__load_modules() + 90 + 91 def run(self): + 92 running = True + 93 while running: + 94 try: + 95 user_input = input(self.__prompt()).strip().split(" ") + 96 command, arguments = user_input[0].lower(), user_input[1:] + 97 + 98 # Exit the command line + 99 if command == "exit": +100 running = False +101 +102 elif command in self.commandCompleterObject.commands.keys(): +103 self.process_command( +104 command=command, +105 arguments=arguments +106 ) +107 +108 elif command.strip() == "": +109 pass +110 +111 # Fallback to unknown command +112 else: +113 print("Unknown command. Type \"help\" for help.") +114 +115 except KeyboardInterrupt as e: +116 print() +117 +118 except EOFError as e: +119 print() +120 running = False +121 +122 except Exception as e: +123 if self.config.debug: +124 traceback.print_exc() +125 print("[!] Error: %s" % str(e)) +126 +127 def process_command(self, command, arguments=[]): +128 # Skip +129 if command.strip() == "": +130 pass +131 +132 # Display help +133 elif command == "help": +134 self.command_help(arguments, command) +135 +136 # Closes the current SMB session +137 elif command == "close": +138 self.command_close(arguments, command) +139 +140 # Change directory in the current share +141 elif command == "cd": +142 self.command_cd(arguments, command) +143 +144 # Get a file +145 elif command == "get": +146 self.command_get(arguments, command) +147 +148 # SMB server info +149 elif command == "info": +150 self.command_info(arguments, command) +151 +152 # List directory contents in a share +153 elif command in ["ls", "dir"]: +154 self.command_ls(arguments, command) +155 +156 # Creates a new remote directory +157 elif command == "mkdir": +158 self.command_mkdir(arguments, command) +159 +160 # Put a file +161 elif command == "put": +162 self.command_put(arguments, command) +163 +164 # Changes the current local directory +165 elif command == "lcd": +166 self.command_lcd(arguments, command) +167 +168 # Lists the contents of the current local directory +169 elif command == "lls": +170 self.command_lls(arguments, command) +171 +172 # Creates a new local directory +173 elif command == "lmkdir": +174 self.command_lmkdir(arguments, command) +175 +176 # Shows the current local directory +177 elif command == "lpwd": +178 self.command_lpwd(arguments, command) +179 +180 # Removes a local file +181 elif command == "lrm": +182 self.command_lrm(arguments, command) +183 +184 # Removes a local directory +185 elif command == "lrmdir": +186 self.command_lrmdir(arguments, command) +187 +188 # Shows the current local directory +189 elif command == "ltree": +190 self.command_ltree(arguments, command) +191 +192 # Modules +193 elif command == "module": +194 self.command_module(arguments, command) +195 +196 # Reconnects the current SMB session +197 elif command in ["connect", "reconnect"]: +198 self.command_reconnect(arguments, command) +199 +200 # Reset the TTY output +201 elif command == "reset": +202 self.command_reset(arguments, command) +203 +204 # Removes a remote file +205 elif command == "rm": +206 self.command_rm(arguments, command) +207 +208 # Removes a remote directory +209 elif command == "rmdir": +210 self.command_rmdir(arguments, command) +211 +212 # List shares +213 elif command == "shares": +214 self.command_shares(arguments, command) +215 +216 # Displays a tree view of the CWD +217 elif command == "tree": +218 self.command_tree(arguments, command) +219 +220 # Use a share +221 elif command == "use": +222 self.command_use(arguments, command) +223 +224 # Commands ================================================================ +225 +226 @command_arguments_required +227 @active_smb_connection_needed +228 @smb_share_is_set +229 def command_cd(self, arguments, command): +230 # Command arguments required : Yes +231 # Active SMB connection needed : Yes +232 # SMB share needed : Yes +233 +234 path = ' '.join(arguments) +235 try: +236 self.smbSession.set_cwd(path=path) +237 except impacket.smbconnection.SessionError as e: +238 print("[!] SMB Error: %s" % e) +239 +240 def command_close(self, arguments, command): +241 # Command arguments required : No +242 # Active SMB connection needed : No +243 # SMB share needed : No +244 +245 self.smbSession.ping_smb_session() +246 if self.smbSession.connected: +247 self.smbSession.close_smb_session() +248 +249 @command_arguments_required +250 @active_smb_connection_needed +251 @smb_share_is_set +252 def command_get(self, arguments, command): +253 # Command arguments required : Yes +254 # Active SMB connection needed : Yes +255 # SMB share needed : Yes +256 +257 # Get files recursively +258 if arguments[0] == "-r": +259 path = ' '.join(arguments[1:]).replace('/', ntpath.sep) +260 try: +261 self.smbSession.get_file_recursively(path=path) +262 except impacket.smbconnection.SessionError as e: +263 print("[!] SMB Error: %s" % e) +264 # Get a single file +265 else: +266 path = ' '.join(arguments).replace('/', ntpath.sep) +267 try: +268 self.smbSession.get_file(path=path) +269 except impacket.smbconnection.SessionError as e: +270 print("[!] SMB Error: %s" % e) +271 +272 def command_help(self, arguments, command): +273 # Command arguments required : No +274 # Active SMB connection needed : No +275 # SMB share needed : No +276 +277 if len(arguments) != 0: +278 self.commandCompleterObject.print_help(command=arguments[0]) +279 else: +280 self.commandCompleterObject.print_help(command=None) +281 +282 @active_smb_connection_needed +283 def command_info(self, arguments, command): +284 # Command arguments required : No +285 # Active SMB connection needed : Yes +286 # SMB share needed : No +287 +288 print_server_info = False +289 print_share_info = False +290 if len(arguments) != 0: +291 print_server_info = (arguments[0].lower() == "server") +292 print_share_info = (arguments[0].lower() == "share") +293 else: +294 print_server_info = True +295 print_share_info = True +296 +297 try: +298 self.smbSession.info( +299 share=print_share_info, +300 server=print_server_info +301 ) +302 except impacket.smbconnection.SessionError as e: +303 print("[!] SMB Error: %s" % e) +304 +305 @command_arguments_required +306 def command_lcd(self, arguments, command): +307 # Command arguments required : Yes +308 # Active SMB connection needed : No +309 # SMB share needed : No +310 +311 path = ' '.join(arguments) +312 if os.path.exists(path=path): +313 if os.path.isdir(s=path): +314 os.chdir(path=path) +315 else: +316 print("[!] Path '%s' is not a directory." % path) +317 else: +318 print("[!] Directory '%s' does not exists." % path) +319 +320 def command_lls(self, arguments, command): +321 # Command arguments required : No +322 # Active SMB connection needed : No +323 # SMB share needed : No +324 +325 if len(arguments) == 0: +326 path = '.' +327 else: +328 path = ' '.join(arguments) +329 +330 # lls <directory> +331 if os.path.isdir(path): +332 directory_contents = os.listdir(path=path) +333 for entryname in sorted(directory_contents): +334 path_to_file = path + os.path.sep + entryname +335 rights_str = unix_permissions(path_to_file) +336 size_str = b_filesize(os.path.getsize(filename=path_to_file)) +337 date_str = datetime.datetime.fromtimestamp(os.path.getmtime(filename=path_to_file)).strftime("%Y-%m-%d %H:%M") +338 +339 if os.path.isdir(s=entryname): +340 if self.config.no_colors: +341 print("%s %10s %s %s%s" % (rights_str, size_str, date_str, entryname, os.path.sep)) +342 else: +343 print("%s %10s %s \x1b[1;96m%s\x1b[0m%s" % (rights_str, size_str, date_str, entryname, os.path.sep)) +344 else: +345 if self.config.no_colors: +346 print("%s %10s %s %s" % (rights_str, size_str, date_str, entryname)) +347 else: +348 print("%s %10s %s \x1b[1m%s\x1b[0m" % (rights_str, size_str, date_str, entryname)) +349 # lls <file> +350 elif os.path.isfile(path): +351 rights_str = unix_permissions(path) +352 size_str = b_filesize(os.path.getsize(filename=path)) +353 date_str = datetime.datetime.fromtimestamp(os.path.getmtime(filename=path)).strftime("%Y-%m-%d %H:%M") +354 if self.config.no_colors: +355 print("%s %10s %s %s" % (rights_str, size_str, date_str, os.path.basename(path))) +356 else: +357 print("%s %10s %s \x1b[1m%s\x1b[0m" % (rights_str, size_str, date_str, os.path.basename(path))) +358 else: +359 print("[!] No such file or directory.") +360 +361 @command_arguments_required +362 def command_lmkdir(self, arguments, command): +363 # Command arguments required : Yes +364 # Active SMB connection needed : No +365 # SMB share needed : No +366 +367 path = ' '.join(arguments) +368 +369 # Split each dir +370 if os.path.sep in path: +371 path = path.strip(os.path.sep).split(os.path.sep) +372 else: +373 path = [path] +374 +375 # Create each dir in the path +376 for depth in range(1, len(path)+1): +377 tmp_path = os.path.sep.join(path[:depth]) +378 if not os.path.exists(tmp_path): +379 os.mkdir(path=tmp_path) +380 +381 def command_lpwd(self, arguments, command): +382 # Command arguments required : No +383 # Active SMB connection needed : No +384 # SMB share needed : No +385 +386 print(os.getcwd()) +387 +388 @command_arguments_required +389 def command_lrm(self, arguments, command): +390 # Command arguments required : Yes +391 # Active SMB connection needed : No +392 # SMB share needed : No +393 +394 path = ' '.join(arguments) +395 if os.path.exists(path): +396 if not os.path.isdir(s=path): +397 try: +398 os.remove(path=path) +399 except Exception as e: +400 print("[!] Error removing file '%s' : %s" % path) +401 else: +402 print("[!] Cannot delete '%s'. It is a directory, use 'lrmdir <directory>' instead." % path) +403 else: +404 print("[!] Path '%s' does not exist." % path) +405 +406 @command_arguments_required +407 def command_lrmdir(self, arguments, command): +408 # Command arguments required : Yes +409 # Active SMB connection needed : No +410 # SMB share needed : No +411 +412 path = ' '.join(arguments) +413 if os.path.exists(path): +414 if os.path.isdir(s=path): +415 try: +416 shutil.rmtree(path=path) +417 except Exception as e: +418 print("[!] Error removing directory '%s' : %s" % path) +419 else: +420 print("[!] Cannot delete '%s'. It is a file, use 'lrm <file>' instead." % path) +421 else: +422 print("[!] Path '%s' does not exist." % path) +423 +424 def command_ltree(self, arguments, command): +425 # Command arguments required : No +426 # Active SMB connection needed : No +427 # SMB share needed : No +428 +429 if len(arguments) == 0: +430 local_tree(path='.', config=self.config) +431 else: +432 local_tree(path=' '.join(arguments), config=self.config) +433 +434 @active_smb_connection_needed +435 @smb_share_is_set +436 def command_ls(self, arguments, command): +437 # Command arguments required : No +438 # Active SMB connection needed : Yes +439 # SMB share needed : Yes +440 +441 # Read the files +442 directory_contents = self.smbSession.list_contents(path=' '.join(arguments)) +443 +444 for longname in sorted(directory_contents.keys(), key=lambda x:x.lower()): +445 windows_ls_entry(directory_contents[longname], self.config) +446 +447 @command_arguments_required +448 @active_smb_connection_needed +449 @smb_share_is_set +450 def command_mkdir(self, arguments, command): +451 # Command arguments required : Yes +452 # Active SMB connection needed : Yes +453 # SMB share needed : Yes +454 +455 path = ' '.join(arguments) +456 self.smbSession.mkdir(path=path) +457 +458 @command_arguments_required +459 @active_smb_connection_needed +460 @smb_share_is_set +461 def command_module(self, arguments, command): +462 module_name = arguments[0] +463 +464 if module_name in self.modules.keys(): +465 module = self.modules[module_name](self.smbSession, self.config) +466 module.run(' '.join(arguments[1:])) +467 else: +468 print("[!] Module '%s' does not exist." % module_name) +469 +470 @command_arguments_required +471 @active_smb_connection_needed +472 @smb_share_is_set +473 def command_put(self, arguments, command): +474 # Command arguments required : Yes +475 # Active SMB connection needed : Yes +476 # SMB share needed : Yes +477 +478 # Put files recursively +479 if arguments[0] == "-r": +480 localpath = ' '.join(arguments[1:]) +481 try: +482 self.smbSession.put_file_recursively(localpath=localpath) +483 except impacket.smbconnection.SessionError as e: +484 print("[!] SMB Error: %s" % e) +485 +486 # Put a single file +487 else: +488 localpath = ' '.join(arguments) +489 try: +490 self.smbSession.put_file(localpath=localpath) +491 except impacket.smbconnection.SessionError as e: +492 print("[!] SMB Error: %s" % e) +493 +494 def command_reconnect(self, arguments, command): +495 # Command arguments required : No +496 # Active SMB connection needed : No +497 # SMB share needed : No +498 +499 self.smbSession.ping_smb_session() +500 if self.smbSession.connected: +501 self.smbSession.close_smb_session() +502 self.smbSession.init_smb_session() +503 else: +504 self.smbSession.init_smb_session() +505 +506 def command_reset(self, arguments, command): +507 # Command arguments required : No +508 # Active SMB connection needed : No +509 # SMB share needed : No +510 sys.stdout.write('\x1b[?25h') # Sets the cursor to on +511 sys.stdout.write('\x1b[v') +512 sys.stdout.write('\x1b[o') # Reset +513 sys.stdout.flush() +514 +515 @command_arguments_required +516 @active_smb_connection_needed +517 @smb_share_is_set +518 def command_rm(self, arguments, command): +519 # Command arguments required : Yes +520 # Active SMB connection needed : Yes +521 # SMB share needed : Yes +522 +523 path = ' '.join(arguments) +524 if self.smbSession.path_exists(path): +525 if self.smbSession.path_isfile(path): +526 try: +527 self.smbSession.rm(path=path) +528 except Exception as e: +529 print("[!] Error removing file '%s' : %s" % path) +530 else: +531 print("[!] Cannot delete '%s': This is a directory, use 'rmdir <directory>' instead." % path) +532 else: +533 print("[!] Remote file '%s' does not exist." % path) +534 +535 @command_arguments_required +536 @active_smb_connection_needed +537 @smb_share_is_set +538 def command_rmdir(self, arguments, command): +539 # Command arguments required : Yes +540 # Active SMB connection needed : Yes +541 # SMB share needed : Yes +542 +543 path = ' '.join(arguments) +544 if self.smbSession.path_exists(path): +545 if self.smbSession.path_isdir(path): +546 try: +547 self.smbSession.rmdir(path=path) +548 except Exception as e: +549 print("[!] Error removing directory '%s' : %s" % path) +550 else: +551 print("[!] Cannot delete '%s': This is a file, use 'rm <file>' instead." % path) +552 else: +553 print("[!] Remote directory '%s' does not exist." % path) +554 +555 @active_smb_connection_needed +556 def command_shares(self, arguments, command): +557 # Command arguments required : No +558 # Active SMB connection needed : Yes +559 # SMB share needed : No +560 +561 shares = self.smbSession.list_shares() +562 if len(shares.keys()) != 0: +563 table = Table(title=None) +564 table.add_column("Share") +565 table.add_column("Hidden") +566 table.add_column("Type") +567 table.add_column("Description", justify="left") +568 +569 for sharename in sorted(shares.keys()): +570 is_hidden = bool(sharename.endswith('$')) +571 types = ', '.join([s.replace("STYPE_","") for s in shares[sharename]["type"]]) +572 if is_hidden: +573 table.add_row(sharename, str(is_hidden), types, shares[sharename]["comment"]) +574 else: +575 table.add_row(sharename, str(is_hidden), types, shares[sharename]["comment"]) +576 +577 Console().print(table) +578 else: +579 print("[!] No share served on '%s'" % self.smbSession.address) +580 +581 @active_smb_connection_needed +582 @smb_share_is_set +583 def command_tree(self, arguments, command): +584 # Command arguments required : No +585 # Active SMB connection needed : Yes +586 # SMB share needed : Yes +587 +588 if len(arguments) == 0: +589 self.smbSession.tree(path='.') +590 else: +591 self.smbSession.tree(path=' '.join(arguments)) +592 +593 @command_arguments_required +594 @active_smb_connection_needed +595 def command_use(self, arguments, command): +596 # Command arguments required : Yes +597 # Active SMB connection needed : Yes +598 # SMB share needed : No +599 +600 sharename = ' '.join(arguments) +601 # Reload the list of shares +602 shares = self.smbSession.list_shares() +603 shares = [s.lower() for s in shares.keys()] +604 if sharename.lower() in shares: +605 self.smbSession.set_share(sharename) +606 else: +607 print("[!] No share named '%s' on '%s'" % (sharename, self.smbSession.address)) +608 +609 # Private functions ======================================================= +610 +611 def __load_modules(self): +612 +613 +614 self.modules.clear() +615 +616 modules_dir = os.path.normpath(os.path.dirname(__file__) + os.path.sep + ".." + os.path.sep + "modules") +617 if self.config.debug: +618 print("[debug] Loading modules from %s ..." % modules_dir) +619 sys.path.extend([modules_dir]) +620 +621 for file in os.listdir(modules_dir): +622 filepath = os.path.normpath(modules_dir + os.path.sep + file) +623 if file.endswith('.py'): +624 if os.path.isfile(filepath) and file not in ["__init__.py"]: +625 try: +626 module_file = import_module('smbclientng.modules.%s' % (file[:-3])) +627 module = module_file.__getattribute__(file[:-3]) +628 self.modules[module.name.lower()] = module +629 except AttributeError as e: +630 pass +631 +632 if self.config.debug: +633 if len(self.modules.keys()) == 0: +634 print("[debug] Loaded 0 modules.") +635 elif len(self.modules.keys()) == 1: +636 print("[debug] Loaded 1 module:") +637 else: +638 print("[debug] Loaded %d modules:" % len(self.modules.keys())) +639 for modulename in sorted(self.modules.keys()): +640 print("[debug] %s : \"%s\"" % (module.name, module.description)) +641 +642 if self.commandCompleterObject is not None: +643 self.commandCompleterObject.commands["module"]["subcommands"] = list(self.modules.keys()) +644 +645 def __prompt(self): +646 """ +647 Prints the command prompt for the interactive shell. +648 +649 This method constructs and returns the command prompt string based on the current state of the SMB session. +650 The prompt indicates the connection status with a visual symbol and displays the current working directory +651 or the SMB share path. The prompt appearance changes based on whether colors are enabled in the configuration. +652 +653 Returns: +654 str: The formatted command prompt string. +655 """ +656 +657 self.smbSession.ping_smb_session() +658 if self.smbSession.connected: +659 if self.config.no_colors: +660 connected_dot = "[v]" +661 else: +662 connected_dot = "\x1b[1;92m⏺\x1b[0m" +663 else: +664 if self.config.no_colors: +665 connected_dot = "[x]" +666 else: +667 connected_dot = "\x1b[1;91m⏺\x1b[0m" +668 +669 if self.smbSession.smb_share is None: +670 if self.config.no_colors: +671 str_prompt = "%s[\\\\%s\\]> " % (connected_dot, self.smbSession.address) +672 else: +673 str_prompt = "%s[\x1b[1;94m\\\\%s\\\x1b[0m]> " % (connected_dot, self.smbSession.address) +674 else: +675 str_path = "\\\\%s\\%s\\%s" % (self.smbSession.address, self.smbSession.smb_share, self.smbSession.smb_cwd.lstrip(ntpath.sep)) +676 if self.config.no_colors: +677 str_prompt = "%s[%s]> " % (connected_dot, str_path) +678 else: +679 str_prompt = "%s[\x1b[1;94m%s\x1b[0m]> " % (connected_dot, str_path) +680 +681 return str_prompt +
Class InteractiveShell is designed to manage the interactive command line interface for smbclient-ng.
+ +This class handles user input, executes commands, and manages the state of the SMB session. It provides +a command line interface for users to interact with SMB shares, execute commands like directory listing, +file transfer, and more.
+ +Attributes: + smbSession (SMBConnection): The active SMB connection session. + debug (bool): Flag to enable or disable debug mode. + smb_share (str): The current SMB share in use. + smb_path (str): The current path within the SMB share. + commandCompleterObject (CommandCompleter): Object to handle command completion and help generation.
+ +Methods: + __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.
+79 def __init__(self, smbSession, config): +80 # Objects +81 self.smbSession = smbSession +82 self.config = config +83 self.commandCompleterObject = CommandCompleter(smbSession=self.smbSession, config=self.config) +84 readline.set_completer(self.commandCompleterObject.complete) +85 readline.parse_and_bind("tab: complete") +86 readline.set_completer_delims("\n") +87 # Additional modules +88 self.modules = {} +89 self.__load_modules() +
91 def run(self): + 92 running = True + 93 while running: + 94 try: + 95 user_input = input(self.__prompt()).strip().split(" ") + 96 command, arguments = user_input[0].lower(), user_input[1:] + 97 + 98 # Exit the command line + 99 if command == "exit": +100 running = False +101 +102 elif command in self.commandCompleterObject.commands.keys(): +103 self.process_command( +104 command=command, +105 arguments=arguments +106 ) +107 +108 elif command.strip() == "": +109 pass +110 +111 # Fallback to unknown command +112 else: +113 print("Unknown command. Type \"help\" for help.") +114 +115 except KeyboardInterrupt as e: +116 print() +117 +118 except EOFError as e: +119 print() +120 running = False +121 +122 except Exception as e: +123 if self.config.debug: +124 traceback.print_exc() +125 print("[!] Error: %s" % str(e)) +
127 def process_command(self, command, arguments=[]): +128 # Skip +129 if command.strip() == "": +130 pass +131 +132 # Display help +133 elif command == "help": +134 self.command_help(arguments, command) +135 +136 # Closes the current SMB session +137 elif command == "close": +138 self.command_close(arguments, command) +139 +140 # Change directory in the current share +141 elif command == "cd": +142 self.command_cd(arguments, command) +143 +144 # Get a file +145 elif command == "get": +146 self.command_get(arguments, command) +147 +148 # SMB server info +149 elif command == "info": +150 self.command_info(arguments, command) +151 +152 # List directory contents in a share +153 elif command in ["ls", "dir"]: +154 self.command_ls(arguments, command) +155 +156 # Creates a new remote directory +157 elif command == "mkdir": +158 self.command_mkdir(arguments, command) +159 +160 # Put a file +161 elif command == "put": +162 self.command_put(arguments, command) +163 +164 # Changes the current local directory +165 elif command == "lcd": +166 self.command_lcd(arguments, command) +167 +168 # Lists the contents of the current local directory +169 elif command == "lls": +170 self.command_lls(arguments, command) +171 +172 # Creates a new local directory +173 elif command == "lmkdir": +174 self.command_lmkdir(arguments, command) +175 +176 # Shows the current local directory +177 elif command == "lpwd": +178 self.command_lpwd(arguments, command) +179 +180 # Removes a local file +181 elif command == "lrm": +182 self.command_lrm(arguments, command) +183 +184 # Removes a local directory +185 elif command == "lrmdir": +186 self.command_lrmdir(arguments, command) +187 +188 # Shows the current local directory +189 elif command == "ltree": +190 self.command_ltree(arguments, command) +191 +192 # Modules +193 elif command == "module": +194 self.command_module(arguments, command) +195 +196 # Reconnects the current SMB session +197 elif command in ["connect", "reconnect"]: +198 self.command_reconnect(arguments, command) +199 +200 # Reset the TTY output +201 elif command == "reset": +202 self.command_reset(arguments, command) +203 +204 # Removes a remote file +205 elif command == "rm": +206 self.command_rm(arguments, command) +207 +208 # Removes a remote directory +209 elif command == "rmdir": +210 self.command_rmdir(arguments, command) +211 +212 # List shares +213 elif command == "shares": +214 self.command_shares(arguments, command) +215 +216 # Displays a tree view of the CWD +217 elif command == "tree": +218 self.command_tree(arguments, command) +219 +220 # Use a share +221 elif command == "use": +222 self.command_use(arguments, command) +
272 def command_help(self, arguments, command): +273 # Command arguments required : No +274 # Active SMB connection needed : No +275 # SMB share needed : No +276 +277 if len(arguments) != 0: +278 self.commandCompleterObject.print_help(command=arguments[0]) +279 else: +280 self.commandCompleterObject.print_help(command=None) +
320 def command_lls(self, arguments, command): +321 # Command arguments required : No +322 # Active SMB connection needed : No +323 # SMB share needed : No +324 +325 if len(arguments) == 0: +326 path = '.' +327 else: +328 path = ' '.join(arguments) +329 +330 # lls <directory> +331 if os.path.isdir(path): +332 directory_contents = os.listdir(path=path) +333 for entryname in sorted(directory_contents): +334 path_to_file = path + os.path.sep + entryname +335 rights_str = unix_permissions(path_to_file) +336 size_str = b_filesize(os.path.getsize(filename=path_to_file)) +337 date_str = datetime.datetime.fromtimestamp(os.path.getmtime(filename=path_to_file)).strftime("%Y-%m-%d %H:%M") +338 +339 if os.path.isdir(s=entryname): +340 if self.config.no_colors: +341 print("%s %10s %s %s%s" % (rights_str, size_str, date_str, entryname, os.path.sep)) +342 else: +343 print("%s %10s %s \x1b[1;96m%s\x1b[0m%s" % (rights_str, size_str, date_str, entryname, os.path.sep)) +344 else: +345 if self.config.no_colors: +346 print("%s %10s %s %s" % (rights_str, size_str, date_str, entryname)) +347 else: +348 print("%s %10s %s \x1b[1m%s\x1b[0m" % (rights_str, size_str, date_str, entryname)) +349 # lls <file> +350 elif os.path.isfile(path): +351 rights_str = unix_permissions(path) +352 size_str = b_filesize(os.path.getsize(filename=path)) +353 date_str = datetime.datetime.fromtimestamp(os.path.getmtime(filename=path)).strftime("%Y-%m-%d %H:%M") +354 if self.config.no_colors: +355 print("%s %10s %s %s" % (rights_str, size_str, date_str, os.path.basename(path))) +356 else: +357 print("%s %10s %s \x1b[1m%s\x1b[0m" % (rights_str, size_str, date_str, os.path.basename(path))) +358 else: +359 print("[!] No such file or directory.") +
424 def command_ltree(self, arguments, command): +425 # Command arguments required : No +426 # Active SMB connection needed : No +427 # SMB share needed : No +428 +429 if len(arguments) == 0: +430 local_tree(path='.', config=self.config) +431 else: +432 local_tree(path=' '.join(arguments), config=self.config) +
494 def command_reconnect(self, arguments, command): +495 # Command arguments required : No +496 # Active SMB connection needed : No +497 # SMB share needed : No +498 +499 self.smbSession.ping_smb_session() +500 if self.smbSession.connected: +501 self.smbSession.close_smb_session() +502 self.smbSession.init_smb_session() +503 else: +504 self.smbSession.init_smb_session() +
506 def command_reset(self, arguments, command): +507 # Command arguments required : No +508 # Active SMB connection needed : No +509 # SMB share needed : No +510 sys.stdout.write('\x1b[?25h') # Sets the cursor to on +511 sys.stdout.write('\x1b[v') +512 sys.stdout.write('\x1b[o') # Reset +513 sys.stdout.flush() +
1#!/usr/bin/env python3 + 2# -*- coding: utf-8 -*- + 3# File name : LocalFileIO.py + 4# Author : Podalirius (@podalirius_) + 5# Date created : 23 may 2024 + 6 + 7 + 8import os + 9import ntpath + 10from rich.progress import BarColumn, DownloadColumn, Progress, TextColumn, TimeRemainingColumn, TransferSpeedColumn + 11 + 12 + 13class LocalFileIO(object): + 14 """ + 15 Class LocalFileIO is designed to handle local file input/output operations within the smbclient-ng tool. + 16 It provides functionalities to open, read, write, and manage progress of file operations based on the expected size of the file. + 17 + 18 Attributes: + 19 mode (str): The mode in which the file should be opened (e.g., 'rb', 'wb'). + 20 path (str): The path to the file that needs to be handled. + 21 expected_size (int, optional): The expected size of the file in bytes. This is used to display progress. + 22 debug (bool): Flag to enable debug mode which provides additional output during operations. + 23 + 24 Methods: + 25 __init__(self, mode, path=None, expected_size=None, debug=False): Initializes the LocalFileIO instance. + 26 write(self, data): Writes data to the file and updates the progress bar if expected size is provided. + 27 read(self, size): Reads data from the file up to the specified size and updates the progress bar if expected size is provided. + 28 """ + 29 + 30 def __init__(self, mode, path=None, expected_size=None, keepRemotePath=False, debug=False): + 31 super(LocalFileIO, self).__init__() + 32 self.mode = mode + 33 # Convert remote path format to local operating system path format + 34 self.path = path.replace(ntpath.sep, os.path.sep) + 35 self.dir = None + 36 self.debug = False + 37 self.expected_size = expected_size + 38 self.keepRemotePath = keepRemotePath + 39 + 40 # Write to local (read remote) + 41 if self.mode in ["wb"]: + 42 self.dir = '.' + os.path.sep + 43 if keepRemotePath: + 44 self.dir += os.path.dirname(self.path) + 45 + 46 if not os.path.exists(self.dir): + 47 if self.debug: + 48 print("[debug] Creating local directory '%s'" % self.dir) + 49 os.makedirs(self.dir) + 50 + 51 if self.debug: + 52 print("[debug] Openning local '%s' with mode '%s'" % (self.path, self.mode)) + 53 + 54 self.fd = open(self.dir + os.path.sep + os.path.basename(self.path), self.mode) + 55 + 56 # Write to remote (read local) + 57 elif self.mode in ["rb"]: + 58 if ntpath.sep in self.path: + 59 self.dir = os.path.dirname(self.path) + 60 + 61 if self.debug: + 62 print("[debug] Openning local '%s' with mode '%s'" % (self.path, self.mode)) + 63 self.fd = open(self.path, self.mode) + 64 + 65 if self.expected_size is None: + 66 self.expected_size = os.path.getsize(filename=self.path) + 67 + 68 # Create progress bar + 69 if self.expected_size is not None: + 70 self.__progress = Progress( + 71 TextColumn("[bold blue]{task.description}", justify="right"), + 72 BarColumn(bar_width=None), + 73 "[progress.percentage]{task.percentage:>3.1f}%", + 74 "•", + 75 DownloadColumn(), + 76 "•", + 77 TransferSpeedColumn(), + 78 "•", + 79 TimeRemainingColumn(), + 80 ) + 81 self.__progress.start() + 82 self.__task = self.__progress.add_task( + 83 description="'%s'" % os.path.basename(self.path), + 84 start=True, + 85 total=self.expected_size, + 86 visible=True + 87 ) + 88 + 89 def write(self, data): + 90 """ + 91 Writes data to the file. + 92 + 93 This method writes the specified data to the file and updates the progress bar with the amount of data written if the expected size is set. + 94 + 95 Args: + 96 data (bytes): The data to be written to the file. + 97 + 98 Returns: + 99 int: The number of bytes written. +100 """ +101 +102 if self.expected_size is not None: +103 self.__progress.update(self.__task, advance=len(data)) +104 return self.fd.write(data) +105 +106 def read(self, size): +107 """ +108 Reads a specified amount of data from the file. +109 +110 This method reads data from the file based on the size specified. It also updates the progress bar with the amount of data read if the expected size is set. +111 +112 Args: +113 size (int): The number of bytes to read from the file. +114 +115 Returns: +116 bytes: The data read from the file. +117 """ +118 +119 read_data = self.fd.read(size) +120 if self.expected_size is not None: +121 self.__progress.update(self.__task, advance=len(read_data)) +122 return read_data +123 +124 def close(self, remove=False): +125 """ +126 Closes the file descriptor and optionally removes the file. +127 +128 This method ensures that the file descriptor is properly closed and the file is removed if specified. +129 It also stops the progress bar if it was initiated and cleans up the object by deleting it. +130 +131 Args: +132 remove (bool): If True, the file at the path will be removed after closing the file descriptor. +133 """ +134 +135 self.fd.close() +136 +137 if remove: +138 os.remove(path=self.path) +139 +140 if self.expected_size is not None: +141 self.__progress.stop() +142 +143 del self +144 +145 def set_error(self, message): +146 """ +147 Sets an error message in the progress bar's description and modifies the progress bar to show only essential columns. +148 +149 This method is used to communicate error states or important messages directly in the progress bar interface. +150 It updates the task description with the provided message and simplifies the progress bar to show only the text +151 and download columns, removing other elements like speed and time remaining which may not be relevant in an error state. +152 +153 Args: +154 message (str): The error or status message to display in the progress bar. +155 """ +156 +157 self.__progress.tasks[0].description = message +158 self.__progress.columns = [ +159 TextColumn("[bold blue]{task.description}", justify="right"), +160 BarColumn(bar_width=None), +161 "•", +162 DownloadColumn(), +163 ] +164 self.__progress.update(self.__task, advance=0) +
14class LocalFileIO(object): + 15 """ + 16 Class LocalFileIO is designed to handle local file input/output operations within the smbclient-ng tool. + 17 It provides functionalities to open, read, write, and manage progress of file operations based on the expected size of the file. + 18 + 19 Attributes: + 20 mode (str): The mode in which the file should be opened (e.g., 'rb', 'wb'). + 21 path (str): The path to the file that needs to be handled. + 22 expected_size (int, optional): The expected size of the file in bytes. This is used to display progress. + 23 debug (bool): Flag to enable debug mode which provides additional output during operations. + 24 + 25 Methods: + 26 __init__(self, mode, path=None, expected_size=None, debug=False): Initializes the LocalFileIO instance. + 27 write(self, data): Writes data to the file and updates the progress bar if expected size is provided. + 28 read(self, size): Reads data from the file up to the specified size and updates the progress bar if expected size is provided. + 29 """ + 30 + 31 def __init__(self, mode, path=None, expected_size=None, keepRemotePath=False, debug=False): + 32 super(LocalFileIO, self).__init__() + 33 self.mode = mode + 34 # Convert remote path format to local operating system path format + 35 self.path = path.replace(ntpath.sep, os.path.sep) + 36 self.dir = None + 37 self.debug = False + 38 self.expected_size = expected_size + 39 self.keepRemotePath = keepRemotePath + 40 + 41 # Write to local (read remote) + 42 if self.mode in ["wb"]: + 43 self.dir = '.' + os.path.sep + 44 if keepRemotePath: + 45 self.dir += os.path.dirname(self.path) + 46 + 47 if not os.path.exists(self.dir): + 48 if self.debug: + 49 print("[debug] Creating local directory '%s'" % self.dir) + 50 os.makedirs(self.dir) + 51 + 52 if self.debug: + 53 print("[debug] Openning local '%s' with mode '%s'" % (self.path, self.mode)) + 54 + 55 self.fd = open(self.dir + os.path.sep + os.path.basename(self.path), self.mode) + 56 + 57 # Write to remote (read local) + 58 elif self.mode in ["rb"]: + 59 if ntpath.sep in self.path: + 60 self.dir = os.path.dirname(self.path) + 61 + 62 if self.debug: + 63 print("[debug] Openning local '%s' with mode '%s'" % (self.path, self.mode)) + 64 self.fd = open(self.path, self.mode) + 65 + 66 if self.expected_size is None: + 67 self.expected_size = os.path.getsize(filename=self.path) + 68 + 69 # Create progress bar + 70 if self.expected_size is not None: + 71 self.__progress = Progress( + 72 TextColumn("[bold blue]{task.description}", justify="right"), + 73 BarColumn(bar_width=None), + 74 "[progress.percentage]{task.percentage:>3.1f}%", + 75 "•", + 76 DownloadColumn(), + 77 "•", + 78 TransferSpeedColumn(), + 79 "•", + 80 TimeRemainingColumn(), + 81 ) + 82 self.__progress.start() + 83 self.__task = self.__progress.add_task( + 84 description="'%s'" % os.path.basename(self.path), + 85 start=True, + 86 total=self.expected_size, + 87 visible=True + 88 ) + 89 + 90 def write(self, data): + 91 """ + 92 Writes data to the file. + 93 + 94 This method writes the specified data to the file and updates the progress bar with the amount of data written if the expected size is set. + 95 + 96 Args: + 97 data (bytes): The data to be written to the file. + 98 + 99 Returns: +100 int: The number of bytes written. +101 """ +102 +103 if self.expected_size is not None: +104 self.__progress.update(self.__task, advance=len(data)) +105 return self.fd.write(data) +106 +107 def read(self, size): +108 """ +109 Reads a specified amount of data from the file. +110 +111 This method reads data from the file based on the size specified. It also updates the progress bar with the amount of data read if the expected size is set. +112 +113 Args: +114 size (int): The number of bytes to read from the file. +115 +116 Returns: +117 bytes: The data read from the file. +118 """ +119 +120 read_data = self.fd.read(size) +121 if self.expected_size is not None: +122 self.__progress.update(self.__task, advance=len(read_data)) +123 return read_data +124 +125 def close(self, remove=False): +126 """ +127 Closes the file descriptor and optionally removes the file. +128 +129 This method ensures that the file descriptor is properly closed and the file is removed if specified. +130 It also stops the progress bar if it was initiated and cleans up the object by deleting it. +131 +132 Args: +133 remove (bool): If True, the file at the path will be removed after closing the file descriptor. +134 """ +135 +136 self.fd.close() +137 +138 if remove: +139 os.remove(path=self.path) +140 +141 if self.expected_size is not None: +142 self.__progress.stop() +143 +144 del self +145 +146 def set_error(self, message): +147 """ +148 Sets an error message in the progress bar's description and modifies the progress bar to show only essential columns. +149 +150 This method is used to communicate error states or important messages directly in the progress bar interface. +151 It updates the task description with the provided message and simplifies the progress bar to show only the text +152 and download columns, removing other elements like speed and time remaining which may not be relevant in an error state. +153 +154 Args: +155 message (str): The error or status message to display in the progress bar. +156 """ +157 +158 self.__progress.tasks[0].description = message +159 self.__progress.columns = [ +160 TextColumn("[bold blue]{task.description}", justify="right"), +161 BarColumn(bar_width=None), +162 "•", +163 DownloadColumn(), +164 ] +165 self.__progress.update(self.__task, advance=0) +
Class LocalFileIO is designed to handle local file input/output operations within the smbclient-ng tool. +It provides functionalities to open, read, write, and manage progress of file operations based on the expected size of the file.
+ +Attributes: + mode (str): The mode in which the file should be opened (e.g., 'rb', 'wb'). + path (str): The path to the file that needs to be handled. + expected_size (int, optional): The expected size of the file in bytes. This is used to display progress. + debug (bool): Flag to enable debug mode which provides additional output during operations.
+ +Methods: + __init__(self, mode, path=None, expected_size=None, debug=False): Initializes the LocalFileIO instance. + write(self, data): Writes data to the file and updates the progress bar if expected size is provided. + read(self, size): Reads data from the file up to the specified size and updates the progress bar if expected size is provided.
+31 def __init__(self, mode, path=None, expected_size=None, keepRemotePath=False, debug=False): +32 super(LocalFileIO, self).__init__() +33 self.mode = mode +34 # Convert remote path format to local operating system path format +35 self.path = path.replace(ntpath.sep, os.path.sep) +36 self.dir = None +37 self.debug = False +38 self.expected_size = expected_size +39 self.keepRemotePath = keepRemotePath +40 +41 # Write to local (read remote) +42 if self.mode in ["wb"]: +43 self.dir = '.' + os.path.sep +44 if keepRemotePath: +45 self.dir += os.path.dirname(self.path) +46 +47 if not os.path.exists(self.dir): +48 if self.debug: +49 print("[debug] Creating local directory '%s'" % self.dir) +50 os.makedirs(self.dir) +51 +52 if self.debug: +53 print("[debug] Openning local '%s' with mode '%s'" % (self.path, self.mode)) +54 +55 self.fd = open(self.dir + os.path.sep + os.path.basename(self.path), self.mode) +56 +57 # Write to remote (read local) +58 elif self.mode in ["rb"]: +59 if ntpath.sep in self.path: +60 self.dir = os.path.dirname(self.path) +61 +62 if self.debug: +63 print("[debug] Openning local '%s' with mode '%s'" % (self.path, self.mode)) +64 self.fd = open(self.path, self.mode) +65 +66 if self.expected_size is None: +67 self.expected_size = os.path.getsize(filename=self.path) +68 +69 # Create progress bar +70 if self.expected_size is not None: +71 self.__progress = Progress( +72 TextColumn("[bold blue]{task.description}", justify="right"), +73 BarColumn(bar_width=None), +74 "[progress.percentage]{task.percentage:>3.1f}%", +75 "•", +76 DownloadColumn(), +77 "•", +78 TransferSpeedColumn(), +79 "•", +80 TimeRemainingColumn(), +81 ) +82 self.__progress.start() +83 self.__task = self.__progress.add_task( +84 description="'%s'" % os.path.basename(self.path), +85 start=True, +86 total=self.expected_size, +87 visible=True +88 ) +
90 def write(self, data): + 91 """ + 92 Writes data to the file. + 93 + 94 This method writes the specified data to the file and updates the progress bar with the amount of data written if the expected size is set. + 95 + 96 Args: + 97 data (bytes): The data to be written to the file. + 98 + 99 Returns: +100 int: The number of bytes written. +101 """ +102 +103 if self.expected_size is not None: +104 self.__progress.update(self.__task, advance=len(data)) +105 return self.fd.write(data) +
Writes data to the file.
+ +This method writes the specified data to the file and updates the progress bar with the amount of data written if the expected size is set.
+ +Args: + data (bytes): The data to be written to the file.
+ +Returns: + int: The number of bytes written.
+107 def read(self, size): +108 """ +109 Reads a specified amount of data from the file. +110 +111 This method reads data from the file based on the size specified. It also updates the progress bar with the amount of data read if the expected size is set. +112 +113 Args: +114 size (int): The number of bytes to read from the file. +115 +116 Returns: +117 bytes: The data read from the file. +118 """ +119 +120 read_data = self.fd.read(size) +121 if self.expected_size is not None: +122 self.__progress.update(self.__task, advance=len(read_data)) +123 return read_data +
Reads a specified amount of data from the file.
+ +This method reads data from the file based on the size specified. It also updates the progress bar with the amount of data read if the expected size is set.
+ +Args: + size (int): The number of bytes to read from the file.
+ +Returns: + bytes: The data read from the file.
+125 def close(self, remove=False): +126 """ +127 Closes the file descriptor and optionally removes the file. +128 +129 This method ensures that the file descriptor is properly closed and the file is removed if specified. +130 It also stops the progress bar if it was initiated and cleans up the object by deleting it. +131 +132 Args: +133 remove (bool): If True, the file at the path will be removed after closing the file descriptor. +134 """ +135 +136 self.fd.close() +137 +138 if remove: +139 os.remove(path=self.path) +140 +141 if self.expected_size is not None: +142 self.__progress.stop() +143 +144 del self +
Closes the file descriptor and optionally removes the file.
+ +This method ensures that the file descriptor is properly closed and the file is removed if specified. +It also stops the progress bar if it was initiated and cleans up the object by deleting it.
+ +Args: + remove (bool): If True, the file at the path will be removed after closing the file descriptor.
+146 def set_error(self, message): +147 """ +148 Sets an error message in the progress bar's description and modifies the progress bar to show only essential columns. +149 +150 This method is used to communicate error states or important messages directly in the progress bar interface. +151 It updates the task description with the provided message and simplifies the progress bar to show only the text +152 and download columns, removing other elements like speed and time remaining which may not be relevant in an error state. +153 +154 Args: +155 message (str): The error or status message to display in the progress bar. +156 """ +157 +158 self.__progress.tasks[0].description = message +159 self.__progress.columns = [ +160 TextColumn("[bold blue]{task.description}", justify="right"), +161 BarColumn(bar_width=None), +162 "•", +163 DownloadColumn(), +164 ] +165 self.__progress.update(self.__task, advance=0) +
Sets an error message in the progress bar's description and modifies the progress bar to show only essential columns.
+ +This method is used to communicate error states or important messages directly in the progress bar interface. +It updates the task description with the provided message and simplifies the progress bar to show only the text +and download columns, removing other elements like speed and time remaining which may not be relevant in an error state.
+ +Args: + message (str): The error or status message to display in the progress bar.
+1#!/usr/bin/env python3 + 2# -*- coding: utf-8 -*- + 3# File name : InteractiveShell.py + 4# Author : Podalirius (@podalirius_) + 5# Date created : 23 may 2024 + 6 + 7 + 8import shlex + 9 +10 +11class Module(object): +12 """ +13 A parent class for all modules in the smbclient-ng tool. +14 +15 This class provides common attributes and methods that are shared among different modules. +16 """ +17 +18 name = "" +19 description = "" +20 smbSession = None +21 options = None +22 +23 def __init__(self, smbSession, config): +24 self.smbSession = smbSession +25 self.config = config +26 +27 def parseArgs(self): +28 raise NotImplementedError("Subclasses must implement this method") +29 +30 def run(self): +31 """ +32 Placeholder method for running the module. +33 +34 This method should be implemented by subclasses to define the specific behavior of the module. +35 """ +36 raise NotImplementedError("Subclasses must implement this method") +37 +38 def processArguments(self, parser, arguments): +39 if type(arguments) == list: +40 arguments = ' '.join(arguments) +41 +42 __iterableArguments = shlex.split(arguments) +43 +44 self.options = parser.parse_args(__iterableArguments) +45 +46 return self.options +47 +
12class Module(object): +13 """ +14 A parent class for all modules in the smbclient-ng tool. +15 +16 This class provides common attributes and methods that are shared among different modules. +17 """ +18 +19 name = "" +20 description = "" +21 smbSession = None +22 options = None +23 +24 def __init__(self, smbSession, config): +25 self.smbSession = smbSession +26 self.config = config +27 +28 def parseArgs(self): +29 raise NotImplementedError("Subclasses must implement this method") +30 +31 def run(self): +32 """ +33 Placeholder method for running the module. +34 +35 This method should be implemented by subclasses to define the specific behavior of the module. +36 """ +37 raise NotImplementedError("Subclasses must implement this method") +38 +39 def processArguments(self, parser, arguments): +40 if type(arguments) == list: +41 arguments = ' '.join(arguments) +42 +43 __iterableArguments = shlex.split(arguments) +44 +45 self.options = parser.parse_args(__iterableArguments) +46 +47 return self.options +
A parent class for all modules in the smbclient-ng tool.
+ +This class provides common attributes and methods that are shared among different modules.
+31 def run(self): +32 """ +33 Placeholder method for running the module. +34 +35 This method should be implemented by subclasses to define the specific behavior of the module. +36 """ +37 raise NotImplementedError("Subclasses must implement this method") +
Placeholder method for running the module.
+ +This method should be implemented by subclasses to define the specific behavior of the module.
+1#!/usr/bin/env python3 + 2# -*- coding: utf-8 -*- + 3# File name : ModuleArgumentParser.py + 4# Author : Podalirius (@podalirius_) + 5# Date created : 23 may 2024 + 6 + 7 + 8import argparse + 9import sys +10 +11 +12class ModuleArgumentParser(argparse.ArgumentParser): +13 """ +14 A custom argument parser for handling module-specific command-line arguments in the smbclientng application. +15 +16 This class extends the argparse.ArgumentParser and provides custom error handling specific to the needs of smbclientng modules. +17 It is designed to provide clear and user-friendly command-line interfaces for various modules within the smbclientng suite. +18 +19 Attributes: +20 None +21 +22 Methods: +23 error(message: str): +24 Overrides the default error handling to provide a more informative error message and display the help text. +25 """ +26 +27 def error(self, message): +28 """ +29 Overrides the default error handling of argparse.ArgumentParser to provide a custom error message and help display. +30 +31 This method is called when ArgumentParser encounters an error. It writes the error message to stderr, +32 displays the help message, and then exits the program with a status code of 2. +33 +34 Args: +35 message (str): The error message to be displayed. +36 """ +37 +38 sys.stderr.write('[!] Error: %s\n' % message) +39 self.print_help() +
13class ModuleArgumentParser(argparse.ArgumentParser): +14 """ +15 A custom argument parser for handling module-specific command-line arguments in the smbclientng application. +16 +17 This class extends the argparse.ArgumentParser and provides custom error handling specific to the needs of smbclientng modules. +18 It is designed to provide clear and user-friendly command-line interfaces for various modules within the smbclientng suite. +19 +20 Attributes: +21 None +22 +23 Methods: +24 error(message: str): +25 Overrides the default error handling to provide a more informative error message and display the help text. +26 """ +27 +28 def error(self, message): +29 """ +30 Overrides the default error handling of argparse.ArgumentParser to provide a custom error message and help display. +31 +32 This method is called when ArgumentParser encounters an error. It writes the error message to stderr, +33 displays the help message, and then exits the program with a status code of 2. +34 +35 Args: +36 message (str): The error message to be displayed. +37 """ +38 +39 sys.stderr.write('[!] Error: %s\n' % message) +40 self.print_help() +
A custom argument parser for handling module-specific command-line arguments in the smbclientng application.
+ +This class extends the argparse.ArgumentParser and provides custom error handling specific to the needs of smbclientng modules. +It is designed to provide clear and user-friendly command-line interfaces for various modules within the smbclientng suite.
+ +Attributes: + None
+ +Methods: + error(message: str): + Overrides the default error handling to provide a more informative error message and display the help text.
+28 def error(self, message): +29 """ +30 Overrides the default error handling of argparse.ArgumentParser to provide a custom error message and help display. +31 +32 This method is called when ArgumentParser encounters an error. It writes the error message to stderr, +33 displays the help message, and then exits the program with a status code of 2. +34 +35 Args: +36 message (str): The error message to be displayed. +37 """ +38 +39 sys.stderr.write('[!] Error: %s\n' % message) +40 self.print_help() +
Overrides the default error handling of argparse.ArgumentParser to provide a custom error message and help display.
+ +This method is called when ArgumentParser encounters an error. It writes the error message to stderr, +displays the help message, and then exits the program with a status code of 2.
+ +Args: + message (str): The error message to be displayed.
+1#!/usr/bin/env python3 + 2# -*- coding: utf-8 -*- + 3# File name : smbclient-ng.py + 4# Author : Podalirius (@podalirius_) + 5# Date created : 20 may 2024 + 6 + 7 + 8import impacket.smbconnection + 9import ntpath + 10import os + 11import re + 12import traceback + 13from smbclientng.core.LocalFileIO import LocalFileIO + 14from smbclientng.core.utils import b_filesize, STYPE_MASK + 15 + 16 + 17class SMBSession(object): + 18 """ + 19 Class SMBSession is designed to handle the session management for SMB (Server Message Block) protocol connections. + 20 It provides functionalities to connect to an SMB server, authenticate using either NTLM or Kerberos, and manage SMB shares. + 21 + 22 Attributes: + 23 address (str): The IP address or hostname of the SMB server. + 24 domain (str): The domain name for SMB server authentication. + 25 username (str): The username for SMB server authentication. + 26 password (str): The password for SMB server authentication. + 27 lmhash (str): The LM hash of the user's password, if available. + 28 nthash (str): The NT hash of the user's password, if available. + 29 use_kerberos (bool): A flag to determine whether to use Kerberos for authentication. + 30 kdcHost (str): The Key Distribution Center (KDC) host for Kerberos authentication. + 31 debug (bool): A flag to enable debug output. + 32 smbClient (object): The SMB client object used for the connection. + 33 connected (bool): A flag to check the status of the connection. + 34 smb_share (str): The current SMB share in use. + 35 smb_path (str): The current path within the SMB share. + 36 + 37 Methods: + 38 __init__(address, domain, username, password, lmhash, nthash, use_kerberos=False, kdcHost=None, debug=False): + 39 Initializes the SMBSession with the specified parameters. + 40 init_smb_session(): + 41 Initializes the SMB session by connecting to the server and authenticating using the specified method. + 42 """ + 43 + 44 def __init__(self, address, domain, username, password, lmhash, nthash, use_kerberos=False, kdcHost=None, config=None): + 45 super(SMBSession, self).__init__() + 46 # Objects + 47 self.config = config + 48 + 49 # Target server + 50 self.address = address + 51 + 52 # Credentials + 53 self.domain = domain + 54 self.username = username + 55 self.password = password + 56 self.lmhash = lmhash + 57 self.nthash = nthash + 58 self.use_kerberos = use_kerberos + 59 self.kdcHost = kdcHost + 60 + 61 self.smbClient = None + 62 self.connected = False + 63 + 64 self.available_shares = {} + 65 self.smb_share = None + 66 self.smb_cwd = "" + 67 + 68 self.list_shares() + 69 + 70 # Connect and disconnect SMB session + 71 + 72 def init_smb_session(self): + 73 """ + 74 Initializes and establishes a session with the SMB server. + 75 + 76 This method sets up the SMB connection using either Kerberos or NTLM authentication based on the configuration. + 77 It attempts to connect to the SMB server specified by the `address` attribute and authenticate using the credentials provided during the object's initialization. + 78 + 79 The method will print debug information if the `debug` attribute is set to True. Upon successful connection and authentication, it sets the `connected` attribute to True. + 80 + 81 Returns: + 82 bool: True if the connection and authentication are successful, False otherwise. + 83 """ + 84 + 85 if self.config.debug: + 86 print("[debug] [>] Connecting to remote SMB server '%s' ... " % self.address) + 87 try: + 88 self.smbClient = impacket.smbconnection.SMBConnection( + 89 remoteName=self.address, + 90 remoteHost=self.address, + 91 sess_port=int(445) + 92 ) + 93 except OSError as err: + 94 print("[!] %s" % err) + 95 self.smbClient = None + 96 + 97 self.connected = False + 98 if self.smbClient is not None: + 99 if self.use_kerberos: +100 if self.config.debug: +101 print("[debug] [>] Authenticating as '%s\\%s' with kerberos ... " % (self.domain, self.username)) +102 self.connected = self.smbClient.kerberosLogin( +103 user=self.username, +104 password=self.password, +105 domain=self.domain, +106 lmhash=self.lmhash, +107 nthash=self.nthash, +108 aesKey=self.aesKey, +109 kdcHost=self.kdcHost +110 ) +111 +112 else: +113 if self.config.debug: +114 print("[debug] [>] Authenticating as '%s\\%s' with NTLM ... " % (self.domain, self.username)) +115 self.connected = self.smbClient.login( +116 user=self.username, +117 password=self.password, +118 domain=self.domain, +119 lmhash=self.lmhash, +120 nthash=self.nthash +121 ) +122 +123 if self.connected: +124 print("[+] Successfully authenticated to '%s' as '%s\\%s'!" % (self.address, self.domain, self.username)) +125 else: +126 print("[!] Failed to authenticate to '%s' as '%s\\%s'!" % (self.address, self.domain, self.username)) +127 +128 return self.connected +129 +130 def close_smb_session(self): +131 """ +132 Closes the current SMB session by disconnecting the SMB client. +133 +134 This method ensures that the SMB client connection is properly closed. It checks if the client is connected +135 and if so, it closes the connection and resets the connection status. +136 +137 Raises: +138 Exception: If the SMB client is not initialized or if there's an error during the disconnection process. +139 """ +140 +141 if self.smbClient is not None: +142 if self.connected: +143 self.smbClient.close() +144 self.connected = False +145 print("[+] SMB connection closed successfully.") +146 else: +147 print("[!] No active SMB connection to close.") +148 else: +149 raise Exception("SMB client is not initialized.") +150 +151 # Operations +152 +153 def get_file(self, path=None, keepRemotePath=False): +154 """ +155 Retrieves a file from the specified path on the SMB share. +156 +157 This method attempts to retrieve a file from the given path within the currently connected SMB share. +158 If the path points to a directory, it skips the retrieval. It handles file retrieval by creating a local +159 file object and writing the contents of the remote file to it using the SMB client's getFile method. +160 +161 Parameters: +162 path (str): The path of the file to retrieve. If None, uses the current smb_path. +163 +164 Returns: +165 None +166 """ +167 +168 tmp_file_path = self.smb_cwd + ntpath.sep + path +169 matches = self.smbClient.listPath( +170 shareName=self.smb_share, +171 path=tmp_file_path +172 ) +173 +174 for entry in matches: +175 if entry.is_directory(): +176 print("[>] Skipping '%s' because it is a directory." % tmp_file_path) +177 else: +178 try: +179 if ntpath.sep in path: +180 outputfile = ntpath.dirname(path) + ntpath.sep + entry.get_longname() +181 else: +182 outputfile = entry.get_longname() +183 f = LocalFileIO( +184 mode="wb", +185 path=outputfile, +186 expected_size=entry.get_filesize(), +187 debug=self.config.debug, +188 keepRemotePath=keepRemotePath +189 ) +190 self.smbClient.getFile( +191 shareName=self.smb_share, +192 pathName=tmp_file_path, +193 callback=f.write +194 ) +195 f.close() +196 except (BrokenPipeError, KeyboardInterrupt) as e: +197 f.close() +198 print("\x1b[v\x1b[o\r[!] Interrupted.") +199 self.close_smb_session() +200 self.init_smb_session() +201 +202 return None +203 +204 def get_file_recursively(self, path=None): +205 """ +206 Recursively retrieves files from a specified path on the SMB share. +207 +208 This method navigates through all directories starting from the given path, +209 and downloads all files found. It handles directories recursively, ensuring +210 that all nested files are retrieved. The method skips over directory entries +211 and handles errors gracefully, attempting to continue the operation where possible. +212 +213 Parameters: +214 path (str): The initial directory path from which to start the recursive file retrieval. +215 If None, it starts from the root of the configured SMB share. +216 """ +217 +218 def recurse_action(base_dir="", path=[]): +219 remote_smb_path = base_dir + ntpath.sep.join(path) +220 entries = self.smbClient.listPath( +221 shareName=self.smb_share, +222 path=remote_smb_path + '\\*' +223 ) +224 if len(entries) != 0: +225 files = [entry for entry in entries if not entry.is_directory()] +226 directories = [entry for entry in entries if entry.is_directory() and entry.get_longname() not in [".", ".."]] +227 +228 # Files +229 if len(files) != 0: +230 print("[>] Retrieving files of '%s'" % remote_smb_path) +231 for entry_file in files: +232 if not entry_file.is_directory(): +233 f = LocalFileIO( +234 mode="wb", +235 path=remote_smb_path + ntpath.sep + entry_file.get_longname(), +236 expected_size=entry_file.get_filesize(), +237 debug=self.config.debug +238 ) +239 try: +240 self.smbClient.getFile( +241 shareName=self.smb_share, +242 pathName=remote_smb_path + ntpath.sep + entry_file.get_longname(), +243 callback=f.write +244 ) +245 f.close() +246 except BrokenPipeError as err: +247 f.set_error(message="[bold red]Failed downloading '%s': %s" % (f.path, err)) +248 f.close(remove=True) +249 break +250 except Exception as err: +251 f.set_error(message="[bold red]Failed downloading '%s': %s" % (f.path, err)) +252 f.close(remove=True) +253 +254 # Directories +255 for entry_directory in directories: +256 if entry_directory.is_directory(): +257 recurse_action( +258 base_dir=self.smb_cwd, +259 path=path+[entry_directory.get_longname()] +260 ) +261 # Entrypoint +262 try: +263 recurse_action( +264 base_dir=self.smb_cwd, +265 path=[path] +266 ) +267 except (BrokenPipeError, KeyboardInterrupt) as e: +268 print("\x1b[v\x1b[o\r[!] Interrupted.") +269 self.close_smb_session() +270 self.init_smb_session() +271 +272 def info(self, share=True, server=True): +273 """ +274 Displays information about the server and optionally the shares. +275 +276 This method prints detailed information about the server's characteristics such as NetBIOS names, DNS details, OS information, and SMB capabilities. If the `share` parameter is set to True and a share is currently set, it will also attempt to display information about the share. +277 +278 Parameters: +279 share (bool): If True, display information about the current share. +280 server (bool): If True, display information about the server. +281 +282 Returns: +283 None +284 """ +285 +286 if server: +287 if self.config.no_colors: +288 print("[+] Server:") +289 print(" ├─NetBIOS:") +290 print(" │ ├─ NetBIOS Hostname ──────── : %s" % (self.smbClient.getServerName())) +291 print(" │ └─ NetBIOS Domain ────────── : %s" % (self.smbClient.getServerDomain())) +292 print(" ├─DNS:") +293 print(" │ ├─ DNS Hostname ──────────── : %s" % (self.smbClient.getServerDNSHostName())) +294 print(" │ └─ DNS Domain ────────────── : %s" % (self.smbClient.getServerDNSDomainName())) +295 print(" ├─OS:") +296 print(" │ ├─ OS Name ───────────────── : %s" % (self.smbClient.getServerOS())) +297 print(" │ └─ OS Version ────────────── : %s.%s.%s" % (self.smbClient.getServerOSMajor(), self.smbClient.getServerOSMinor(), self.smbClient.getServerOSBuild())) +298 print(" ├─Server:") +299 print(" │ ├─ Signing Required ──────── : %s" % (self.smbClient.isSigningRequired())) +300 print(" │ ├─ Login Required ────────── : %s" % (self.smbClient.isLoginRequired())) +301 print(" │ ├─ Supports NTLMv2 ───────── : %s" % (self.smbClient.doesSupportNTLMv2())) +302 MaxReadSize = self.smbClient.getIOCapabilities()["MaxReadSize"] +303 print(" │ ├─ Max size of read chunk ── : %d bytes (%s)" % (MaxReadSize, b_filesize(MaxReadSize))) +304 MaxWriteSize = self.smbClient.getIOCapabilities()["MaxWriteSize"] +305 print(" │ └─ Max size of write chunk ─ : %d bytes (%s)" % (MaxWriteSize, b_filesize(MaxWriteSize))) +306 print(" └─") +307 else: +308 print("[+] Server:") +309 print(" ├─NetBIOS:") +310 print(" │ ├─ \x1b[94mNetBIOS Hostname\x1b[0m \x1b[90m────────\x1b[0m : \x1b[93m%s\x1b[0m" % (self.smbClient.getServerName())) +311 print(" │ └─ \x1b[94mNetBIOS Domain\x1b[0m \x1b[90m──────────\x1b[0m : \x1b[93m%s\x1b[0m" % (self.smbClient.getServerDomain())) +312 print(" ├─DNS:") +313 print(" │ ├─ \x1b[94mDNS Hostname\x1b[0m \x1b[90m────────────\x1b[0m : \x1b[93m%s\x1b[0m" % (self.smbClient.getServerDNSHostName())) +314 print(" │ └─ \x1b[94mDNS Domain\x1b[0m \x1b[90m──────────────\x1b[0m : \x1b[93m%s\x1b[0m" % (self.smbClient.getServerDNSDomainName())) +315 print(" ├─OS:") +316 print(" │ ├─ \x1b[94mOS Name\x1b[0m \x1b[90m─────────────────\x1b[0m : \x1b[93m%s\x1b[0m" % (self.smbClient.getServerOS())) +317 print(" │ └─ \x1b[94mOS Version\x1b[0m \x1b[90m──────────────\x1b[0m : \x1b[93m%s.%s.%s\x1b[0m" % (self.smbClient.getServerOSMajor(), self.smbClient.getServerOSMinor(), self.smbClient.getServerOSBuild())) +318 print(" ├─Server:") +319 print(" │ ├─ \x1b[94mSigning Required\x1b[0m \x1b[90m────────\x1b[0m : \x1b[93m%s\x1b[0m" % (self.smbClient.isSigningRequired())) +320 print(" │ ├─ \x1b[94mLogin Required\x1b[0m \x1b[90m──────────\x1b[0m : \x1b[93m%s\x1b[0m" % (self.smbClient.isLoginRequired())) +321 print(" │ ├─ \x1b[94mSupports NTLMv2\x1b[0m \x1b[90m─────────\x1b[0m : \x1b[93m%s\x1b[0m" % (self.smbClient.doesSupportNTLMv2())) +322 MaxReadSize = self.smbClient.getIOCapabilities()["MaxReadSize"] +323 print(" │ ├─ \x1b[94mMax size of read chunk\x1b[0m \x1b[90m──\x1b[0m : \x1b[93m%d bytes (%s)\x1b[0m" % (MaxReadSize, b_filesize(MaxReadSize))) +324 MaxWriteSize = self.smbClient.getIOCapabilities()["MaxWriteSize"] +325 print(" │ └─ \x1b[94mMax size of write chunk\x1b[0m \x1b[90m─\x1b[0m : \x1b[93m%d bytes (%s)\x1b[0m" % (MaxWriteSize, b_filesize(MaxWriteSize))) +326 print(" └─") +327 +328 if share and self.smb_share is not None: +329 share_name = self.available_shares.get(self.smb_share.lower(), "")["name"] +330 share_comment = self.available_shares.get(self.smb_share.lower(), "")["comment"] +331 share_type = self.available_shares.get(self.smb_share.lower(), "")["type"] +332 share_type =', '.join([s.replace("STYPE_","") for s in share_type]) +333 share_rawtype = self.available_shares.get(self.smb_share.lower(), "")["rawtype"] +334 if self.config.no_colors: +335 print("\n[+] Share:") +336 print(" ├─ Name ──────────── : %s" % (share_name)) +337 print(" ├─ Description ───── : %s" % (share_comment)) +338 print(" ├─ Type ──────────── : %s" % (share_type)) +339 print(" └─ Raw type value ── : %s" % (share_rawtype)) +340 else: +341 print("\n[+] Share:") +342 print(" ├─ \x1b[94mName\x1b[0m \x1b[90m────────────\x1b[0m : \x1b[93m%s\x1b[0m" % (share_name)) +343 print(" ├─ \x1b[94mDescription\x1b[0m \x1b[90m─────\x1b[0m : \x1b[93m%s\x1b[0m" % (share_comment)) +344 print(" ├─ \x1b[94mType\x1b[0m \x1b[90m────────────\x1b[0m : \x1b[93m%s\x1b[0m" % (share_type)) +345 print(" └─ \x1b[94mRaw type value\x1b[0m \x1b[90m──\x1b[0m : \x1b[93m%s\x1b[0m" % (share_rawtype)) +346 +347 def list_contents(self, path=None): +348 """ +349 Lists the contents of a specified directory on the SMB share. +350 +351 This method retrieves the contents of a directory specified by `shareName` and `path`. If `shareName` or `path` +352 is not provided, it defaults to the instance's current SMB share or path. The method returns a dictionary with +353 the long names of the files and directories as keys and their respective SMB entry objects as values. +354 +355 Args: +356 shareName (str, optional): The name of the SMB share. Defaults to the current SMB share if None. +357 path (str, optional): The directory path to list contents from. Defaults to the current path if None. +358 +359 Returns: +360 dict: A dictionary with file and directory names as keys and their SMB entry objects as values. +361 """ +362 +363 if path is None or len(path) == 0: +364 path = self.smb_cwd +365 path = path.rstrip(ntpath.sep) + ntpath.sep + "*" +366 +367 contents = {} +368 entries = self.smbClient.listPath( +369 shareName=self.smb_share, +370 path=path +371 ) +372 for entry in entries: +373 contents[entry.get_longname()] = entry +374 +375 return contents +376 +377 def list_shares(self): +378 """ +379 Lists all the shares available on the connected SMB server. +380 +381 This method queries the SMB server to retrieve a list of all available shares. It populates the `shares` dictionary +382 with key-value pairs where the key is the share name and the value is a dictionary containing details about the share +383 such as its name, type, raw type, and any comments associated with the share. +384 +385 Returns: +386 dict: A dictionary containing information about each share available on the server. +387 """ +388 +389 self.available_shares = {} +390 +391 if self.connected: +392 if self.smbClient is not None: +393 resp = self.smbClient.listShares() +394 +395 for share in resp: +396 # SHARE_INFO_1 structure (lmshare.h) +397 # https://learn.microsoft.com/en-us/windows/win32/api/lmshare/ns-lmshare-share_info_1 +398 sharename = share["shi1_netname"][:-1] +399 sharecomment = share["shi1_remark"][:-1] +400 sharetype = share["shi1_type"] +401 +402 self.available_shares[sharename.lower()] = { +403 "name": sharename, +404 "type": STYPE_MASK(sharetype), +405 "rawtype": sharetype, +406 "comment": sharecomment +407 } +408 else: +409 print("[!] Error: SMBSession.smbClient is None.") +410 +411 return self.available_shares +412 +413 def mkdir(self, path=None): +414 """ +415 Creates a directory at the specified path on the SMB share. +416 +417 This method takes a path and attempts to create the directory structure on the SMB share. If the path includes +418 nested directories, it will create each directory in the sequence. If a directory already exists, it will skip +419 the creation for that directory without raising an error. +420 +421 Args: +422 path (str, optional): The full path of the directory to create on the SMB share. Defaults to None. +423 +424 Note: +425 The path should use forward slashes ('/') which will be converted to backslashes (ntpath.sep) for SMB compatibility. +426 """ +427 +428 if path is not None: +429 # Prepare path +430 path = path.replace('/',ntpath.sep) +431 if ntpath.sep in path: +432 path = path.strip(ntpath.sep).split(ntpath.sep) +433 else: +434 path = [path] +435 +436 # Create each dir in the path +437 for depth in range(1, len(path)+1): +438 tmp_path = ntpath.sep.join(path[:depth]) +439 try: +440 self.smbClient.createDirectory( +441 shareName=self.smb_share, +442 pathName=ntpath.normpath(self.smb_cwd + ntpath.sep + tmp_path + ntpath.sep) +443 ) +444 except impacket.smbconnection.SessionError as err: +445 if err.getErrorCode() == 0xc0000035: +446 # STATUS_OBJECT_NAME_COLLISION +447 # Remote directory already created, this is normal +448 # Src: https://github.com/fortra/impacket/blob/269ce69872f0e8f2188a80addb0c39fedfa6dcb8/impacket/nt_errors.py#L268C9-L268C19 +449 pass +450 else: +451 print("[!] Failed to create directory '%s': %s" % (tmp_path, err)) +452 if self.config.debug: +453 traceback.print_exc() +454 else: +455 pass +456 +457 def path_exists(self, path=None): +458 """ +459 Checks if the specified path exists on the SMB share. +460 +461 This method determines if a given path exists on the SMB share by attempting to list the contents of the path. +462 If the path listing is successful and returns one or more entries, the path is considered to exist. +463 +464 Args: +465 path (str, optional): The path to check on the SMB share. Defaults to None. +466 +467 Returns: +468 bool: True if the path exists, False otherwise or if an error occurs. +469 """ +470 +471 if path is not None: +472 path = path.replace('*','') +473 try: +474 contents = self.smbClient.listPath( +475 shareName=self.smb_share, +476 path=ntpath.normpath(self.smb_cwd + ntpath.sep + path + ntpath.sep) +477 ) +478 return (len(contents) != 0) +479 except Exception as e: +480 return False +481 else: +482 return False +483 +484 def path_isdir(self, pathFromRoot=None): +485 """ +486 Checks if the specified path is a directory on the SMB share. +487 +488 This method determines if a given path corresponds to a directory on the SMB share. It does this by listing the +489 contents of the path and filtering for entries that match the basename of the path and are marked as directories. +490 +491 Args: +492 path (str, optional): The path to check on the SMB share. Defaults to None. +493 +494 Returns: +495 bool: True if the path is a directory, False otherwise or if an error occurs. +496 """ +497 +498 if pathFromRoot is not None: +499 # Replace slashes if any +500 path = pathFromRoot.replace('/', ntpath.sep) +501 +502 # Strip wildcards to avoid injections +503 path = path.replace('*','') +504 +505 # Normalize path and strip leading backslash +506 path = ntpath.normpath(path + ntpath.sep).lstrip(ntpath.sep) +507 +508 if path.strip() in ['', '.', '..']: +509 # By defininition they exist on the filesystem +510 return True +511 else: +512 try: +513 contents = self.smbClient.listPath( +514 shareName=self.smb_share, +515 path=path+'*' +516 ) +517 # Filter on directories +518 contents = [ +519 c for c in contents +520 if c.get_longname() == ntpath.basename(path) and c.is_directory() +521 ] +522 return (len(contents) != 0) +523 except Exception as e: +524 return False +525 else: +526 return False +527 +528 def path_isfile(self, path=None): +529 """ +530 Checks if the specified path is a file on the SMB share. +531 +532 This method determines if a given path corresponds to a file on the SMB share. It does this by listing the +533 contents of the path and filtering for entries that match the basename of the path and are not marked as directories. +534 +535 Args: +536 path (str, optional): The path to check on the SMB share. Defaults to None. +537 +538 Returns: +539 bool: True if the path is a file, False otherwise or if an error occurs. +540 """ +541 +542 if path is not None: +543 path = path.replace('*','') +544 try: +545 contents = self.smbClient.listPath( +546 shareName=self.smb_share, +547 path=ntpath.normpath(self.smb_cwd + ntpath.sep + path + ntpath.sep) +548 ) +549 # Filter on files +550 contents = [ +551 c for c in contents +552 if c.get_longname() == ntpath.basename(path) and not c.is_directory() +553 ] +554 return (len(contents) != 0) +555 except Exception as e: +556 return False +557 else: +558 return False +559 +560 def ping_smb_session(self): +561 """ +562 Tests the connectivity to the SMB server by sending an echo command. +563 +564 This method attempts to send an echo command to the SMB server to check if the session is still active. +565 It updates the `connected` attribute of the class based on the success or failure of the echo command. +566 +567 Returns: +568 bool: True if the echo command succeeds (indicating the session is active), False otherwise. +569 """ +570 +571 try: +572 self.smbClient.getSMBServer().echo() +573 self.connected = True +574 except Exception as e: +575 self.connected = False +576 return self.connected +577 +578 def put_file(self, localpath=None): +579 """ +580 Uploads a single file to the SMB share. +581 +582 This method takes a local file path, opens the file, and uploads it to the SMB share at the specified path. +583 It handles exceptions such as broken pipe errors or keyboard interrupts by closing and reinitializing the SMB session. +584 General exceptions are caught and logged, with a traceback provided if debugging is enabled. +585 +586 Args: +587 localpath (str, optional): The local file path of the file to be uploaded. Defaults to None. +588 """ +589 +590 if os.path.exists(localpath): +591 if os.path.isfile(localpath): +592 try: +593 localfile = os.path.basename(localpath) +594 f = LocalFileIO( +595 mode="rb", +596 path=localpath, +597 debug=self.config.debug +598 ) +599 self.smbClient.putFile( +600 shareName=self.smb_share, +601 pathName=ntpath.normpath(self.smb_cwd + ntpath.sep + localfile + ntpath.sep), +602 callback=f.read +603 ) +604 f.close() +605 except (BrokenPipeError, KeyboardInterrupt) as err: +606 print("[!] Interrupted.") +607 self.close_smb_session() +608 self.init_smb_session() +609 except Exception as err: +610 print("[!] Failed to upload '%s': %s" % (localfile, err)) +611 if self.config.debug: +612 traceback.print_exc() +613 else: +614 print("[!] The specified localpath is a directory. Use 'put -r <directory>' instead.") +615 else: +616 print("[!] The specified localpath does not exist.") +617 +618 def put_file_recursively(self, localpath=None): +619 """ +620 Recursively uploads files from a specified local directory to the SMB share. +621 +622 This method walks through the given local directory and all its subdirectories, uploading each file to the +623 corresponding directory structure on the SMB share. It first checks if the local path is a directory. If it is, +624 it iterates over all files and directories within the local path, creating necessary directories on the SMB share +625 and uploading files. If the local path is not a directory, it prints an error message. +626 +627 Args: +628 localpath (str, optional): The local directory path from which files will be uploaded. Defaults to None. +629 """ +630 +631 if os.path.exists(localpath): +632 if os.path.isfile(localpath): +633 # Iterate over all files and directories within the local path +634 local_files = {} +635 for root, dirs, files in os.walk(localpath): +636 if len(files) != 0: +637 local_files[root] = files +638 +639 # Iterate over the found files +640 for local_dir_path in sorted(local_files.keys()): +641 print("[>] Putting files of '%s'" % local_dir_path) +642 +643 # Create remote directory +644 remote_dir_path = local_dir_path.replace(os.path.sep, ntpath.sep) +645 self.mkdir( +646 path=ntpath.normpath(self.smb_cwd + ntpath.sep + remote_dir_path + ntpath.sep) +647 ) +648 +649 for local_file_path in local_files[local_dir_path]: +650 try: +651 f = LocalFileIO( +652 mode="rb", +653 path=local_dir_path + os.path.sep + local_file_path, +654 debug=self.config.debug +655 ) +656 self.smbClient.putFile( +657 shareName=self.smb_share, +658 pathName=ntpath.normpath(self.smb_cwd + ntpath.sep + remote_dir_path + ntpath.sep + local_file_path), +659 callback=f.read +660 ) +661 f.close() +662 +663 except BrokenPipeError as err: +664 f.set_error(message="[bold red]Failed uploading '%s': %s" % (f.path, err)) +665 f.close(remove=True) +666 break +667 except Exception as err: +668 f.set_error(message="[bold red]Failed uploading '%s': %s" % (f.path, err)) +669 f.close(remove=True) +670 else: +671 print("[!] The specified localpath is a file. Use 'put <file>' instead.") +672 else: +673 print("[!] The specified localpath does not exist.") +674 +675 def rmdir(self, path=None): +676 """ +677 Removes a directory from the SMB share at the specified path. +678 +679 This method attempts to delete a directory located at the given path on the SMB share. If the operation fails, +680 it prints an error message indicating the failure and the reason. If debugging is enabled, it also prints +681 the stack trace of the exception. +682 +683 Args: +684 path (str, optional): The path of the directory to be removed on the SMB share. Defaults to None. +685 """ +686 try: +687 self.smbClient.deleteDirectory( +688 shareName=self.smb_share, +689 pathName=ntpath.normpath(self.smb_cwd + ntpath.sep + path), +690 ) +691 except Exception as err: +692 print("[!] Failed to remove directory '%s': %s" % (path, err)) +693 if self.config.debug: +694 traceback.print_exc() +695 +696 def rm(self, path=None): +697 """ +698 Removes a file from the SMB share at the specified path. +699 +700 This method attempts to delete a file located at the given path on the SMB share. If the operation fails, +701 it prints an error message indicating the failure and the reason. If debugging is enabled, it also prints +702 the stack trace of the exception. +703 +704 Args: +705 path (str, optional): The path of the file to be removed on the SMB share. Defaults to None. +706 """ +707 try: +708 self.smbClient.deleteFile( +709 shareName=self.smb_share, +710 pathName=ntpath.normpath(self.smb_cwd + ntpath.sep + path), +711 ) +712 except Exception as err: +713 print("[!] Failed to remove file '%s': %s" % (path, err)) +714 if self.config.debug: +715 traceback.print_exc() +716 +717 def tree(self, path=None): +718 """ +719 Recursively lists the directory structure of the SMB share starting from the specified path. +720 +721 This function prints a visual representation of the directory tree of the remote SMB share. It uses +722 recursion to navigate through directories and lists all files and subdirectories in each directory. +723 The output is color-coded and formatted to enhance readability, with directories highlighted in cyan. +724 +725 Args: +726 path (str, optional): The starting path on the SMB share from which to begin listing the tree. +727 Defaults to the root of the current share. +728 """ +729 +730 def recurse_action(base_dir="", path=[], prompt=[]): +731 bars = ["│ ", "├── ", "└── "] +732 +733 remote_smb_path = ntpath.normpath(base_dir + ntpath.sep + ntpath.sep.join(path)) +734 +735 entries = [] +736 try: +737 entries = self.smbClient.listPath( +738 shareName=self.smb_share, +739 path=remote_smb_path+'\\*' +740 ) +741 except impacket.smbconnection.SessionError as err: +742 code, const, text = err.getErrorCode(), err.getErrorString()[0], err.getErrorString()[1] +743 errmsg = "Error 0x%08x (%s): %s" % (code, const, text) +744 if self.config.no_colors: +745 print("%s%s" % (''.join(prompt+[bars[2]]), errmsg)) +746 else: +747 print("%s\x1b[1;91m%s\x1b[0m" % (''.join(prompt+[bars[2]]), errmsg)) +748 return +749 +750 entries = [e for e in entries if e.get_longname() not in [".", ".."]] +751 entries = sorted(entries, key=lambda x:x.get_longname()) +752 +753 # +754 if len(entries) > 1: +755 index = 0 +756 for entry in entries: +757 index += 1 +758 # This is the first entry +759 if index == 0: +760 if entry.is_directory(): +761 if self.config.no_colors: +762 print("%s%s\\" % (''.join(prompt+[bars[1]]), entry.get_longname())) +763 else: +764 print("%s\x1b[1;96m%s\x1b[0m\\" % (''.join(prompt+[bars[1]]), entry.get_longname())) +765 recurse_action( +766 base_dir=base_dir, +767 path=path+[entry.get_longname()], +768 prompt=prompt+["│ "] +769 ) +770 else: +771 if self.config.no_colors: +772 print("%s%s" % (''.join(prompt+[bars[1]]), entry.get_longname())) +773 else: +774 print("%s\x1b[1m%s\x1b[0m" % (''.join(prompt+[bars[1]]), entry.get_longname())) +775 +776 # This is the last entry +777 elif index == len(entries): +778 if entry.is_directory(): +779 if self.config.no_colors: +780 print("%s%s\\" % (''.join(prompt+[bars[2]]), entry.get_longname())) +781 else: +782 print("%s\x1b[1;96m%s\x1b[0m\\" % (''.join(prompt+[bars[2]]), entry.get_longname())) +783 recurse_action( +784 base_dir=base_dir, +785 path=path+[entry.get_longname()], +786 prompt=prompt+[" "] +787 ) +788 else: +789 if self.config.no_colors: +790 print("%s%s" % (''.join(prompt+[bars[2]]), entry.get_longname())) +791 else: +792 print("%s\x1b[1m%s\x1b[0m" % (''.join(prompt+[bars[2]]), entry.get_longname())) +793 +794 # These are entries in the middle +795 else: +796 if entry.is_directory(): +797 if self.config.no_colors: +798 print("%s%s\\" % (''.join(prompt+[bars[1]]), entry.get_longname())) +799 else: +800 print("%s\x1b[1;96m%s\x1b[0m\\" % (''.join(prompt+[bars[1]]), entry.get_longname())) +801 recurse_action( +802 base_dir=base_dir, +803 path=path+[entry.get_longname()], +804 prompt=prompt+["│ "] +805 ) +806 else: +807 if self.config.no_colors: +808 print("%s%s" % (''.join(prompt+[bars[1]]), entry.get_longname())) +809 else: +810 print("%s\x1b[1m%s\x1b[0m" % (''.join(prompt+[bars[1]]), entry.get_longname())) +811 +812 # +813 elif len(entries) == 1: +814 entry = entries[0] +815 if entry.is_directory(): +816 if self.config.no_colors: +817 print("%s%s\\" % (''.join(prompt+[bars[2]]), entry.get_longname())) +818 else: +819 print("%s\x1b[1;96m%s\x1b[0m\\" % (''.join(prompt+[bars[2]]), entry.get_longname())) +820 recurse_action( +821 base_dir=base_dir, +822 path=path+[entry.get_longname()], +823 prompt=prompt+[" "] +824 ) +825 else: +826 if self.config.no_colors: +827 print("%s%s" % (''.join(prompt+[bars[2]]), entry.get_longname())) +828 else: +829 print("%s\x1b[1m%s\x1b[0m" % (''.join(prompt+[bars[2]]), entry.get_longname())) +830 +831 # Entrypoint +832 try: +833 if self.config.no_colors: +834 print("%s\\" % path) +835 else: +836 print("\x1b[1;96m%s\x1b[0m\\" % path) +837 recurse_action( +838 base_dir=self.smb_cwd, +839 path=[path], +840 prompt=[""] +841 ) +842 except (BrokenPipeError, KeyboardInterrupt) as e: +843 print("[!] Interrupted.") +844 self.close_smb_session() +845 self.init_smb_session() +846 +847 # Setter / Getter +848 +849 def set_share(self, shareName): +850 """ +851 Sets the current SMB share to the specified share name. +852 +853 This method updates the SMB session to use the specified share name. It checks if the share name is valid +854 and updates the smb_share attribute of the SMBSession instance. +855 +856 Parameters: +857 shareName (str): The name of the share to set as the current SMB share. +858 +859 Raises: +860 ValueError: If the shareName is None or an empty string. +861 """ +862 +863 if shareName is not None: +864 self.smb_share = shareName +865 +866 def set_cwd(self, path=None): +867 """ +868 Sets the current working directory on the SMB share to the specified path. +869 +870 This method updates the current working directory (cwd) of the SMB session to the given path if it is a valid directory. +871 If the specified path is not a directory, the cwd remains unchanged. +872 +873 Parameters: +874 path (str): The path to set as the current working directory. +875 +876 Raises: +877 ValueError: If the specified path is not a directory. +878 """ +879 +880 if path is not None: +881 # Set path separators to ntpath sep +882 if '/' in path: +883 path = path.replace('/', ntpath.sep) +884 +885 if path.startswith(ntpath.sep): +886 # Absolute path +887 path = path + ntpath.sep +888 else: +889 # Relative path to the CWD +890 if len(self.smb_cwd) == 0: +891 path = path + ntpath.sep +892 else: +893 path = self.smb_cwd + ntpath.sep + path +894 +895 # Path normalization +896 path = ntpath.normpath(path) +897 path = re.sub(r'\\+', r'\\', path) +898 +899 if path in ["", ".", ".."]: +900 self.smb_cwd = "" +901 else: +902 if self.path_isdir(pathFromRoot=path.strip(ntpath.sep)): +903 # Path exists on the remote +904 self.smb_cwd = ntpath.normpath(path) +905 else: +906 # Path does not exists or is not a directory on the remote +907 print("[!] Remote directory '%s' does not exist." % path) +
18class SMBSession(object): + 19 """ + 20 Class SMBSession is designed to handle the session management for SMB (Server Message Block) protocol connections. + 21 It provides functionalities to connect to an SMB server, authenticate using either NTLM or Kerberos, and manage SMB shares. + 22 + 23 Attributes: + 24 address (str): The IP address or hostname of the SMB server. + 25 domain (str): The domain name for SMB server authentication. + 26 username (str): The username for SMB server authentication. + 27 password (str): The password for SMB server authentication. + 28 lmhash (str): The LM hash of the user's password, if available. + 29 nthash (str): The NT hash of the user's password, if available. + 30 use_kerberos (bool): A flag to determine whether to use Kerberos for authentication. + 31 kdcHost (str): The Key Distribution Center (KDC) host for Kerberos authentication. + 32 debug (bool): A flag to enable debug output. + 33 smbClient (object): The SMB client object used for the connection. + 34 connected (bool): A flag to check the status of the connection. + 35 smb_share (str): The current SMB share in use. + 36 smb_path (str): The current path within the SMB share. + 37 + 38 Methods: + 39 __init__(address, domain, username, password, lmhash, nthash, use_kerberos=False, kdcHost=None, debug=False): + 40 Initializes the SMBSession with the specified parameters. + 41 init_smb_session(): + 42 Initializes the SMB session by connecting to the server and authenticating using the specified method. + 43 """ + 44 + 45 def __init__(self, address, domain, username, password, lmhash, nthash, use_kerberos=False, kdcHost=None, config=None): + 46 super(SMBSession, self).__init__() + 47 # Objects + 48 self.config = config + 49 + 50 # Target server + 51 self.address = address + 52 + 53 # Credentials + 54 self.domain = domain + 55 self.username = username + 56 self.password = password + 57 self.lmhash = lmhash + 58 self.nthash = nthash + 59 self.use_kerberos = use_kerberos + 60 self.kdcHost = kdcHost + 61 + 62 self.smbClient = None + 63 self.connected = False + 64 + 65 self.available_shares = {} + 66 self.smb_share = None + 67 self.smb_cwd = "" + 68 + 69 self.list_shares() + 70 + 71 # Connect and disconnect SMB session + 72 + 73 def init_smb_session(self): + 74 """ + 75 Initializes and establishes a session with the SMB server. + 76 + 77 This method sets up the SMB connection using either Kerberos or NTLM authentication based on the configuration. + 78 It attempts to connect to the SMB server specified by the `address` attribute and authenticate using the credentials provided during the object's initialization. + 79 + 80 The method will print debug information if the `debug` attribute is set to True. Upon successful connection and authentication, it sets the `connected` attribute to True. + 81 + 82 Returns: + 83 bool: True if the connection and authentication are successful, False otherwise. + 84 """ + 85 + 86 if self.config.debug: + 87 print("[debug] [>] Connecting to remote SMB server '%s' ... " % self.address) + 88 try: + 89 self.smbClient = impacket.smbconnection.SMBConnection( + 90 remoteName=self.address, + 91 remoteHost=self.address, + 92 sess_port=int(445) + 93 ) + 94 except OSError as err: + 95 print("[!] %s" % err) + 96 self.smbClient = None + 97 + 98 self.connected = False + 99 if self.smbClient is not None: +100 if self.use_kerberos: +101 if self.config.debug: +102 print("[debug] [>] Authenticating as '%s\\%s' with kerberos ... " % (self.domain, self.username)) +103 self.connected = self.smbClient.kerberosLogin( +104 user=self.username, +105 password=self.password, +106 domain=self.domain, +107 lmhash=self.lmhash, +108 nthash=self.nthash, +109 aesKey=self.aesKey, +110 kdcHost=self.kdcHost +111 ) +112 +113 else: +114 if self.config.debug: +115 print("[debug] [>] Authenticating as '%s\\%s' with NTLM ... " % (self.domain, self.username)) +116 self.connected = self.smbClient.login( +117 user=self.username, +118 password=self.password, +119 domain=self.domain, +120 lmhash=self.lmhash, +121 nthash=self.nthash +122 ) +123 +124 if self.connected: +125 print("[+] Successfully authenticated to '%s' as '%s\\%s'!" % (self.address, self.domain, self.username)) +126 else: +127 print("[!] Failed to authenticate to '%s' as '%s\\%s'!" % (self.address, self.domain, self.username)) +128 +129 return self.connected +130 +131 def close_smb_session(self): +132 """ +133 Closes the current SMB session by disconnecting the SMB client. +134 +135 This method ensures that the SMB client connection is properly closed. It checks if the client is connected +136 and if so, it closes the connection and resets the connection status. +137 +138 Raises: +139 Exception: If the SMB client is not initialized or if there's an error during the disconnection process. +140 """ +141 +142 if self.smbClient is not None: +143 if self.connected: +144 self.smbClient.close() +145 self.connected = False +146 print("[+] SMB connection closed successfully.") +147 else: +148 print("[!] No active SMB connection to close.") +149 else: +150 raise Exception("SMB client is not initialized.") +151 +152 # Operations +153 +154 def get_file(self, path=None, keepRemotePath=False): +155 """ +156 Retrieves a file from the specified path on the SMB share. +157 +158 This method attempts to retrieve a file from the given path within the currently connected SMB share. +159 If the path points to a directory, it skips the retrieval. It handles file retrieval by creating a local +160 file object and writing the contents of the remote file to it using the SMB client's getFile method. +161 +162 Parameters: +163 path (str): The path of the file to retrieve. If None, uses the current smb_path. +164 +165 Returns: +166 None +167 """ +168 +169 tmp_file_path = self.smb_cwd + ntpath.sep + path +170 matches = self.smbClient.listPath( +171 shareName=self.smb_share, +172 path=tmp_file_path +173 ) +174 +175 for entry in matches: +176 if entry.is_directory(): +177 print("[>] Skipping '%s' because it is a directory." % tmp_file_path) +178 else: +179 try: +180 if ntpath.sep in path: +181 outputfile = ntpath.dirname(path) + ntpath.sep + entry.get_longname() +182 else: +183 outputfile = entry.get_longname() +184 f = LocalFileIO( +185 mode="wb", +186 path=outputfile, +187 expected_size=entry.get_filesize(), +188 debug=self.config.debug, +189 keepRemotePath=keepRemotePath +190 ) +191 self.smbClient.getFile( +192 shareName=self.smb_share, +193 pathName=tmp_file_path, +194 callback=f.write +195 ) +196 f.close() +197 except (BrokenPipeError, KeyboardInterrupt) as e: +198 f.close() +199 print("\x1b[v\x1b[o\r[!] Interrupted.") +200 self.close_smb_session() +201 self.init_smb_session() +202 +203 return None +204 +205 def get_file_recursively(self, path=None): +206 """ +207 Recursively retrieves files from a specified path on the SMB share. +208 +209 This method navigates through all directories starting from the given path, +210 and downloads all files found. It handles directories recursively, ensuring +211 that all nested files are retrieved. The method skips over directory entries +212 and handles errors gracefully, attempting to continue the operation where possible. +213 +214 Parameters: +215 path (str): The initial directory path from which to start the recursive file retrieval. +216 If None, it starts from the root of the configured SMB share. +217 """ +218 +219 def recurse_action(base_dir="", path=[]): +220 remote_smb_path = base_dir + ntpath.sep.join(path) +221 entries = self.smbClient.listPath( +222 shareName=self.smb_share, +223 path=remote_smb_path + '\\*' +224 ) +225 if len(entries) != 0: +226 files = [entry for entry in entries if not entry.is_directory()] +227 directories = [entry for entry in entries if entry.is_directory() and entry.get_longname() not in [".", ".."]] +228 +229 # Files +230 if len(files) != 0: +231 print("[>] Retrieving files of '%s'" % remote_smb_path) +232 for entry_file in files: +233 if not entry_file.is_directory(): +234 f = LocalFileIO( +235 mode="wb", +236 path=remote_smb_path + ntpath.sep + entry_file.get_longname(), +237 expected_size=entry_file.get_filesize(), +238 debug=self.config.debug +239 ) +240 try: +241 self.smbClient.getFile( +242 shareName=self.smb_share, +243 pathName=remote_smb_path + ntpath.sep + entry_file.get_longname(), +244 callback=f.write +245 ) +246 f.close() +247 except BrokenPipeError as err: +248 f.set_error(message="[bold red]Failed downloading '%s': %s" % (f.path, err)) +249 f.close(remove=True) +250 break +251 except Exception as err: +252 f.set_error(message="[bold red]Failed downloading '%s': %s" % (f.path, err)) +253 f.close(remove=True) +254 +255 # Directories +256 for entry_directory in directories: +257 if entry_directory.is_directory(): +258 recurse_action( +259 base_dir=self.smb_cwd, +260 path=path+[entry_directory.get_longname()] +261 ) +262 # Entrypoint +263 try: +264 recurse_action( +265 base_dir=self.smb_cwd, +266 path=[path] +267 ) +268 except (BrokenPipeError, KeyboardInterrupt) as e: +269 print("\x1b[v\x1b[o\r[!] Interrupted.") +270 self.close_smb_session() +271 self.init_smb_session() +272 +273 def info(self, share=True, server=True): +274 """ +275 Displays information about the server and optionally the shares. +276 +277 This method prints detailed information about the server's characteristics such as NetBIOS names, DNS details, OS information, and SMB capabilities. If the `share` parameter is set to True and a share is currently set, it will also attempt to display information about the share. +278 +279 Parameters: +280 share (bool): If True, display information about the current share. +281 server (bool): If True, display information about the server. +282 +283 Returns: +284 None +285 """ +286 +287 if server: +288 if self.config.no_colors: +289 print("[+] Server:") +290 print(" ├─NetBIOS:") +291 print(" │ ├─ NetBIOS Hostname ──────── : %s" % (self.smbClient.getServerName())) +292 print(" │ └─ NetBIOS Domain ────────── : %s" % (self.smbClient.getServerDomain())) +293 print(" ├─DNS:") +294 print(" │ ├─ DNS Hostname ──────────── : %s" % (self.smbClient.getServerDNSHostName())) +295 print(" │ └─ DNS Domain ────────────── : %s" % (self.smbClient.getServerDNSDomainName())) +296 print(" ├─OS:") +297 print(" │ ├─ OS Name ───────────────── : %s" % (self.smbClient.getServerOS())) +298 print(" │ └─ OS Version ────────────── : %s.%s.%s" % (self.smbClient.getServerOSMajor(), self.smbClient.getServerOSMinor(), self.smbClient.getServerOSBuild())) +299 print(" ├─Server:") +300 print(" │ ├─ Signing Required ──────── : %s" % (self.smbClient.isSigningRequired())) +301 print(" │ ├─ Login Required ────────── : %s" % (self.smbClient.isLoginRequired())) +302 print(" │ ├─ Supports NTLMv2 ───────── : %s" % (self.smbClient.doesSupportNTLMv2())) +303 MaxReadSize = self.smbClient.getIOCapabilities()["MaxReadSize"] +304 print(" │ ├─ Max size of read chunk ── : %d bytes (%s)" % (MaxReadSize, b_filesize(MaxReadSize))) +305 MaxWriteSize = self.smbClient.getIOCapabilities()["MaxWriteSize"] +306 print(" │ └─ Max size of write chunk ─ : %d bytes (%s)" % (MaxWriteSize, b_filesize(MaxWriteSize))) +307 print(" └─") +308 else: +309 print("[+] Server:") +310 print(" ├─NetBIOS:") +311 print(" │ ├─ \x1b[94mNetBIOS Hostname\x1b[0m \x1b[90m────────\x1b[0m : \x1b[93m%s\x1b[0m" % (self.smbClient.getServerName())) +312 print(" │ └─ \x1b[94mNetBIOS Domain\x1b[0m \x1b[90m──────────\x1b[0m : \x1b[93m%s\x1b[0m" % (self.smbClient.getServerDomain())) +313 print(" ├─DNS:") +314 print(" │ ├─ \x1b[94mDNS Hostname\x1b[0m \x1b[90m────────────\x1b[0m : \x1b[93m%s\x1b[0m" % (self.smbClient.getServerDNSHostName())) +315 print(" │ └─ \x1b[94mDNS Domain\x1b[0m \x1b[90m──────────────\x1b[0m : \x1b[93m%s\x1b[0m" % (self.smbClient.getServerDNSDomainName())) +316 print(" ├─OS:") +317 print(" │ ├─ \x1b[94mOS Name\x1b[0m \x1b[90m─────────────────\x1b[0m : \x1b[93m%s\x1b[0m" % (self.smbClient.getServerOS())) +318 print(" │ └─ \x1b[94mOS Version\x1b[0m \x1b[90m──────────────\x1b[0m : \x1b[93m%s.%s.%s\x1b[0m" % (self.smbClient.getServerOSMajor(), self.smbClient.getServerOSMinor(), self.smbClient.getServerOSBuild())) +319 print(" ├─Server:") +320 print(" │ ├─ \x1b[94mSigning Required\x1b[0m \x1b[90m────────\x1b[0m : \x1b[93m%s\x1b[0m" % (self.smbClient.isSigningRequired())) +321 print(" │ ├─ \x1b[94mLogin Required\x1b[0m \x1b[90m──────────\x1b[0m : \x1b[93m%s\x1b[0m" % (self.smbClient.isLoginRequired())) +322 print(" │ ├─ \x1b[94mSupports NTLMv2\x1b[0m \x1b[90m─────────\x1b[0m : \x1b[93m%s\x1b[0m" % (self.smbClient.doesSupportNTLMv2())) +323 MaxReadSize = self.smbClient.getIOCapabilities()["MaxReadSize"] +324 print(" │ ├─ \x1b[94mMax size of read chunk\x1b[0m \x1b[90m──\x1b[0m : \x1b[93m%d bytes (%s)\x1b[0m" % (MaxReadSize, b_filesize(MaxReadSize))) +325 MaxWriteSize = self.smbClient.getIOCapabilities()["MaxWriteSize"] +326 print(" │ └─ \x1b[94mMax size of write chunk\x1b[0m \x1b[90m─\x1b[0m : \x1b[93m%d bytes (%s)\x1b[0m" % (MaxWriteSize, b_filesize(MaxWriteSize))) +327 print(" └─") +328 +329 if share and self.smb_share is not None: +330 share_name = self.available_shares.get(self.smb_share.lower(), "")["name"] +331 share_comment = self.available_shares.get(self.smb_share.lower(), "")["comment"] +332 share_type = self.available_shares.get(self.smb_share.lower(), "")["type"] +333 share_type =', '.join([s.replace("STYPE_","") for s in share_type]) +334 share_rawtype = self.available_shares.get(self.smb_share.lower(), "")["rawtype"] +335 if self.config.no_colors: +336 print("\n[+] Share:") +337 print(" ├─ Name ──────────── : %s" % (share_name)) +338 print(" ├─ Description ───── : %s" % (share_comment)) +339 print(" ├─ Type ──────────── : %s" % (share_type)) +340 print(" └─ Raw type value ── : %s" % (share_rawtype)) +341 else: +342 print("\n[+] Share:") +343 print(" ├─ \x1b[94mName\x1b[0m \x1b[90m────────────\x1b[0m : \x1b[93m%s\x1b[0m" % (share_name)) +344 print(" ├─ \x1b[94mDescription\x1b[0m \x1b[90m─────\x1b[0m : \x1b[93m%s\x1b[0m" % (share_comment)) +345 print(" ├─ \x1b[94mType\x1b[0m \x1b[90m────────────\x1b[0m : \x1b[93m%s\x1b[0m" % (share_type)) +346 print(" └─ \x1b[94mRaw type value\x1b[0m \x1b[90m──\x1b[0m : \x1b[93m%s\x1b[0m" % (share_rawtype)) +347 +348 def list_contents(self, path=None): +349 """ +350 Lists the contents of a specified directory on the SMB share. +351 +352 This method retrieves the contents of a directory specified by `shareName` and `path`. If `shareName` or `path` +353 is not provided, it defaults to the instance's current SMB share or path. The method returns a dictionary with +354 the long names of the files and directories as keys and their respective SMB entry objects as values. +355 +356 Args: +357 shareName (str, optional): The name of the SMB share. Defaults to the current SMB share if None. +358 path (str, optional): The directory path to list contents from. Defaults to the current path if None. +359 +360 Returns: +361 dict: A dictionary with file and directory names as keys and their SMB entry objects as values. +362 """ +363 +364 if path is None or len(path) == 0: +365 path = self.smb_cwd +366 path = path.rstrip(ntpath.sep) + ntpath.sep + "*" +367 +368 contents = {} +369 entries = self.smbClient.listPath( +370 shareName=self.smb_share, +371 path=path +372 ) +373 for entry in entries: +374 contents[entry.get_longname()] = entry +375 +376 return contents +377 +378 def list_shares(self): +379 """ +380 Lists all the shares available on the connected SMB server. +381 +382 This method queries the SMB server to retrieve a list of all available shares. It populates the `shares` dictionary +383 with key-value pairs where the key is the share name and the value is a dictionary containing details about the share +384 such as its name, type, raw type, and any comments associated with the share. +385 +386 Returns: +387 dict: A dictionary containing information about each share available on the server. +388 """ +389 +390 self.available_shares = {} +391 +392 if self.connected: +393 if self.smbClient is not None: +394 resp = self.smbClient.listShares() +395 +396 for share in resp: +397 # SHARE_INFO_1 structure (lmshare.h) +398 # https://learn.microsoft.com/en-us/windows/win32/api/lmshare/ns-lmshare-share_info_1 +399 sharename = share["shi1_netname"][:-1] +400 sharecomment = share["shi1_remark"][:-1] +401 sharetype = share["shi1_type"] +402 +403 self.available_shares[sharename.lower()] = { +404 "name": sharename, +405 "type": STYPE_MASK(sharetype), +406 "rawtype": sharetype, +407 "comment": sharecomment +408 } +409 else: +410 print("[!] Error: SMBSession.smbClient is None.") +411 +412 return self.available_shares +413 +414 def mkdir(self, path=None): +415 """ +416 Creates a directory at the specified path on the SMB share. +417 +418 This method takes a path and attempts to create the directory structure on the SMB share. If the path includes +419 nested directories, it will create each directory in the sequence. If a directory already exists, it will skip +420 the creation for that directory without raising an error. +421 +422 Args: +423 path (str, optional): The full path of the directory to create on the SMB share. Defaults to None. +424 +425 Note: +426 The path should use forward slashes ('/') which will be converted to backslashes (ntpath.sep) for SMB compatibility. +427 """ +428 +429 if path is not None: +430 # Prepare path +431 path = path.replace('/',ntpath.sep) +432 if ntpath.sep in path: +433 path = path.strip(ntpath.sep).split(ntpath.sep) +434 else: +435 path = [path] +436 +437 # Create each dir in the path +438 for depth in range(1, len(path)+1): +439 tmp_path = ntpath.sep.join(path[:depth]) +440 try: +441 self.smbClient.createDirectory( +442 shareName=self.smb_share, +443 pathName=ntpath.normpath(self.smb_cwd + ntpath.sep + tmp_path + ntpath.sep) +444 ) +445 except impacket.smbconnection.SessionError as err: +446 if err.getErrorCode() == 0xc0000035: +447 # STATUS_OBJECT_NAME_COLLISION +448 # Remote directory already created, this is normal +449 # Src: https://github.com/fortra/impacket/blob/269ce69872f0e8f2188a80addb0c39fedfa6dcb8/impacket/nt_errors.py#L268C9-L268C19 +450 pass +451 else: +452 print("[!] Failed to create directory '%s': %s" % (tmp_path, err)) +453 if self.config.debug: +454 traceback.print_exc() +455 else: +456 pass +457 +458 def path_exists(self, path=None): +459 """ +460 Checks if the specified path exists on the SMB share. +461 +462 This method determines if a given path exists on the SMB share by attempting to list the contents of the path. +463 If the path listing is successful and returns one or more entries, the path is considered to exist. +464 +465 Args: +466 path (str, optional): The path to check on the SMB share. Defaults to None. +467 +468 Returns: +469 bool: True if the path exists, False otherwise or if an error occurs. +470 """ +471 +472 if path is not None: +473 path = path.replace('*','') +474 try: +475 contents = self.smbClient.listPath( +476 shareName=self.smb_share, +477 path=ntpath.normpath(self.smb_cwd + ntpath.sep + path + ntpath.sep) +478 ) +479 return (len(contents) != 0) +480 except Exception as e: +481 return False +482 else: +483 return False +484 +485 def path_isdir(self, pathFromRoot=None): +486 """ +487 Checks if the specified path is a directory on the SMB share. +488 +489 This method determines if a given path corresponds to a directory on the SMB share. It does this by listing the +490 contents of the path and filtering for entries that match the basename of the path and are marked as directories. +491 +492 Args: +493 path (str, optional): The path to check on the SMB share. Defaults to None. +494 +495 Returns: +496 bool: True if the path is a directory, False otherwise or if an error occurs. +497 """ +498 +499 if pathFromRoot is not None: +500 # Replace slashes if any +501 path = pathFromRoot.replace('/', ntpath.sep) +502 +503 # Strip wildcards to avoid injections +504 path = path.replace('*','') +505 +506 # Normalize path and strip leading backslash +507 path = ntpath.normpath(path + ntpath.sep).lstrip(ntpath.sep) +508 +509 if path.strip() in ['', '.', '..']: +510 # By defininition they exist on the filesystem +511 return True +512 else: +513 try: +514 contents = self.smbClient.listPath( +515 shareName=self.smb_share, +516 path=path+'*' +517 ) +518 # Filter on directories +519 contents = [ +520 c for c in contents +521 if c.get_longname() == ntpath.basename(path) and c.is_directory() +522 ] +523 return (len(contents) != 0) +524 except Exception as e: +525 return False +526 else: +527 return False +528 +529 def path_isfile(self, path=None): +530 """ +531 Checks if the specified path is a file on the SMB share. +532 +533 This method determines if a given path corresponds to a file on the SMB share. It does this by listing the +534 contents of the path and filtering for entries that match the basename of the path and are not marked as directories. +535 +536 Args: +537 path (str, optional): The path to check on the SMB share. Defaults to None. +538 +539 Returns: +540 bool: True if the path is a file, False otherwise or if an error occurs. +541 """ +542 +543 if path is not None: +544 path = path.replace('*','') +545 try: +546 contents = self.smbClient.listPath( +547 shareName=self.smb_share, +548 path=ntpath.normpath(self.smb_cwd + ntpath.sep + path + ntpath.sep) +549 ) +550 # Filter on files +551 contents = [ +552 c for c in contents +553 if c.get_longname() == ntpath.basename(path) and not c.is_directory() +554 ] +555 return (len(contents) != 0) +556 except Exception as e: +557 return False +558 else: +559 return False +560 +561 def ping_smb_session(self): +562 """ +563 Tests the connectivity to the SMB server by sending an echo command. +564 +565 This method attempts to send an echo command to the SMB server to check if the session is still active. +566 It updates the `connected` attribute of the class based on the success or failure of the echo command. +567 +568 Returns: +569 bool: True if the echo command succeeds (indicating the session is active), False otherwise. +570 """ +571 +572 try: +573 self.smbClient.getSMBServer().echo() +574 self.connected = True +575 except Exception as e: +576 self.connected = False +577 return self.connected +578 +579 def put_file(self, localpath=None): +580 """ +581 Uploads a single file to the SMB share. +582 +583 This method takes a local file path, opens the file, and uploads it to the SMB share at the specified path. +584 It handles exceptions such as broken pipe errors or keyboard interrupts by closing and reinitializing the SMB session. +585 General exceptions are caught and logged, with a traceback provided if debugging is enabled. +586 +587 Args: +588 localpath (str, optional): The local file path of the file to be uploaded. Defaults to None. +589 """ +590 +591 if os.path.exists(localpath): +592 if os.path.isfile(localpath): +593 try: +594 localfile = os.path.basename(localpath) +595 f = LocalFileIO( +596 mode="rb", +597 path=localpath, +598 debug=self.config.debug +599 ) +600 self.smbClient.putFile( +601 shareName=self.smb_share, +602 pathName=ntpath.normpath(self.smb_cwd + ntpath.sep + localfile + ntpath.sep), +603 callback=f.read +604 ) +605 f.close() +606 except (BrokenPipeError, KeyboardInterrupt) as err: +607 print("[!] Interrupted.") +608 self.close_smb_session() +609 self.init_smb_session() +610 except Exception as err: +611 print("[!] Failed to upload '%s': %s" % (localfile, err)) +612 if self.config.debug: +613 traceback.print_exc() +614 else: +615 print("[!] The specified localpath is a directory. Use 'put -r <directory>' instead.") +616 else: +617 print("[!] The specified localpath does not exist.") +618 +619 def put_file_recursively(self, localpath=None): +620 """ +621 Recursively uploads files from a specified local directory to the SMB share. +622 +623 This method walks through the given local directory and all its subdirectories, uploading each file to the +624 corresponding directory structure on the SMB share. It first checks if the local path is a directory. If it is, +625 it iterates over all files and directories within the local path, creating necessary directories on the SMB share +626 and uploading files. If the local path is not a directory, it prints an error message. +627 +628 Args: +629 localpath (str, optional): The local directory path from which files will be uploaded. Defaults to None. +630 """ +631 +632 if os.path.exists(localpath): +633 if os.path.isfile(localpath): +634 # Iterate over all files and directories within the local path +635 local_files = {} +636 for root, dirs, files in os.walk(localpath): +637 if len(files) != 0: +638 local_files[root] = files +639 +640 # Iterate over the found files +641 for local_dir_path in sorted(local_files.keys()): +642 print("[>] Putting files of '%s'" % local_dir_path) +643 +644 # Create remote directory +645 remote_dir_path = local_dir_path.replace(os.path.sep, ntpath.sep) +646 self.mkdir( +647 path=ntpath.normpath(self.smb_cwd + ntpath.sep + remote_dir_path + ntpath.sep) +648 ) +649 +650 for local_file_path in local_files[local_dir_path]: +651 try: +652 f = LocalFileIO( +653 mode="rb", +654 path=local_dir_path + os.path.sep + local_file_path, +655 debug=self.config.debug +656 ) +657 self.smbClient.putFile( +658 shareName=self.smb_share, +659 pathName=ntpath.normpath(self.smb_cwd + ntpath.sep + remote_dir_path + ntpath.sep + local_file_path), +660 callback=f.read +661 ) +662 f.close() +663 +664 except BrokenPipeError as err: +665 f.set_error(message="[bold red]Failed uploading '%s': %s" % (f.path, err)) +666 f.close(remove=True) +667 break +668 except Exception as err: +669 f.set_error(message="[bold red]Failed uploading '%s': %s" % (f.path, err)) +670 f.close(remove=True) +671 else: +672 print("[!] The specified localpath is a file. Use 'put <file>' instead.") +673 else: +674 print("[!] The specified localpath does not exist.") +675 +676 def rmdir(self, path=None): +677 """ +678 Removes a directory from the SMB share at the specified path. +679 +680 This method attempts to delete a directory located at the given path on the SMB share. If the operation fails, +681 it prints an error message indicating the failure and the reason. If debugging is enabled, it also prints +682 the stack trace of the exception. +683 +684 Args: +685 path (str, optional): The path of the directory to be removed on the SMB share. Defaults to None. +686 """ +687 try: +688 self.smbClient.deleteDirectory( +689 shareName=self.smb_share, +690 pathName=ntpath.normpath(self.smb_cwd + ntpath.sep + path), +691 ) +692 except Exception as err: +693 print("[!] Failed to remove directory '%s': %s" % (path, err)) +694 if self.config.debug: +695 traceback.print_exc() +696 +697 def rm(self, path=None): +698 """ +699 Removes a file from the SMB share at the specified path. +700 +701 This method attempts to delete a file located at the given path on the SMB share. If the operation fails, +702 it prints an error message indicating the failure and the reason. If debugging is enabled, it also prints +703 the stack trace of the exception. +704 +705 Args: +706 path (str, optional): The path of the file to be removed on the SMB share. Defaults to None. +707 """ +708 try: +709 self.smbClient.deleteFile( +710 shareName=self.smb_share, +711 pathName=ntpath.normpath(self.smb_cwd + ntpath.sep + path), +712 ) +713 except Exception as err: +714 print("[!] Failed to remove file '%s': %s" % (path, err)) +715 if self.config.debug: +716 traceback.print_exc() +717 +718 def tree(self, path=None): +719 """ +720 Recursively lists the directory structure of the SMB share starting from the specified path. +721 +722 This function prints a visual representation of the directory tree of the remote SMB share. It uses +723 recursion to navigate through directories and lists all files and subdirectories in each directory. +724 The output is color-coded and formatted to enhance readability, with directories highlighted in cyan. +725 +726 Args: +727 path (str, optional): The starting path on the SMB share from which to begin listing the tree. +728 Defaults to the root of the current share. +729 """ +730 +731 def recurse_action(base_dir="", path=[], prompt=[]): +732 bars = ["│ ", "├── ", "└── "] +733 +734 remote_smb_path = ntpath.normpath(base_dir + ntpath.sep + ntpath.sep.join(path)) +735 +736 entries = [] +737 try: +738 entries = self.smbClient.listPath( +739 shareName=self.smb_share, +740 path=remote_smb_path+'\\*' +741 ) +742 except impacket.smbconnection.SessionError as err: +743 code, const, text = err.getErrorCode(), err.getErrorString()[0], err.getErrorString()[1] +744 errmsg = "Error 0x%08x (%s): %s" % (code, const, text) +745 if self.config.no_colors: +746 print("%s%s" % (''.join(prompt+[bars[2]]), errmsg)) +747 else: +748 print("%s\x1b[1;91m%s\x1b[0m" % (''.join(prompt+[bars[2]]), errmsg)) +749 return +750 +751 entries = [e for e in entries if e.get_longname() not in [".", ".."]] +752 entries = sorted(entries, key=lambda x:x.get_longname()) +753 +754 # +755 if len(entries) > 1: +756 index = 0 +757 for entry in entries: +758 index += 1 +759 # This is the first entry +760 if index == 0: +761 if entry.is_directory(): +762 if self.config.no_colors: +763 print("%s%s\\" % (''.join(prompt+[bars[1]]), entry.get_longname())) +764 else: +765 print("%s\x1b[1;96m%s\x1b[0m\\" % (''.join(prompt+[bars[1]]), entry.get_longname())) +766 recurse_action( +767 base_dir=base_dir, +768 path=path+[entry.get_longname()], +769 prompt=prompt+["│ "] +770 ) +771 else: +772 if self.config.no_colors: +773 print("%s%s" % (''.join(prompt+[bars[1]]), entry.get_longname())) +774 else: +775 print("%s\x1b[1m%s\x1b[0m" % (''.join(prompt+[bars[1]]), entry.get_longname())) +776 +777 # This is the last entry +778 elif index == len(entries): +779 if entry.is_directory(): +780 if self.config.no_colors: +781 print("%s%s\\" % (''.join(prompt+[bars[2]]), entry.get_longname())) +782 else: +783 print("%s\x1b[1;96m%s\x1b[0m\\" % (''.join(prompt+[bars[2]]), entry.get_longname())) +784 recurse_action( +785 base_dir=base_dir, +786 path=path+[entry.get_longname()], +787 prompt=prompt+[" "] +788 ) +789 else: +790 if self.config.no_colors: +791 print("%s%s" % (''.join(prompt+[bars[2]]), entry.get_longname())) +792 else: +793 print("%s\x1b[1m%s\x1b[0m" % (''.join(prompt+[bars[2]]), entry.get_longname())) +794 +795 # These are entries in the middle +796 else: +797 if entry.is_directory(): +798 if self.config.no_colors: +799 print("%s%s\\" % (''.join(prompt+[bars[1]]), entry.get_longname())) +800 else: +801 print("%s\x1b[1;96m%s\x1b[0m\\" % (''.join(prompt+[bars[1]]), entry.get_longname())) +802 recurse_action( +803 base_dir=base_dir, +804 path=path+[entry.get_longname()], +805 prompt=prompt+["│ "] +806 ) +807 else: +808 if self.config.no_colors: +809 print("%s%s" % (''.join(prompt+[bars[1]]), entry.get_longname())) +810 else: +811 print("%s\x1b[1m%s\x1b[0m" % (''.join(prompt+[bars[1]]), entry.get_longname())) +812 +813 # +814 elif len(entries) == 1: +815 entry = entries[0] +816 if entry.is_directory(): +817 if self.config.no_colors: +818 print("%s%s\\" % (''.join(prompt+[bars[2]]), entry.get_longname())) +819 else: +820 print("%s\x1b[1;96m%s\x1b[0m\\" % (''.join(prompt+[bars[2]]), entry.get_longname())) +821 recurse_action( +822 base_dir=base_dir, +823 path=path+[entry.get_longname()], +824 prompt=prompt+[" "] +825 ) +826 else: +827 if self.config.no_colors: +828 print("%s%s" % (''.join(prompt+[bars[2]]), entry.get_longname())) +829 else: +830 print("%s\x1b[1m%s\x1b[0m" % (''.join(prompt+[bars[2]]), entry.get_longname())) +831 +832 # Entrypoint +833 try: +834 if self.config.no_colors: +835 print("%s\\" % path) +836 else: +837 print("\x1b[1;96m%s\x1b[0m\\" % path) +838 recurse_action( +839 base_dir=self.smb_cwd, +840 path=[path], +841 prompt=[""] +842 ) +843 except (BrokenPipeError, KeyboardInterrupt) as e: +844 print("[!] Interrupted.") +845 self.close_smb_session() +846 self.init_smb_session() +847 +848 # Setter / Getter +849 +850 def set_share(self, shareName): +851 """ +852 Sets the current SMB share to the specified share name. +853 +854 This method updates the SMB session to use the specified share name. It checks if the share name is valid +855 and updates the smb_share attribute of the SMBSession instance. +856 +857 Parameters: +858 shareName (str): The name of the share to set as the current SMB share. +859 +860 Raises: +861 ValueError: If the shareName is None or an empty string. +862 """ +863 +864 if shareName is not None: +865 self.smb_share = shareName +866 +867 def set_cwd(self, path=None): +868 """ +869 Sets the current working directory on the SMB share to the specified path. +870 +871 This method updates the current working directory (cwd) of the SMB session to the given path if it is a valid directory. +872 If the specified path is not a directory, the cwd remains unchanged. +873 +874 Parameters: +875 path (str): The path to set as the current working directory. +876 +877 Raises: +878 ValueError: If the specified path is not a directory. +879 """ +880 +881 if path is not None: +882 # Set path separators to ntpath sep +883 if '/' in path: +884 path = path.replace('/', ntpath.sep) +885 +886 if path.startswith(ntpath.sep): +887 # Absolute path +888 path = path + ntpath.sep +889 else: +890 # Relative path to the CWD +891 if len(self.smb_cwd) == 0: +892 path = path + ntpath.sep +893 else: +894 path = self.smb_cwd + ntpath.sep + path +895 +896 # Path normalization +897 path = ntpath.normpath(path) +898 path = re.sub(r'\\+', r'\\', path) +899 +900 if path in ["", ".", ".."]: +901 self.smb_cwd = "" +902 else: +903 if self.path_isdir(pathFromRoot=path.strip(ntpath.sep)): +904 # Path exists on the remote +905 self.smb_cwd = ntpath.normpath(path) +906 else: +907 # Path does not exists or is not a directory on the remote +908 print("[!] Remote directory '%s' does not exist." % path) +
Class SMBSession is designed to handle the session management for SMB (Server Message Block) protocol connections. +It provides functionalities to connect to an SMB server, authenticate using either NTLM or Kerberos, and manage SMB shares.
+ +Attributes: + address (str): The IP address or hostname of the SMB server. + domain (str): The domain name for SMB server authentication. + username (str): The username for SMB server authentication. + password (str): The password for SMB server authentication. + lmhash (str): The LM hash of the user's password, if available. + nthash (str): The NT hash of the user's password, if available. + use_kerberos (bool): A flag to determine whether to use Kerberos for authentication. + kdcHost (str): The Key Distribution Center (KDC) host for Kerberos authentication. + debug (bool): A flag to enable debug output. + smbClient (object): The SMB client object used for the connection. + connected (bool): A flag to check the status of the connection. + smb_share (str): The current SMB share in use. + smb_path (str): The current path within the SMB share.
+ +Methods: + __init__(address, domain, username, password, lmhash, nthash, use_kerberos=False, kdcHost=None, debug=False): + Initializes the SMBSession with the specified parameters. + init_smb_session(): + Initializes the SMB session by connecting to the server and authenticating using the specified method.
+45 def __init__(self, address, domain, username, password, lmhash, nthash, use_kerberos=False, kdcHost=None, config=None): +46 super(SMBSession, self).__init__() +47 # Objects +48 self.config = config +49 +50 # Target server +51 self.address = address +52 +53 # Credentials +54 self.domain = domain +55 self.username = username +56 self.password = password +57 self.lmhash = lmhash +58 self.nthash = nthash +59 self.use_kerberos = use_kerberos +60 self.kdcHost = kdcHost +61 +62 self.smbClient = None +63 self.connected = False +64 +65 self.available_shares = {} +66 self.smb_share = None +67 self.smb_cwd = "" +68 +69 self.list_shares() +
73 def init_smb_session(self): + 74 """ + 75 Initializes and establishes a session with the SMB server. + 76 + 77 This method sets up the SMB connection using either Kerberos or NTLM authentication based on the configuration. + 78 It attempts to connect to the SMB server specified by the `address` attribute and authenticate using the credentials provided during the object's initialization. + 79 + 80 The method will print debug information if the `debug` attribute is set to True. Upon successful connection and authentication, it sets the `connected` attribute to True. + 81 + 82 Returns: + 83 bool: True if the connection and authentication are successful, False otherwise. + 84 """ + 85 + 86 if self.config.debug: + 87 print("[debug] [>] Connecting to remote SMB server '%s' ... " % self.address) + 88 try: + 89 self.smbClient = impacket.smbconnection.SMBConnection( + 90 remoteName=self.address, + 91 remoteHost=self.address, + 92 sess_port=int(445) + 93 ) + 94 except OSError as err: + 95 print("[!] %s" % err) + 96 self.smbClient = None + 97 + 98 self.connected = False + 99 if self.smbClient is not None: +100 if self.use_kerberos: +101 if self.config.debug: +102 print("[debug] [>] Authenticating as '%s\\%s' with kerberos ... " % (self.domain, self.username)) +103 self.connected = self.smbClient.kerberosLogin( +104 user=self.username, +105 password=self.password, +106 domain=self.domain, +107 lmhash=self.lmhash, +108 nthash=self.nthash, +109 aesKey=self.aesKey, +110 kdcHost=self.kdcHost +111 ) +112 +113 else: +114 if self.config.debug: +115 print("[debug] [>] Authenticating as '%s\\%s' with NTLM ... " % (self.domain, self.username)) +116 self.connected = self.smbClient.login( +117 user=self.username, +118 password=self.password, +119 domain=self.domain, +120 lmhash=self.lmhash, +121 nthash=self.nthash +122 ) +123 +124 if self.connected: +125 print("[+] Successfully authenticated to '%s' as '%s\\%s'!" % (self.address, self.domain, self.username)) +126 else: +127 print("[!] Failed to authenticate to '%s' as '%s\\%s'!" % (self.address, self.domain, self.username)) +128 +129 return self.connected +
Initializes and establishes a session with the SMB server.
+ +This method sets up the SMB connection using either Kerberos or NTLM authentication based on the configuration.
+It attempts to connect to the SMB server specified by the address
attribute and authenticate using the credentials provided during the object's initialization.
The method will print debug information if the debug
attribute is set to True. Upon successful connection and authentication, it sets the connected
attribute to True.
Returns: + bool: True if the connection and authentication are successful, False otherwise.
+131 def close_smb_session(self): +132 """ +133 Closes the current SMB session by disconnecting the SMB client. +134 +135 This method ensures that the SMB client connection is properly closed. It checks if the client is connected +136 and if so, it closes the connection and resets the connection status. +137 +138 Raises: +139 Exception: If the SMB client is not initialized or if there's an error during the disconnection process. +140 """ +141 +142 if self.smbClient is not None: +143 if self.connected: +144 self.smbClient.close() +145 self.connected = False +146 print("[+] SMB connection closed successfully.") +147 else: +148 print("[!] No active SMB connection to close.") +149 else: +150 raise Exception("SMB client is not initialized.") +
Closes the current SMB session by disconnecting the SMB client.
+ +This method ensures that the SMB client connection is properly closed. It checks if the client is connected +and if so, it closes the connection and resets the connection status.
+ +Raises: + Exception: If the SMB client is not initialized or if there's an error during the disconnection process.
+154 def get_file(self, path=None, keepRemotePath=False): +155 """ +156 Retrieves a file from the specified path on the SMB share. +157 +158 This method attempts to retrieve a file from the given path within the currently connected SMB share. +159 If the path points to a directory, it skips the retrieval. It handles file retrieval by creating a local +160 file object and writing the contents of the remote file to it using the SMB client's getFile method. +161 +162 Parameters: +163 path (str): The path of the file to retrieve. If None, uses the current smb_path. +164 +165 Returns: +166 None +167 """ +168 +169 tmp_file_path = self.smb_cwd + ntpath.sep + path +170 matches = self.smbClient.listPath( +171 shareName=self.smb_share, +172 path=tmp_file_path +173 ) +174 +175 for entry in matches: +176 if entry.is_directory(): +177 print("[>] Skipping '%s' because it is a directory." % tmp_file_path) +178 else: +179 try: +180 if ntpath.sep in path: +181 outputfile = ntpath.dirname(path) + ntpath.sep + entry.get_longname() +182 else: +183 outputfile = entry.get_longname() +184 f = LocalFileIO( +185 mode="wb", +186 path=outputfile, +187 expected_size=entry.get_filesize(), +188 debug=self.config.debug, +189 keepRemotePath=keepRemotePath +190 ) +191 self.smbClient.getFile( +192 shareName=self.smb_share, +193 pathName=tmp_file_path, +194 callback=f.write +195 ) +196 f.close() +197 except (BrokenPipeError, KeyboardInterrupt) as e: +198 f.close() +199 print("\x1b[v\x1b[o\r[!] Interrupted.") +200 self.close_smb_session() +201 self.init_smb_session() +202 +203 return None +
Retrieves a file from the specified path on the SMB share.
+ +This method attempts to retrieve a file from the given path within the currently connected SMB share. +If the path points to a directory, it skips the retrieval. It handles file retrieval by creating a local +file object and writing the contents of the remote file to it using the SMB client's getFile method.
+ +Parameters: + path (str): The path of the file to retrieve. If None, uses the current smb_path.
+ +Returns: + None
+205 def get_file_recursively(self, path=None): +206 """ +207 Recursively retrieves files from a specified path on the SMB share. +208 +209 This method navigates through all directories starting from the given path, +210 and downloads all files found. It handles directories recursively, ensuring +211 that all nested files are retrieved. The method skips over directory entries +212 and handles errors gracefully, attempting to continue the operation where possible. +213 +214 Parameters: +215 path (str): The initial directory path from which to start the recursive file retrieval. +216 If None, it starts from the root of the configured SMB share. +217 """ +218 +219 def recurse_action(base_dir="", path=[]): +220 remote_smb_path = base_dir + ntpath.sep.join(path) +221 entries = self.smbClient.listPath( +222 shareName=self.smb_share, +223 path=remote_smb_path + '\\*' +224 ) +225 if len(entries) != 0: +226 files = [entry for entry in entries if not entry.is_directory()] +227 directories = [entry for entry in entries if entry.is_directory() and entry.get_longname() not in [".", ".."]] +228 +229 # Files +230 if len(files) != 0: +231 print("[>] Retrieving files of '%s'" % remote_smb_path) +232 for entry_file in files: +233 if not entry_file.is_directory(): +234 f = LocalFileIO( +235 mode="wb", +236 path=remote_smb_path + ntpath.sep + entry_file.get_longname(), +237 expected_size=entry_file.get_filesize(), +238 debug=self.config.debug +239 ) +240 try: +241 self.smbClient.getFile( +242 shareName=self.smb_share, +243 pathName=remote_smb_path + ntpath.sep + entry_file.get_longname(), +244 callback=f.write +245 ) +246 f.close() +247 except BrokenPipeError as err: +248 f.set_error(message="[bold red]Failed downloading '%s': %s" % (f.path, err)) +249 f.close(remove=True) +250 break +251 except Exception as err: +252 f.set_error(message="[bold red]Failed downloading '%s': %s" % (f.path, err)) +253 f.close(remove=True) +254 +255 # Directories +256 for entry_directory in directories: +257 if entry_directory.is_directory(): +258 recurse_action( +259 base_dir=self.smb_cwd, +260 path=path+[entry_directory.get_longname()] +261 ) +262 # Entrypoint +263 try: +264 recurse_action( +265 base_dir=self.smb_cwd, +266 path=[path] +267 ) +268 except (BrokenPipeError, KeyboardInterrupt) as e: +269 print("\x1b[v\x1b[o\r[!] Interrupted.") +270 self.close_smb_session() +271 self.init_smb_session() +
Recursively retrieves files from a specified path on the SMB share.
+ +This method navigates through all directories starting from the given path, +and downloads all files found. It handles directories recursively, ensuring +that all nested files are retrieved. The method skips over directory entries +and handles errors gracefully, attempting to continue the operation where possible.
+ +Parameters: + path (str): The initial directory path from which to start the recursive file retrieval. + If None, it starts from the root of the configured SMB share.
+273 def info(self, share=True, server=True): +274 """ +275 Displays information about the server and optionally the shares. +276 +277 This method prints detailed information about the server's characteristics such as NetBIOS names, DNS details, OS information, and SMB capabilities. If the `share` parameter is set to True and a share is currently set, it will also attempt to display information about the share. +278 +279 Parameters: +280 share (bool): If True, display information about the current share. +281 server (bool): If True, display information about the server. +282 +283 Returns: +284 None +285 """ +286 +287 if server: +288 if self.config.no_colors: +289 print("[+] Server:") +290 print(" ├─NetBIOS:") +291 print(" │ ├─ NetBIOS Hostname ──────── : %s" % (self.smbClient.getServerName())) +292 print(" │ └─ NetBIOS Domain ────────── : %s" % (self.smbClient.getServerDomain())) +293 print(" ├─DNS:") +294 print(" │ ├─ DNS Hostname ──────────── : %s" % (self.smbClient.getServerDNSHostName())) +295 print(" │ └─ DNS Domain ────────────── : %s" % (self.smbClient.getServerDNSDomainName())) +296 print(" ├─OS:") +297 print(" │ ├─ OS Name ───────────────── : %s" % (self.smbClient.getServerOS())) +298 print(" │ └─ OS Version ────────────── : %s.%s.%s" % (self.smbClient.getServerOSMajor(), self.smbClient.getServerOSMinor(), self.smbClient.getServerOSBuild())) +299 print(" ├─Server:") +300 print(" │ ├─ Signing Required ──────── : %s" % (self.smbClient.isSigningRequired())) +301 print(" │ ├─ Login Required ────────── : %s" % (self.smbClient.isLoginRequired())) +302 print(" │ ├─ Supports NTLMv2 ───────── : %s" % (self.smbClient.doesSupportNTLMv2())) +303 MaxReadSize = self.smbClient.getIOCapabilities()["MaxReadSize"] +304 print(" │ ├─ Max size of read chunk ── : %d bytes (%s)" % (MaxReadSize, b_filesize(MaxReadSize))) +305 MaxWriteSize = self.smbClient.getIOCapabilities()["MaxWriteSize"] +306 print(" │ └─ Max size of write chunk ─ : %d bytes (%s)" % (MaxWriteSize, b_filesize(MaxWriteSize))) +307 print(" └─") +308 else: +309 print("[+] Server:") +310 print(" ├─NetBIOS:") +311 print(" │ ├─ \x1b[94mNetBIOS Hostname\x1b[0m \x1b[90m────────\x1b[0m : \x1b[93m%s\x1b[0m" % (self.smbClient.getServerName())) +312 print(" │ └─ \x1b[94mNetBIOS Domain\x1b[0m \x1b[90m──────────\x1b[0m : \x1b[93m%s\x1b[0m" % (self.smbClient.getServerDomain())) +313 print(" ├─DNS:") +314 print(" │ ├─ \x1b[94mDNS Hostname\x1b[0m \x1b[90m────────────\x1b[0m : \x1b[93m%s\x1b[0m" % (self.smbClient.getServerDNSHostName())) +315 print(" │ └─ \x1b[94mDNS Domain\x1b[0m \x1b[90m──────────────\x1b[0m : \x1b[93m%s\x1b[0m" % (self.smbClient.getServerDNSDomainName())) +316 print(" ├─OS:") +317 print(" │ ├─ \x1b[94mOS Name\x1b[0m \x1b[90m─────────────────\x1b[0m : \x1b[93m%s\x1b[0m" % (self.smbClient.getServerOS())) +318 print(" │ └─ \x1b[94mOS Version\x1b[0m \x1b[90m──────────────\x1b[0m : \x1b[93m%s.%s.%s\x1b[0m" % (self.smbClient.getServerOSMajor(), self.smbClient.getServerOSMinor(), self.smbClient.getServerOSBuild())) +319 print(" ├─Server:") +320 print(" │ ├─ \x1b[94mSigning Required\x1b[0m \x1b[90m────────\x1b[0m : \x1b[93m%s\x1b[0m" % (self.smbClient.isSigningRequired())) +321 print(" │ ├─ \x1b[94mLogin Required\x1b[0m \x1b[90m──────────\x1b[0m : \x1b[93m%s\x1b[0m" % (self.smbClient.isLoginRequired())) +322 print(" │ ├─ \x1b[94mSupports NTLMv2\x1b[0m \x1b[90m─────────\x1b[0m : \x1b[93m%s\x1b[0m" % (self.smbClient.doesSupportNTLMv2())) +323 MaxReadSize = self.smbClient.getIOCapabilities()["MaxReadSize"] +324 print(" │ ├─ \x1b[94mMax size of read chunk\x1b[0m \x1b[90m──\x1b[0m : \x1b[93m%d bytes (%s)\x1b[0m" % (MaxReadSize, b_filesize(MaxReadSize))) +325 MaxWriteSize = self.smbClient.getIOCapabilities()["MaxWriteSize"] +326 print(" │ └─ \x1b[94mMax size of write chunk\x1b[0m \x1b[90m─\x1b[0m : \x1b[93m%d bytes (%s)\x1b[0m" % (MaxWriteSize, b_filesize(MaxWriteSize))) +327 print(" └─") +328 +329 if share and self.smb_share is not None: +330 share_name = self.available_shares.get(self.smb_share.lower(), "")["name"] +331 share_comment = self.available_shares.get(self.smb_share.lower(), "")["comment"] +332 share_type = self.available_shares.get(self.smb_share.lower(), "")["type"] +333 share_type =', '.join([s.replace("STYPE_","") for s in share_type]) +334 share_rawtype = self.available_shares.get(self.smb_share.lower(), "")["rawtype"] +335 if self.config.no_colors: +336 print("\n[+] Share:") +337 print(" ├─ Name ──────────── : %s" % (share_name)) +338 print(" ├─ Description ───── : %s" % (share_comment)) +339 print(" ├─ Type ──────────── : %s" % (share_type)) +340 print(" └─ Raw type value ── : %s" % (share_rawtype)) +341 else: +342 print("\n[+] Share:") +343 print(" ├─ \x1b[94mName\x1b[0m \x1b[90m────────────\x1b[0m : \x1b[93m%s\x1b[0m" % (share_name)) +344 print(" ├─ \x1b[94mDescription\x1b[0m \x1b[90m─────\x1b[0m : \x1b[93m%s\x1b[0m" % (share_comment)) +345 print(" ├─ \x1b[94mType\x1b[0m \x1b[90m────────────\x1b[0m : \x1b[93m%s\x1b[0m" % (share_type)) +346 print(" └─ \x1b[94mRaw type value\x1b[0m \x1b[90m──\x1b[0m : \x1b[93m%s\x1b[0m" % (share_rawtype)) +
Displays information about the server and optionally the shares.
+ +This method prints detailed information about the server's characteristics such as NetBIOS names, DNS details, OS information, and SMB capabilities. If the share
parameter is set to True and a share is currently set, it will also attempt to display information about the share.
Parameters: + share (bool): If True, display information about the current share. + server (bool): If True, display information about the server.
+ +Returns: + None
+348 def list_contents(self, path=None): +349 """ +350 Lists the contents of a specified directory on the SMB share. +351 +352 This method retrieves the contents of a directory specified by `shareName` and `path`. If `shareName` or `path` +353 is not provided, it defaults to the instance's current SMB share or path. The method returns a dictionary with +354 the long names of the files and directories as keys and their respective SMB entry objects as values. +355 +356 Args: +357 shareName (str, optional): The name of the SMB share. Defaults to the current SMB share if None. +358 path (str, optional): The directory path to list contents from. Defaults to the current path if None. +359 +360 Returns: +361 dict: A dictionary with file and directory names as keys and their SMB entry objects as values. +362 """ +363 +364 if path is None or len(path) == 0: +365 path = self.smb_cwd +366 path = path.rstrip(ntpath.sep) + ntpath.sep + "*" +367 +368 contents = {} +369 entries = self.smbClient.listPath( +370 shareName=self.smb_share, +371 path=path +372 ) +373 for entry in entries: +374 contents[entry.get_longname()] = entry +375 +376 return contents +
Lists the contents of a specified directory on the SMB share.
+ +This method retrieves the contents of a directory specified by shareName
and path
. If shareName
or path
+is not provided, it defaults to the instance's current SMB share or path. The method returns a dictionary with
+the long names of the files and directories as keys and their respective SMB entry objects as values.
Args: + shareName (str, optional): The name of the SMB share. Defaults to the current SMB share if None. + path (str, optional): The directory path to list contents from. Defaults to the current path if None.
+ +Returns: + dict: A dictionary with file and directory names as keys and their SMB entry objects as values.
+414 def mkdir(self, path=None): +415 """ +416 Creates a directory at the specified path on the SMB share. +417 +418 This method takes a path and attempts to create the directory structure on the SMB share. If the path includes +419 nested directories, it will create each directory in the sequence. If a directory already exists, it will skip +420 the creation for that directory without raising an error. +421 +422 Args: +423 path (str, optional): The full path of the directory to create on the SMB share. Defaults to None. +424 +425 Note: +426 The path should use forward slashes ('/') which will be converted to backslashes (ntpath.sep) for SMB compatibility. +427 """ +428 +429 if path is not None: +430 # Prepare path +431 path = path.replace('/',ntpath.sep) +432 if ntpath.sep in path: +433 path = path.strip(ntpath.sep).split(ntpath.sep) +434 else: +435 path = [path] +436 +437 # Create each dir in the path +438 for depth in range(1, len(path)+1): +439 tmp_path = ntpath.sep.join(path[:depth]) +440 try: +441 self.smbClient.createDirectory( +442 shareName=self.smb_share, +443 pathName=ntpath.normpath(self.smb_cwd + ntpath.sep + tmp_path + ntpath.sep) +444 ) +445 except impacket.smbconnection.SessionError as err: +446 if err.getErrorCode() == 0xc0000035: +447 # STATUS_OBJECT_NAME_COLLISION +448 # Remote directory already created, this is normal +449 # Src: https://github.com/fortra/impacket/blob/269ce69872f0e8f2188a80addb0c39fedfa6dcb8/impacket/nt_errors.py#L268C9-L268C19 +450 pass +451 else: +452 print("[!] Failed to create directory '%s': %s" % (tmp_path, err)) +453 if self.config.debug: +454 traceback.print_exc() +455 else: +456 pass +
Creates a directory at the specified path on the SMB share.
+ +This method takes a path and attempts to create the directory structure on the SMB share. If the path includes +nested directories, it will create each directory in the sequence. If a directory already exists, it will skip +the creation for that directory without raising an error.
+ +Args: + path (str, optional): The full path of the directory to create on the SMB share. Defaults to None.
+ +Note: + The path should use forward slashes ('/') which will be converted to backslashes (ntpath.sep) for SMB compatibility.
+458 def path_exists(self, path=None): +459 """ +460 Checks if the specified path exists on the SMB share. +461 +462 This method determines if a given path exists on the SMB share by attempting to list the contents of the path. +463 If the path listing is successful and returns one or more entries, the path is considered to exist. +464 +465 Args: +466 path (str, optional): The path to check on the SMB share. Defaults to None. +467 +468 Returns: +469 bool: True if the path exists, False otherwise or if an error occurs. +470 """ +471 +472 if path is not None: +473 path = path.replace('*','') +474 try: +475 contents = self.smbClient.listPath( +476 shareName=self.smb_share, +477 path=ntpath.normpath(self.smb_cwd + ntpath.sep + path + ntpath.sep) +478 ) +479 return (len(contents) != 0) +480 except Exception as e: +481 return False +482 else: +483 return False +
Checks if the specified path exists on the SMB share.
+ +This method determines if a given path exists on the SMB share by attempting to list the contents of the path. +If the path listing is successful and returns one or more entries, the path is considered to exist.
+ +Args: + path (str, optional): The path to check on the SMB share. Defaults to None.
+ +Returns: + bool: True if the path exists, False otherwise or if an error occurs.
+485 def path_isdir(self, pathFromRoot=None): +486 """ +487 Checks if the specified path is a directory on the SMB share. +488 +489 This method determines if a given path corresponds to a directory on the SMB share. It does this by listing the +490 contents of the path and filtering for entries that match the basename of the path and are marked as directories. +491 +492 Args: +493 path (str, optional): The path to check on the SMB share. Defaults to None. +494 +495 Returns: +496 bool: True if the path is a directory, False otherwise or if an error occurs. +497 """ +498 +499 if pathFromRoot is not None: +500 # Replace slashes if any +501 path = pathFromRoot.replace('/', ntpath.sep) +502 +503 # Strip wildcards to avoid injections +504 path = path.replace('*','') +505 +506 # Normalize path and strip leading backslash +507 path = ntpath.normpath(path + ntpath.sep).lstrip(ntpath.sep) +508 +509 if path.strip() in ['', '.', '..']: +510 # By defininition they exist on the filesystem +511 return True +512 else: +513 try: +514 contents = self.smbClient.listPath( +515 shareName=self.smb_share, +516 path=path+'*' +517 ) +518 # Filter on directories +519 contents = [ +520 c for c in contents +521 if c.get_longname() == ntpath.basename(path) and c.is_directory() +522 ] +523 return (len(contents) != 0) +524 except Exception as e: +525 return False +526 else: +527 return False +
Checks if the specified path is a directory on the SMB share.
+ +This method determines if a given path corresponds to a directory on the SMB share. It does this by listing the +contents of the path and filtering for entries that match the basename of the path and are marked as directories.
+ +Args: + path (str, optional): The path to check on the SMB share. Defaults to None.
+ +Returns: + bool: True if the path is a directory, False otherwise or if an error occurs.
+529 def path_isfile(self, path=None): +530 """ +531 Checks if the specified path is a file on the SMB share. +532 +533 This method determines if a given path corresponds to a file on the SMB share. It does this by listing the +534 contents of the path and filtering for entries that match the basename of the path and are not marked as directories. +535 +536 Args: +537 path (str, optional): The path to check on the SMB share. Defaults to None. +538 +539 Returns: +540 bool: True if the path is a file, False otherwise or if an error occurs. +541 """ +542 +543 if path is not None: +544 path = path.replace('*','') +545 try: +546 contents = self.smbClient.listPath( +547 shareName=self.smb_share, +548 path=ntpath.normpath(self.smb_cwd + ntpath.sep + path + ntpath.sep) +549 ) +550 # Filter on files +551 contents = [ +552 c for c in contents +553 if c.get_longname() == ntpath.basename(path) and not c.is_directory() +554 ] +555 return (len(contents) != 0) +556 except Exception as e: +557 return False +558 else: +559 return False +
Checks if the specified path is a file on the SMB share.
+ +This method determines if a given path corresponds to a file on the SMB share. It does this by listing the +contents of the path and filtering for entries that match the basename of the path and are not marked as directories.
+ +Args: + path (str, optional): The path to check on the SMB share. Defaults to None.
+ +Returns: + bool: True if the path is a file, False otherwise or if an error occurs.
+561 def ping_smb_session(self): +562 """ +563 Tests the connectivity to the SMB server by sending an echo command. +564 +565 This method attempts to send an echo command to the SMB server to check if the session is still active. +566 It updates the `connected` attribute of the class based on the success or failure of the echo command. +567 +568 Returns: +569 bool: True if the echo command succeeds (indicating the session is active), False otherwise. +570 """ +571 +572 try: +573 self.smbClient.getSMBServer().echo() +574 self.connected = True +575 except Exception as e: +576 self.connected = False +577 return self.connected +
Tests the connectivity to the SMB server by sending an echo command.
+ +This method attempts to send an echo command to the SMB server to check if the session is still active.
+It updates the connected
attribute of the class based on the success or failure of the echo command.
Returns: + bool: True if the echo command succeeds (indicating the session is active), False otherwise.
+579 def put_file(self, localpath=None): +580 """ +581 Uploads a single file to the SMB share. +582 +583 This method takes a local file path, opens the file, and uploads it to the SMB share at the specified path. +584 It handles exceptions such as broken pipe errors or keyboard interrupts by closing and reinitializing the SMB session. +585 General exceptions are caught and logged, with a traceback provided if debugging is enabled. +586 +587 Args: +588 localpath (str, optional): The local file path of the file to be uploaded. Defaults to None. +589 """ +590 +591 if os.path.exists(localpath): +592 if os.path.isfile(localpath): +593 try: +594 localfile = os.path.basename(localpath) +595 f = LocalFileIO( +596 mode="rb", +597 path=localpath, +598 debug=self.config.debug +599 ) +600 self.smbClient.putFile( +601 shareName=self.smb_share, +602 pathName=ntpath.normpath(self.smb_cwd + ntpath.sep + localfile + ntpath.sep), +603 callback=f.read +604 ) +605 f.close() +606 except (BrokenPipeError, KeyboardInterrupt) as err: +607 print("[!] Interrupted.") +608 self.close_smb_session() +609 self.init_smb_session() +610 except Exception as err: +611 print("[!] Failed to upload '%s': %s" % (localfile, err)) +612 if self.config.debug: +613 traceback.print_exc() +614 else: +615 print("[!] The specified localpath is a directory. Use 'put -r <directory>' instead.") +616 else: +617 print("[!] The specified localpath does not exist.") +
Uploads a single file to the SMB share.
+ +This method takes a local file path, opens the file, and uploads it to the SMB share at the specified path. +It handles exceptions such as broken pipe errors or keyboard interrupts by closing and reinitializing the SMB session. +General exceptions are caught and logged, with a traceback provided if debugging is enabled.
+ +Args: + localpath (str, optional): The local file path of the file to be uploaded. Defaults to None.
+619 def put_file_recursively(self, localpath=None): +620 """ +621 Recursively uploads files from a specified local directory to the SMB share. +622 +623 This method walks through the given local directory and all its subdirectories, uploading each file to the +624 corresponding directory structure on the SMB share. It first checks if the local path is a directory. If it is, +625 it iterates over all files and directories within the local path, creating necessary directories on the SMB share +626 and uploading files. If the local path is not a directory, it prints an error message. +627 +628 Args: +629 localpath (str, optional): The local directory path from which files will be uploaded. Defaults to None. +630 """ +631 +632 if os.path.exists(localpath): +633 if os.path.isfile(localpath): +634 # Iterate over all files and directories within the local path +635 local_files = {} +636 for root, dirs, files in os.walk(localpath): +637 if len(files) != 0: +638 local_files[root] = files +639 +640 # Iterate over the found files +641 for local_dir_path in sorted(local_files.keys()): +642 print("[>] Putting files of '%s'" % local_dir_path) +643 +644 # Create remote directory +645 remote_dir_path = local_dir_path.replace(os.path.sep, ntpath.sep) +646 self.mkdir( +647 path=ntpath.normpath(self.smb_cwd + ntpath.sep + remote_dir_path + ntpath.sep) +648 ) +649 +650 for local_file_path in local_files[local_dir_path]: +651 try: +652 f = LocalFileIO( +653 mode="rb", +654 path=local_dir_path + os.path.sep + local_file_path, +655 debug=self.config.debug +656 ) +657 self.smbClient.putFile( +658 shareName=self.smb_share, +659 pathName=ntpath.normpath(self.smb_cwd + ntpath.sep + remote_dir_path + ntpath.sep + local_file_path), +660 callback=f.read +661 ) +662 f.close() +663 +664 except BrokenPipeError as err: +665 f.set_error(message="[bold red]Failed uploading '%s': %s" % (f.path, err)) +666 f.close(remove=True) +667 break +668 except Exception as err: +669 f.set_error(message="[bold red]Failed uploading '%s': %s" % (f.path, err)) +670 f.close(remove=True) +671 else: +672 print("[!] The specified localpath is a file. Use 'put <file>' instead.") +673 else: +674 print("[!] The specified localpath does not exist.") +
Recursively uploads files from a specified local directory to the SMB share.
+ +This method walks through the given local directory and all its subdirectories, uploading each file to the +corresponding directory structure on the SMB share. It first checks if the local path is a directory. If it is, +it iterates over all files and directories within the local path, creating necessary directories on the SMB share +and uploading files. If the local path is not a directory, it prints an error message.
+ +Args: + localpath (str, optional): The local directory path from which files will be uploaded. Defaults to None.
+676 def rmdir(self, path=None): +677 """ +678 Removes a directory from the SMB share at the specified path. +679 +680 This method attempts to delete a directory located at the given path on the SMB share. If the operation fails, +681 it prints an error message indicating the failure and the reason. If debugging is enabled, it also prints +682 the stack trace of the exception. +683 +684 Args: +685 path (str, optional): The path of the directory to be removed on the SMB share. Defaults to None. +686 """ +687 try: +688 self.smbClient.deleteDirectory( +689 shareName=self.smb_share, +690 pathName=ntpath.normpath(self.smb_cwd + ntpath.sep + path), +691 ) +692 except Exception as err: +693 print("[!] Failed to remove directory '%s': %s" % (path, err)) +694 if self.config.debug: +695 traceback.print_exc() +
Removes a directory from the SMB share at the specified path.
+ +This method attempts to delete a directory located at the given path on the SMB share. If the operation fails, +it prints an error message indicating the failure and the reason. If debugging is enabled, it also prints +the stack trace of the exception.
+ +Args: + path (str, optional): The path of the directory to be removed on the SMB share. Defaults to None.
+697 def rm(self, path=None): +698 """ +699 Removes a file from the SMB share at the specified path. +700 +701 This method attempts to delete a file located at the given path on the SMB share. If the operation fails, +702 it prints an error message indicating the failure and the reason. If debugging is enabled, it also prints +703 the stack trace of the exception. +704 +705 Args: +706 path (str, optional): The path of the file to be removed on the SMB share. Defaults to None. +707 """ +708 try: +709 self.smbClient.deleteFile( +710 shareName=self.smb_share, +711 pathName=ntpath.normpath(self.smb_cwd + ntpath.sep + path), +712 ) +713 except Exception as err: +714 print("[!] Failed to remove file '%s': %s" % (path, err)) +715 if self.config.debug: +716 traceback.print_exc() +
Removes a file from the SMB share at the specified path.
+ +This method attempts to delete a file located at the given path on the SMB share. If the operation fails, +it prints an error message indicating the failure and the reason. If debugging is enabled, it also prints +the stack trace of the exception.
+ +Args: + path (str, optional): The path of the file to be removed on the SMB share. Defaults to None.
+718 def tree(self, path=None): +719 """ +720 Recursively lists the directory structure of the SMB share starting from the specified path. +721 +722 This function prints a visual representation of the directory tree of the remote SMB share. It uses +723 recursion to navigate through directories and lists all files and subdirectories in each directory. +724 The output is color-coded and formatted to enhance readability, with directories highlighted in cyan. +725 +726 Args: +727 path (str, optional): The starting path on the SMB share from which to begin listing the tree. +728 Defaults to the root of the current share. +729 """ +730 +731 def recurse_action(base_dir="", path=[], prompt=[]): +732 bars = ["│ ", "├── ", "└── "] +733 +734 remote_smb_path = ntpath.normpath(base_dir + ntpath.sep + ntpath.sep.join(path)) +735 +736 entries = [] +737 try: +738 entries = self.smbClient.listPath( +739 shareName=self.smb_share, +740 path=remote_smb_path+'\\*' +741 ) +742 except impacket.smbconnection.SessionError as err: +743 code, const, text = err.getErrorCode(), err.getErrorString()[0], err.getErrorString()[1] +744 errmsg = "Error 0x%08x (%s): %s" % (code, const, text) +745 if self.config.no_colors: +746 print("%s%s" % (''.join(prompt+[bars[2]]), errmsg)) +747 else: +748 print("%s\x1b[1;91m%s\x1b[0m" % (''.join(prompt+[bars[2]]), errmsg)) +749 return +750 +751 entries = [e for e in entries if e.get_longname() not in [".", ".."]] +752 entries = sorted(entries, key=lambda x:x.get_longname()) +753 +754 # +755 if len(entries) > 1: +756 index = 0 +757 for entry in entries: +758 index += 1 +759 # This is the first entry +760 if index == 0: +761 if entry.is_directory(): +762 if self.config.no_colors: +763 print("%s%s\\" % (''.join(prompt+[bars[1]]), entry.get_longname())) +764 else: +765 print("%s\x1b[1;96m%s\x1b[0m\\" % (''.join(prompt+[bars[1]]), entry.get_longname())) +766 recurse_action( +767 base_dir=base_dir, +768 path=path+[entry.get_longname()], +769 prompt=prompt+["│ "] +770 ) +771 else: +772 if self.config.no_colors: +773 print("%s%s" % (''.join(prompt+[bars[1]]), entry.get_longname())) +774 else: +775 print("%s\x1b[1m%s\x1b[0m" % (''.join(prompt+[bars[1]]), entry.get_longname())) +776 +777 # This is the last entry +778 elif index == len(entries): +779 if entry.is_directory(): +780 if self.config.no_colors: +781 print("%s%s\\" % (''.join(prompt+[bars[2]]), entry.get_longname())) +782 else: +783 print("%s\x1b[1;96m%s\x1b[0m\\" % (''.join(prompt+[bars[2]]), entry.get_longname())) +784 recurse_action( +785 base_dir=base_dir, +786 path=path+[entry.get_longname()], +787 prompt=prompt+[" "] +788 ) +789 else: +790 if self.config.no_colors: +791 print("%s%s" % (''.join(prompt+[bars[2]]), entry.get_longname())) +792 else: +793 print("%s\x1b[1m%s\x1b[0m" % (''.join(prompt+[bars[2]]), entry.get_longname())) +794 +795 # These are entries in the middle +796 else: +797 if entry.is_directory(): +798 if self.config.no_colors: +799 print("%s%s\\" % (''.join(prompt+[bars[1]]), entry.get_longname())) +800 else: +801 print("%s\x1b[1;96m%s\x1b[0m\\" % (''.join(prompt+[bars[1]]), entry.get_longname())) +802 recurse_action( +803 base_dir=base_dir, +804 path=path+[entry.get_longname()], +805 prompt=prompt+["│ "] +806 ) +807 else: +808 if self.config.no_colors: +809 print("%s%s" % (''.join(prompt+[bars[1]]), entry.get_longname())) +810 else: +811 print("%s\x1b[1m%s\x1b[0m" % (''.join(prompt+[bars[1]]), entry.get_longname())) +812 +813 # +814 elif len(entries) == 1: +815 entry = entries[0] +816 if entry.is_directory(): +817 if self.config.no_colors: +818 print("%s%s\\" % (''.join(prompt+[bars[2]]), entry.get_longname())) +819 else: +820 print("%s\x1b[1;96m%s\x1b[0m\\" % (''.join(prompt+[bars[2]]), entry.get_longname())) +821 recurse_action( +822 base_dir=base_dir, +823 path=path+[entry.get_longname()], +824 prompt=prompt+[" "] +825 ) +826 else: +827 if self.config.no_colors: +828 print("%s%s" % (''.join(prompt+[bars[2]]), entry.get_longname())) +829 else: +830 print("%s\x1b[1m%s\x1b[0m" % (''.join(prompt+[bars[2]]), entry.get_longname())) +831 +832 # Entrypoint +833 try: +834 if self.config.no_colors: +835 print("%s\\" % path) +836 else: +837 print("\x1b[1;96m%s\x1b[0m\\" % path) +838 recurse_action( +839 base_dir=self.smb_cwd, +840 path=[path], +841 prompt=[""] +842 ) +843 except (BrokenPipeError, KeyboardInterrupt) as e: +844 print("[!] Interrupted.") +845 self.close_smb_session() +846 self.init_smb_session() +
Recursively lists the directory structure of the SMB share starting from the specified path.
+ +This function prints a visual representation of the directory tree of the remote SMB share. It uses +recursion to navigate through directories and lists all files and subdirectories in each directory. +The output is color-coded and formatted to enhance readability, with directories highlighted in cyan.
+ +Args: + path (str, optional): The starting path on the SMB share from which to begin listing the tree. + Defaults to the root of the current share.
+867 def set_cwd(self, path=None): +868 """ +869 Sets the current working directory on the SMB share to the specified path. +870 +871 This method updates the current working directory (cwd) of the SMB session to the given path if it is a valid directory. +872 If the specified path is not a directory, the cwd remains unchanged. +873 +874 Parameters: +875 path (str): The path to set as the current working directory. +876 +877 Raises: +878 ValueError: If the specified path is not a directory. +879 """ +880 +881 if path is not None: +882 # Set path separators to ntpath sep +883 if '/' in path: +884 path = path.replace('/', ntpath.sep) +885 +886 if path.startswith(ntpath.sep): +887 # Absolute path +888 path = path + ntpath.sep +889 else: +890 # Relative path to the CWD +891 if len(self.smb_cwd) == 0: +892 path = path + ntpath.sep +893 else: +894 path = self.smb_cwd + ntpath.sep + path +895 +896 # Path normalization +897 path = ntpath.normpath(path) +898 path = re.sub(r'\\+', r'\\', path) +899 +900 if path in ["", ".", ".."]: +901 self.smb_cwd = "" +902 else: +903 if self.path_isdir(pathFromRoot=path.strip(ntpath.sep)): +904 # Path exists on the remote +905 self.smb_cwd = ntpath.normpath(path) +906 else: +907 # Path does not exists or is not a directory on the remote +908 print("[!] Remote directory '%s' does not exist." % path) +
Sets the current working directory on the SMB share to the specified path.
+ +This method updates the current working directory (cwd) of the SMB session to the given path if it is a valid directory. +If the specified path is not a directory, the cwd remains unchanged.
+ +Parameters: + path (str): The path to set as the current working directory.
+ +Raises: + ValueError: If the specified path is not a directory.
+1#!/usr/bin/env python3 + 2# -*- coding: utf-8 -*- + 3# File name : utils.py + 4# Author : Podalirius (@podalirius_) + 5# Date created : 23 may 2024 + 6 + 7 + 8import datetime + 9import os + 10import ntpath + 11import re + 12import stat + 13 + 14 + 15def parse_lm_nt_hashes(lm_nt_hashes_string): + 16 """ + 17 Parse the input string containing LM and NT hash values and return them separately. + 18 + 19 This function takes a string containing LM and NT hash values, typically separated by a colon (:). + 20 It returns the LM and NT hash values as separate strings. If only one hash value is provided, it is + 21 assumed to be the NT hash and the LM hash is set to its default value. If no valid hash values are + 22 found, both return values are empty strings. + 23 + 24 Args: + 25 lm_nt_hashes_string (str): A string containing LM and NT hash values separated by a colon. + 26 + 27 Returns: + 28 tuple: A tuple containing two strings (lm_hash_value, nt_hash_value). + 29 - lm_hash_value: The LM hash value or its default if not provided. + 30 - nt_hash_value: The NT hash value or its default if not provided. + 31 + 32 Extracted from p0dalirius/sectools library + 33 Src: https://github.com/p0dalirius/sectools/blob/7bb3f5cb7815ad4d4845713c8739e2e2b0ea4e75/sectools/windows/crypto.py#L11-L24 + 34 """ + 35 + 36 lm_hash_value, nt_hash_value = "", "" + 37 if lm_nt_hashes_string is not None: + 38 matched = re.match("([0-9a-f]{32})?(:)?([0-9a-f]{32})?", lm_nt_hashes_string.strip().lower()) + 39 m_lm_hash, m_sep, m_nt_hash = matched.groups() + 40 if m_lm_hash is None and m_sep is None and m_nt_hash is None: + 41 lm_hash_value, nt_hash_value = "", "" + 42 elif m_lm_hash is None and m_nt_hash is not None: + 43 lm_hash_value = "aad3b435b51404eeaad3b435b51404ee" + 44 nt_hash_value = m_nt_hash + 45 elif m_lm_hash is not None and m_nt_hash is None: + 46 lm_hash_value = m_lm_hash + 47 nt_hash_value = "31d6cfe0d16ae931b73c59d7e0c089c0" + 48 return lm_hash_value, nt_hash_value + 49 + 50 + 51def b_filesize(l): + 52 """ + 53 Convert a file size from bytes to a more readable format using the largest appropriate unit. + 54 + 55 This function takes an integer representing a file size in bytes and converts it to a human-readable + 56 string using the largest appropriate unit from bytes (B) to petabytes (PB). The result is rounded to + 57 two decimal places. + 58 + 59 Args: + 60 l (int): The file size in bytes. + 61 + 62 Returns: + 63 str: A string representing the file size in a more readable format, including the appropriate unit. + 64 """ + 65 + 66 units = ['B','kB','MB','GB','TB','PB'] + 67 for k in range(len(units)): + 68 if l < (1024**(k+1)): + 69 break + 70 return "%4.2f %s" % (round(l/(1024**(k)),2), units[k]) + 71 + 72 + 73def unix_permissions(entryname): + 74 """ + 75 Generate a string representing the Unix-style permissions for a given file or directory. + 76 + 77 This function uses the os.lstat() method to retrieve the status of the specified file or directory, + 78 then constructs a string that represents the Unix-style permissions based on the mode of the file. + 79 + 80 Args: + 81 entryname (str): The path to the file or directory for which permissions are being determined. + 82 + 83 Returns: + 84 str: A string of length 10 representing the Unix-style permissions (e.g., '-rwxr-xr--'). + 85 The first character is either 'd' (directory), '-' (not a directory), followed by + 86 three groups of 'r', 'w', 'x' (read, write, execute permissions) for owner, group, + 87 and others respectively. + 88 """ + 89 + 90 mode = os.lstat(entryname).st_mode + 91 permissions = [] + 92 + 93 permissions.append('d' if stat.S_ISDIR(mode) else '-') + 94 + 95 permissions.append('r' if mode & stat.S_IRUSR else '-') + 96 permissions.append('w' if mode & stat.S_IWUSR else '-') + 97 permissions.append('x' if mode & stat.S_IXUSR else '-') + 98 + 99 permissions.append('r' if mode & stat.S_IRGRP else '-') +100 permissions.append('w' if mode & stat.S_IWGRP else '-') +101 permissions.append('x' if mode & stat.S_IXGRP else '-') +102 +103 permissions.append('r' if mode & stat.S_IROTH else '-') +104 permissions.append('w' if mode & stat.S_IWOTH else '-') +105 permissions.append('x' if mode & stat.S_IXOTH else '-') +106 +107 return ''.join(permissions) +108 +109 +110def STYPE_MASK(stype_value): +111 """ +112 Extracts the share type flags from a given share type value. +113 +114 This function uses bitwise operations to determine which share type flags are set in the provided `stype_value`. +115 It checks against known share type flags and returns a list of the flags that are set. +116 +117 Parameters: +118 stype_value (int): The share type value to analyze, typically obtained from SMB share properties. +119 +120 Returns: +121 list: A list of strings, where each string represents a share type flag that is set in the input value. +122 """ +123 +124 known_flags = { +125 ## One of the following values may be specified. You can isolate these values by using the STYPE_MASK value. +126 # Disk drive. +127 "STYPE_DISKTREE": 0x0, +128 +129 # Print queue. +130 "STYPE_PRINTQ": 0x1, +131 +132 # Communication device. +133 "STYPE_DEVICE": 0x2, +134 +135 # Interprocess communication (IPC). +136 "STYPE_IPC": 0x3, +137 +138 ## In addition, one or both of the following values may be specified. +139 # Special share reserved for interprocess communication (IPC$) or remote administration of the server (ADMIN$). +140 # Can also refer to administrative shares such as C$, D$, E$, and so forth. For more information, see Network Share Functions. +141 "STYPE_SPECIAL": 0x80000000, +142 +143 # A temporary share. +144 "STYPE_TEMPORARY": 0x40000000 +145 } +146 flags = [] +147 if (stype_value & 0b11) == known_flags["STYPE_DISKTREE"]: +148 flags.append("STYPE_DISKTREE") +149 elif (stype_value & 0b11) == known_flags["STYPE_PRINTQ"]: +150 flags.append("STYPE_PRINTQ") +151 elif (stype_value & 0b11) == known_flags["STYPE_DEVICE"]: +152 flags.append("STYPE_DEVICE") +153 elif (stype_value & 0b11) == known_flags["STYPE_IPC"]: +154 flags.append("STYPE_IPC") +155 if (stype_value & known_flags["STYPE_SPECIAL"]) == known_flags["STYPE_SPECIAL"]: +156 flags.append("STYPE_SPECIAL") +157 if (stype_value & known_flags["STYPE_TEMPORARY"]) == known_flags["STYPE_TEMPORARY"]: +158 flags.append("STYPE_TEMPORARY") +159 return flags +160 +161 +162def windows_ls_entry(entry, config, pathToPrint=None): +163 """ +164 This function generates a metadata string based on the attributes of the provided entry object. +165 +166 Parameters: +167 entry (object): An object representing a file or directory entry. +168 +169 Returns: +170 str: A string representing the metadata of the entry, including attributes like directory, archive, compressed, hidden, normal, readonly, system, and temporary. +171 """ +172 +173 if pathToPrint is not None: +174 pathToPrint = pathToPrint + ntpath.sep + entry.get_longname() +175 else: +176 pathToPrint = entry.get_longname() +177 +178 meta_string = "" +179 meta_string += ("d" if entry.is_directory() else "-") +180 meta_string += ("a" if entry.is_archive() else "-") +181 meta_string += ("c" if entry.is_compressed() else "-") +182 meta_string += ("h" if entry.is_hidden() else "-") +183 meta_string += ("n" if entry.is_normal() else "-") +184 meta_string += ("r" if entry.is_readonly() else "-") +185 meta_string += ("s" if entry.is_system() else "-") +186 meta_string += ("t" if entry.is_temporary() else "-") +187 +188 size_str = b_filesize(entry.get_filesize()) +189 +190 date_str = datetime.datetime.fromtimestamp(entry.get_atime_epoch()).strftime("%Y-%m-%d %H:%M") +191 +192 if entry.is_directory(): +193 if config.no_colors: +194 print("%s %10s %s %s\\" % (meta_string, size_str, date_str, pathToPrint)) +195 else: +196 print("%s %10s %s \x1b[1;96m%s\x1b[0m\\" % (meta_string, size_str, date_str, pathToPrint)) +197 else: +198 if config.no_colors: +199 print("%s %10s %s %s" % (meta_string, size_str, date_str, pathToPrint)) +200 else: +201 print("%s %10s %s \x1b[1m%s\x1b[0m" % (meta_string, size_str, date_str, pathToPrint)) +202 +203 +204def local_tree(path, config): +205 """ +206 This function recursively lists the contents of a directory in a tree-like format. +207 +208 Parameters: +209 path (str): The path to the directory to list. +210 config (object): Configuration settings which may affect the output, such as whether to use colors. +211 +212 Returns: +213 None: This function does not return anything but prints the directory tree to the console. +214 """ +215 +216 def recurse_action(base_dir="", path=[], prompt=[]): +217 bars = ["│ ", "├── ", "└── "] +218 +219 local_path = os.path.normpath(base_dir + os.path.sep + os.path.sep.join(path) + os.path.sep) +220 +221 entries = [] +222 try: +223 entries = os.listdir(local_path) +224 except Exception as err: +225 if config.no_colors: +226 print("%s%s" % (''.join(prompt+[bars[2]]), err)) +227 else: +228 print("%s\x1b[1;91m%s\x1b[0m" % (''.join(prompt+[bars[2]]), err)) +229 return +230 +231 entries = sorted(entries) +232 +233 # +234 if len(entries) > 1: +235 index = 0 +236 for entry in entries: +237 index += 1 +238 # This is the first entry +239 if index == 0: +240 if os.path.isdir(local_path + os.path.sep + entry): +241 if config.no_colors: +242 print("%s%s%s" % (''.join(prompt+[bars[1]]), entry, os.path.sep)) +243 else: +244 print("%s\x1b[1;96m%s\x1b[0m%s" % (''.join(prompt+[bars[1]]), entry, os.path.sep)) +245 recurse_action( +246 base_dir=base_dir, +247 path=path+[entry], +248 prompt=prompt+["│ "] +249 ) +250 else: +251 if config.no_colors: +252 print("%s%s" % (''.join(prompt+[bars[1]]), entry)) +253 else: +254 print("%s\x1b[1m%s\x1b[0m" % (''.join(prompt+[bars[1]]), entry)) +255 +256 # This is the last entry +257 elif index == len(entries): +258 if os.path.isdir(local_path + os.path.sep + entry): +259 if config.no_colors: +260 print("%s%s%s" % (''.join(prompt+[bars[2]]), entry, os.path.sep)) +261 else: +262 print("%s\x1b[1;96m%s\x1b[0m%s" % (''.join(prompt+[bars[2]]), entry, os.path.sep)) +263 recurse_action( +264 base_dir=base_dir, +265 path=path+[entry], +266 prompt=prompt+[" "] +267 ) +268 else: +269 if config.no_colors: +270 print("%s%s" % (''.join(prompt+[bars[2]]), entry)) +271 else: +272 print("%s\x1b[1m%s\x1b[0m" % (''.join(prompt+[bars[2]]), entry)) +273 +274 # These are entries in the middle +275 else: +276 if os.path.isdir(local_path + os.path.sep + entry): +277 if config.no_colors: +278 print("%s%s%s" % (''.join(prompt+[bars[1]]), entry, os.path.sep)) +279 else: +280 print("%s\x1b[1;96m%s\x1b[0m%s" % (''.join(prompt+[bars[1]]), entry, os.path.sep)) +281 recurse_action( +282 base_dir=base_dir, +283 path=path+[entry], +284 prompt=prompt+["│ "] +285 ) +286 else: +287 if config.no_colors: +288 print("%s%s" % (''.join(prompt+[bars[1]]), entry)) +289 else: +290 print("%s\x1b[1m%s\x1b[0m" % (''.join(prompt+[bars[1]]), entry)) +291 +292 # +293 elif len(entries) == 1: +294 entry = entries[0] +295 if os.path.isdir(local_path + os.path.sep + entry): +296 if config.no_colors: +297 print("%s%s%s" % (''.join(prompt+[bars[2]]), entry, os.path.sep)) +298 else: +299 print("%s\x1b[1;96m%s\x1b[0m%s" % (''.join(prompt+[bars[2]]), entry, os.path.sep)) +300 recurse_action( +301 base_dir=base_dir, +302 path=path+[entry], +303 prompt=prompt+[" "] +304 ) +305 else: +306 if config.no_colors: +307 print("%s%s" % (''.join(prompt+[bars[2]]), entry)) +308 else: +309 print("%s\x1b[1m%s\x1b[0m" % (''.join(prompt+[bars[2]]), entry)) +310 +311 # Entrypoint +312 try: +313 if config.no_colors: +314 print("%s%s" % (path, os.path.sep)) +315 else: +316 print("\x1b[1;96m%s\x1b[0m%s" % (path, os.path.sep)) +317 recurse_action( +318 base_dir=os.getcwd(), +319 path=[path], +320 prompt=[""] +321 ) +322 except (BrokenPipeError, KeyboardInterrupt) as e: +323 print("[!] Interrupted.") +
16def parse_lm_nt_hashes(lm_nt_hashes_string): +17 """ +18 Parse the input string containing LM and NT hash values and return them separately. +19 +20 This function takes a string containing LM and NT hash values, typically separated by a colon (:). +21 It returns the LM and NT hash values as separate strings. If only one hash value is provided, it is +22 assumed to be the NT hash and the LM hash is set to its default value. If no valid hash values are +23 found, both return values are empty strings. +24 +25 Args: +26 lm_nt_hashes_string (str): A string containing LM and NT hash values separated by a colon. +27 +28 Returns: +29 tuple: A tuple containing two strings (lm_hash_value, nt_hash_value). +30 - lm_hash_value: The LM hash value or its default if not provided. +31 - nt_hash_value: The NT hash value or its default if not provided. +32 +33 Extracted from p0dalirius/sectools library +34 Src: https://github.com/p0dalirius/sectools/blob/7bb3f5cb7815ad4d4845713c8739e2e2b0ea4e75/sectools/windows/crypto.py#L11-L24 +35 """ +36 +37 lm_hash_value, nt_hash_value = "", "" +38 if lm_nt_hashes_string is not None: +39 matched = re.match("([0-9a-f]{32})?(:)?([0-9a-f]{32})?", lm_nt_hashes_string.strip().lower()) +40 m_lm_hash, m_sep, m_nt_hash = matched.groups() +41 if m_lm_hash is None and m_sep is None and m_nt_hash is None: +42 lm_hash_value, nt_hash_value = "", "" +43 elif m_lm_hash is None and m_nt_hash is not None: +44 lm_hash_value = "aad3b435b51404eeaad3b435b51404ee" +45 nt_hash_value = m_nt_hash +46 elif m_lm_hash is not None and m_nt_hash is None: +47 lm_hash_value = m_lm_hash +48 nt_hash_value = "31d6cfe0d16ae931b73c59d7e0c089c0" +49 return lm_hash_value, nt_hash_value +
Parse the input string containing LM and NT hash values and return them separately.
+ +This function takes a string containing LM and NT hash values, typically separated by a colon (:). +It returns the LM and NT hash values as separate strings. If only one hash value is provided, it is +assumed to be the NT hash and the LM hash is set to its default value. If no valid hash values are +found, both return values are empty strings.
+ +Args: + lm_nt_hashes_string (str): A string containing LM and NT hash values separated by a colon.
+ +Returns: + tuple: A tuple containing two strings (lm_hash_value, nt_hash_value). + - lm_hash_value: The LM hash value or its default if not provided. + - nt_hash_value: The NT hash value or its default if not provided.
+ +Extracted from p0dalirius/sectools library +Src: https://github.com/p0dalirius/sectools/blob/7bb3f5cb7815ad4d4845713c8739e2e2b0ea4e75/sectools/windows/crypto.py#L11-L24
+52def b_filesize(l): +53 """ +54 Convert a file size from bytes to a more readable format using the largest appropriate unit. +55 +56 This function takes an integer representing a file size in bytes and converts it to a human-readable +57 string using the largest appropriate unit from bytes (B) to petabytes (PB). The result is rounded to +58 two decimal places. +59 +60 Args: +61 l (int): The file size in bytes. +62 +63 Returns: +64 str: A string representing the file size in a more readable format, including the appropriate unit. +65 """ +66 +67 units = ['B','kB','MB','GB','TB','PB'] +68 for k in range(len(units)): +69 if l < (1024**(k+1)): +70 break +71 return "%4.2f %s" % (round(l/(1024**(k)),2), units[k]) +
Convert a file size from bytes to a more readable format using the largest appropriate unit.
+ +This function takes an integer representing a file size in bytes and converts it to a human-readable +string using the largest appropriate unit from bytes (B) to petabytes (PB). The result is rounded to +two decimal places.
+ +Args: + l (int): The file size in bytes.
+ +Returns: + str: A string representing the file size in a more readable format, including the appropriate unit.
+74def unix_permissions(entryname): + 75 """ + 76 Generate a string representing the Unix-style permissions for a given file or directory. + 77 + 78 This function uses the os.lstat() method to retrieve the status of the specified file or directory, + 79 then constructs a string that represents the Unix-style permissions based on the mode of the file. + 80 + 81 Args: + 82 entryname (str): The path to the file or directory for which permissions are being determined. + 83 + 84 Returns: + 85 str: A string of length 10 representing the Unix-style permissions (e.g., '-rwxr-xr--'). + 86 The first character is either 'd' (directory), '-' (not a directory), followed by + 87 three groups of 'r', 'w', 'x' (read, write, execute permissions) for owner, group, + 88 and others respectively. + 89 """ + 90 + 91 mode = os.lstat(entryname).st_mode + 92 permissions = [] + 93 + 94 permissions.append('d' if stat.S_ISDIR(mode) else '-') + 95 + 96 permissions.append('r' if mode & stat.S_IRUSR else '-') + 97 permissions.append('w' if mode & stat.S_IWUSR else '-') + 98 permissions.append('x' if mode & stat.S_IXUSR else '-') + 99 +100 permissions.append('r' if mode & stat.S_IRGRP else '-') +101 permissions.append('w' if mode & stat.S_IWGRP else '-') +102 permissions.append('x' if mode & stat.S_IXGRP else '-') +103 +104 permissions.append('r' if mode & stat.S_IROTH else '-') +105 permissions.append('w' if mode & stat.S_IWOTH else '-') +106 permissions.append('x' if mode & stat.S_IXOTH else '-') +107 +108 return ''.join(permissions) +
Generate a string representing the Unix-style permissions for a given file or directory.
+ +This function uses the os.lstat() method to retrieve the status of the specified file or directory, +then constructs a string that represents the Unix-style permissions based on the mode of the file.
+ +Args: + entryname (str): The path to the file or directory for which permissions are being determined.
+ +Returns: + str: A string of length 10 representing the Unix-style permissions (e.g., '-rwxr-xr--'). + The first character is either 'd' (directory), '-' (not a directory), followed by + three groups of 'r', 'w', 'x' (read, write, execute permissions) for owner, group, + and others respectively.
+111def STYPE_MASK(stype_value): +112 """ +113 Extracts the share type flags from a given share type value. +114 +115 This function uses bitwise operations to determine which share type flags are set in the provided `stype_value`. +116 It checks against known share type flags and returns a list of the flags that are set. +117 +118 Parameters: +119 stype_value (int): The share type value to analyze, typically obtained from SMB share properties. +120 +121 Returns: +122 list: A list of strings, where each string represents a share type flag that is set in the input value. +123 """ +124 +125 known_flags = { +126 ## One of the following values may be specified. You can isolate these values by using the STYPE_MASK value. +127 # Disk drive. +128 "STYPE_DISKTREE": 0x0, +129 +130 # Print queue. +131 "STYPE_PRINTQ": 0x1, +132 +133 # Communication device. +134 "STYPE_DEVICE": 0x2, +135 +136 # Interprocess communication (IPC). +137 "STYPE_IPC": 0x3, +138 +139 ## In addition, one or both of the following values may be specified. +140 # Special share reserved for interprocess communication (IPC$) or remote administration of the server (ADMIN$). +141 # Can also refer to administrative shares such as C$, D$, E$, and so forth. For more information, see Network Share Functions. +142 "STYPE_SPECIAL": 0x80000000, +143 +144 # A temporary share. +145 "STYPE_TEMPORARY": 0x40000000 +146 } +147 flags = [] +148 if (stype_value & 0b11) == known_flags["STYPE_DISKTREE"]: +149 flags.append("STYPE_DISKTREE") +150 elif (stype_value & 0b11) == known_flags["STYPE_PRINTQ"]: +151 flags.append("STYPE_PRINTQ") +152 elif (stype_value & 0b11) == known_flags["STYPE_DEVICE"]: +153 flags.append("STYPE_DEVICE") +154 elif (stype_value & 0b11) == known_flags["STYPE_IPC"]: +155 flags.append("STYPE_IPC") +156 if (stype_value & known_flags["STYPE_SPECIAL"]) == known_flags["STYPE_SPECIAL"]: +157 flags.append("STYPE_SPECIAL") +158 if (stype_value & known_flags["STYPE_TEMPORARY"]) == known_flags["STYPE_TEMPORARY"]: +159 flags.append("STYPE_TEMPORARY") +160 return flags +
Extracts the share type flags from a given share type value.
+ +This function uses bitwise operations to determine which share type flags are set in the provided stype_value
.
+It checks against known share type flags and returns a list of the flags that are set.
Parameters: + stype_value (int): The share type value to analyze, typically obtained from SMB share properties.
+ +Returns: + list: A list of strings, where each string represents a share type flag that is set in the input value.
+163def windows_ls_entry(entry, config, pathToPrint=None): +164 """ +165 This function generates a metadata string based on the attributes of the provided entry object. +166 +167 Parameters: +168 entry (object): An object representing a file or directory entry. +169 +170 Returns: +171 str: A string representing the metadata of the entry, including attributes like directory, archive, compressed, hidden, normal, readonly, system, and temporary. +172 """ +173 +174 if pathToPrint is not None: +175 pathToPrint = pathToPrint + ntpath.sep + entry.get_longname() +176 else: +177 pathToPrint = entry.get_longname() +178 +179 meta_string = "" +180 meta_string += ("d" if entry.is_directory() else "-") +181 meta_string += ("a" if entry.is_archive() else "-") +182 meta_string += ("c" if entry.is_compressed() else "-") +183 meta_string += ("h" if entry.is_hidden() else "-") +184 meta_string += ("n" if entry.is_normal() else "-") +185 meta_string += ("r" if entry.is_readonly() else "-") +186 meta_string += ("s" if entry.is_system() else "-") +187 meta_string += ("t" if entry.is_temporary() else "-") +188 +189 size_str = b_filesize(entry.get_filesize()) +190 +191 date_str = datetime.datetime.fromtimestamp(entry.get_atime_epoch()).strftime("%Y-%m-%d %H:%M") +192 +193 if entry.is_directory(): +194 if config.no_colors: +195 print("%s %10s %s %s\\" % (meta_string, size_str, date_str, pathToPrint)) +196 else: +197 print("%s %10s %s \x1b[1;96m%s\x1b[0m\\" % (meta_string, size_str, date_str, pathToPrint)) +198 else: +199 if config.no_colors: +200 print("%s %10s %s %s" % (meta_string, size_str, date_str, pathToPrint)) +201 else: +202 print("%s %10s %s \x1b[1m%s\x1b[0m" % (meta_string, size_str, date_str, pathToPrint)) +
This function generates a metadata string based on the attributes of the provided entry object.
+ +Parameters: + entry (object): An object representing a file or directory entry.
+ +Returns: + str: A string representing the metadata of the entry, including attributes like directory, archive, compressed, hidden, normal, readonly, system, and temporary.
+205def local_tree(path, config): +206 """ +207 This function recursively lists the contents of a directory in a tree-like format. +208 +209 Parameters: +210 path (str): The path to the directory to list. +211 config (object): Configuration settings which may affect the output, such as whether to use colors. +212 +213 Returns: +214 None: This function does not return anything but prints the directory tree to the console. +215 """ +216 +217 def recurse_action(base_dir="", path=[], prompt=[]): +218 bars = ["│ ", "├── ", "└── "] +219 +220 local_path = os.path.normpath(base_dir + os.path.sep + os.path.sep.join(path) + os.path.sep) +221 +222 entries = [] +223 try: +224 entries = os.listdir(local_path) +225 except Exception as err: +226 if config.no_colors: +227 print("%s%s" % (''.join(prompt+[bars[2]]), err)) +228 else: +229 print("%s\x1b[1;91m%s\x1b[0m" % (''.join(prompt+[bars[2]]), err)) +230 return +231 +232 entries = sorted(entries) +233 +234 # +235 if len(entries) > 1: +236 index = 0 +237 for entry in entries: +238 index += 1 +239 # This is the first entry +240 if index == 0: +241 if os.path.isdir(local_path + os.path.sep + entry): +242 if config.no_colors: +243 print("%s%s%s" % (''.join(prompt+[bars[1]]), entry, os.path.sep)) +244 else: +245 print("%s\x1b[1;96m%s\x1b[0m%s" % (''.join(prompt+[bars[1]]), entry, os.path.sep)) +246 recurse_action( +247 base_dir=base_dir, +248 path=path+[entry], +249 prompt=prompt+["│ "] +250 ) +251 else: +252 if config.no_colors: +253 print("%s%s" % (''.join(prompt+[bars[1]]), entry)) +254 else: +255 print("%s\x1b[1m%s\x1b[0m" % (''.join(prompt+[bars[1]]), entry)) +256 +257 # This is the last entry +258 elif index == len(entries): +259 if os.path.isdir(local_path + os.path.sep + entry): +260 if config.no_colors: +261 print("%s%s%s" % (''.join(prompt+[bars[2]]), entry, os.path.sep)) +262 else: +263 print("%s\x1b[1;96m%s\x1b[0m%s" % (''.join(prompt+[bars[2]]), entry, os.path.sep)) +264 recurse_action( +265 base_dir=base_dir, +266 path=path+[entry], +267 prompt=prompt+[" "] +268 ) +269 else: +270 if config.no_colors: +271 print("%s%s" % (''.join(prompt+[bars[2]]), entry)) +272 else: +273 print("%s\x1b[1m%s\x1b[0m" % (''.join(prompt+[bars[2]]), entry)) +274 +275 # These are entries in the middle +276 else: +277 if os.path.isdir(local_path + os.path.sep + entry): +278 if config.no_colors: +279 print("%s%s%s" % (''.join(prompt+[bars[1]]), entry, os.path.sep)) +280 else: +281 print("%s\x1b[1;96m%s\x1b[0m%s" % (''.join(prompt+[bars[1]]), entry, os.path.sep)) +282 recurse_action( +283 base_dir=base_dir, +284 path=path+[entry], +285 prompt=prompt+["│ "] +286 ) +287 else: +288 if config.no_colors: +289 print("%s%s" % (''.join(prompt+[bars[1]]), entry)) +290 else: +291 print("%s\x1b[1m%s\x1b[0m" % (''.join(prompt+[bars[1]]), entry)) +292 +293 # +294 elif len(entries) == 1: +295 entry = entries[0] +296 if os.path.isdir(local_path + os.path.sep + entry): +297 if config.no_colors: +298 print("%s%s%s" % (''.join(prompt+[bars[2]]), entry, os.path.sep)) +299 else: +300 print("%s\x1b[1;96m%s\x1b[0m%s" % (''.join(prompt+[bars[2]]), entry, os.path.sep)) +301 recurse_action( +302 base_dir=base_dir, +303 path=path+[entry], +304 prompt=prompt+[" "] +305 ) +306 else: +307 if config.no_colors: +308 print("%s%s" % (''.join(prompt+[bars[2]]), entry)) +309 else: +310 print("%s\x1b[1m%s\x1b[0m" % (''.join(prompt+[bars[2]]), entry)) +311 +312 # Entrypoint +313 try: +314 if config.no_colors: +315 print("%s%s" % (path, os.path.sep)) +316 else: +317 print("\x1b[1;96m%s\x1b[0m%s" % (path, os.path.sep)) +318 recurse_action( +319 base_dir=os.getcwd(), +320 path=[path], +321 prompt=[""] +322 ) +323 except (BrokenPipeError, KeyboardInterrupt) as e: +324 print("[!] Interrupted.") +
This function recursively lists the contents of a directory in a tree-like format.
+ +Parameters: + path (str): The path to the directory to list. + config (object): Configuration settings which may affect the output, such as whether to use colors.
+ +Returns: + None: This function does not return anything but prints the directory tree to the console.
+1#!/usr/bin/env python3 + 2# -*- coding: utf-8 -*- + 3# File name : InteractiveShell.py + 4# Author : Podalirius (@podalirius_) + 5# Date created : 23 may 2024 + 6 + 7 + 8import impacket + 9import ntpath + 10import re + 11from smbclientng.core.Module import Module + 12from smbclientng.core.ModuleArgumentParser import ModuleArgumentParser + 13from smbclientng.core.utils import windows_ls_entry + 14 + 15 + 16class Find(Module): + 17 """ + 18 A class to search for files in a directory hierarchy. + 19 + 20 This class provides functionality to search for files based on various criteria in a directory hierarchy. + 21 """ + 22 + 23 name = "find" + 24 description = "Search for files in a directory hierarchy" + 25 + 26 def parseArgs(self, arguments): + 27 """ + 28 Parses the command line arguments provided to the module. + 29 + 30 This method initializes the argument parser with the module's name and description, and defines all the necessary arguments that the module accepts. It then parses the provided command line arguments based on these definitions. + 31 + 32 Args: + 33 arguments (str): A string of command line arguments. + 34 + 35 Returns: + 36 ModuleArgumentParser.Namespace | None: The parsed arguments as a Namespace object if successful, None if there are no arguments or help is requested. + 37 """ + 38 + 39 parser = ModuleArgumentParser(prog=self.name, description=self.description) + 40 + 41 # Adding positional arguments + 42 parser.add_argument("paths", metavar="PATH", type=str, nargs="*", default=[], help="The starting point(s) for the search.") + 43 + 44 # Adding tests, actions, and options for expressions (incomplete for brevity) + 45 parser.add_argument("-name", type=str, help="Base of file name (the path with the leading directories removed).") + 46 parser.add_argument("-iname", type=str, help="Like -name, but the match is case insensitive.") + 47 parser.add_argument("-type", type=str, default=None, help="File type (e.g., f for regular file, d for directory).") + 48 parser.add_argument("-size", type=str, help="File uses n units of space.") + 49 # parser.add_argument("-mtime", type=str, help="File's data was last modified n*24 hours ago") + 50 # parser.add_argument("-ctime", type=str, help="File's status was last changed n*24 hours ago") + 51 # parser.add_argument("-atime", type=str, help="File was last accessed n*24 hours ago") + 52 + 53 # Adding actions + 54 parser.add_argument("-ls", action="store_true", default=False, help="List current file in ls -dils format on standard output.") + 55 parser.add_argument("-download", action="store_true", default=False, help="List current file in ls -dils format on standard output.") + 56 + 57 # Other options (incomplete for brevity) + 58 parser.add_argument("-maxdepth", type=int, help="Descend at most levels (a non-negative integer) levels of directories below the command line arguments.") + 59 parser.add_argument("-mindepth", type=int, help="Do not apply any tests or actions at levels less than levels (a non-negative integer).") + 60 + 61 if len(arguments.strip()) == 0: + 62 parser.print_help() + 63 return None + 64 else: + 65 self.options = self.processArguments(parser, arguments) + 66 + 67 if self.options is not None: + 68 if len(self.options.paths) == 0: + 69 parser.print_help() + 70 self.options = None + 71 + 72 return self.options + 73 + 74 def __recurse_action(self, paths=[], depth=0): + 75 """ + 76 Recursively searches for files in a directory hierarchy and prints the results based on specified criteria. + 77 + 78 Args: + 79 base_dir (str): The base directory to start the search from. + 80 paths (list): List of paths to search within the base directory. + 81 depth (int): The current depth level in the directory hierarchy. + 82 + 83 Returns: + 84 None + 85 """ + 86 + 87 next_directories_to_explore = [] + 88 + 89 for path in paths: + 90 remote_smb_path = ntpath.normpath(self.smbSession.smb_cwd + ntpath.sep + path) + 91 + 92 entries = [] + 93 try: + 94 entries = self.smbSession.smbClient.listPath( + 95 shareName=self.smbSession.smb_share, + 96 path=(remote_smb_path + ntpath.sep + '*') + 97 ) + 98 except impacket.smbconnection.SessionError as err: + 99 continue +100 # Remove dot names +101 entries = [e for e in entries if e.get_longname() not in [".", ".."]] +102 # Sort the entries ignoring case +103 entries = sorted(entries, key=lambda x:x.get_longname().lower()) +104 +105 # Match and print results +106 do_print_results = True +107 if self.options.mindepth is not None: +108 if depth < self.options.mindepth: +109 do_print_results = False +110 if self.options.maxdepth is not None: +111 if depth > self.options.maxdepth: +112 do_print_results = False +113 +114 if do_print_results: +115 for entry in entries: +116 do_print_entry = False +117 # Print directory +118 if entry.is_directory(): +119 if (self.options.type == 'd' or self.options.type is None): +120 # No name filtering +121 if self.options.name is None and self.options.iname is None: +122 do_print_entry = True +123 +124 # Filtering on names case sensitive +125 elif self.options.name is not None: +126 if '*' in self.options.name: +127 regex = self.options.name +128 regex = regex.replace('.', '\\.') +129 regex = regex.replace('*', '.*') +130 regex = '^' + regex + '$' +131 if re.match(regex, entry.get_longname()): +132 do_print_entry = True +133 else: +134 do_print_entry = False +135 else: +136 do_print_entry = (entry.get_longname().lower() == self.options.name.lower()) +137 +138 # Filtering on names case insensitive +139 elif self.options.iname is not None: +140 if '*' in self.options.iname: +141 regex = self.options.iname +142 regex = regex.replace('.', '\\.') +143 regex = regex.replace('*', '.*') +144 regex = '^' + regex + '$' +145 if re.match(regex, entry.get_longname(), re.IGNORECASE): +146 do_print_entry = True +147 else: +148 do_print_entry = False +149 else: +150 do_print_entry = (entry.get_longname().lower() == self.options.iname.lower()) +151 +152 # Print file +153 else: +154 if (self.options.type == 'f' or self.options.type is None): +155 # No name filtering +156 if self.options.name is None and self.options.iname is None: +157 do_print_entry = True +158 +159 # Filtering on names case sensitive +160 elif self.options.name is not None: +161 if '*' in self.options.name: +162 regex = self.options.name +163 regex = regex.replace('.', '\\.') +164 regex = regex.replace('*', '.*') +165 regex = '^' + regex + '$' +166 if re.match(regex, entry.get_longname()): +167 do_print_entry = True +168 else: +169 do_print_entry = False +170 else: +171 do_print_entry = (entry.get_longname().lower() == self.options.name.lower()) +172 +173 # Filtering on names case insensitive +174 elif self.options.iname is not None: +175 if '*' in self.options.iname: +176 regex = self.options.iname +177 regex = regex.replace('.', '\\.') +178 regex = regex.replace('*', '.*') +179 regex = '^' + regex + '$' +180 if re.match(regex, entry.get_longname(), re.IGNORECASE): +181 do_print_entry = True +182 else: +183 do_print_entry = False +184 else: +185 do_print_entry = (entry.get_longname().lower() == self.options.iname.lower()) +186 +187 if do_print_entry: +188 # Actions on matches +189 if self.options.download: +190 if entry.is_directory(): +191 self.smbSession.get_file_recursively(path=(path + entry.get_longname() + ntpath.sep)) +192 else: +193 self.smbSession.get_file(path=(path + entry.get_longname() + ntpath.sep), keepRemotePath=True) +194 # Output formats +195 if self.options.ls: +196 if entry.is_directory(): +197 windows_ls_entry(entry, (path + entry.get_longname() + ntpath.sep)) +198 else: +199 windows_ls_entry(entry, (path + entry.get_longname())) +200 else: +201 if entry.is_directory(): +202 print("%s" % (path + entry.get_longname() + ntpath.sep)) +203 else: +204 print("%s" % (path + entry.get_longname())) +205 +206 # Next directories to explore +207 for entry in entries: +208 if entry.is_directory(): +209 next_directories_to_explore.append(path + entry.get_longname() + ntpath.sep) +210 +211 return next_directories_to_explore +212 +213 def run(self, arguments): +214 """ +215 This function recursively searches for files in a directory hierarchy and prints the results based on specified criteria. +216 +217 Args: +218 base_dir (str): The base directory to start the search from. +219 paths (list): List of paths to search within the base directory. +220 depth (int): The current depth level in the directory hierarchy. +221 +222 Returns: +223 None +224 """ +225 +226 self.options = self.parseArgs(arguments=arguments) +227 +228 if self.options is not None: +229 # Entrypoint +230 try: +231 next_directories_to_explore = [] +232 for path in list(set(self.options.paths)): +233 next_directories_to_explore.append(ntpath.normpath(path) + ntpath.sep) +234 next_directories_to_explore = sorted(list(set(next_directories_to_explore))) +235 +236 depth = 0 +237 +238 while len(next_directories_to_explore) != 0: +239 next_directories_to_explore = self.__recurse_action( +240 paths=next_directories_to_explore, +241 depth=depth +242 ) +243 depth = depth + 1 +244 +245 except (BrokenPipeError, KeyboardInterrupt) as e: +246 print("[!] Interrupted.") +247 self.smbSession.close_smb_session() +248 self.smbSession.init_smb_session() +
17class Find(Module): + 18 """ + 19 A class to search for files in a directory hierarchy. + 20 + 21 This class provides functionality to search for files based on various criteria in a directory hierarchy. + 22 """ + 23 + 24 name = "find" + 25 description = "Search for files in a directory hierarchy" + 26 + 27 def parseArgs(self, arguments): + 28 """ + 29 Parses the command line arguments provided to the module. + 30 + 31 This method initializes the argument parser with the module's name and description, and defines all the necessary arguments that the module accepts. It then parses the provided command line arguments based on these definitions. + 32 + 33 Args: + 34 arguments (str): A string of command line arguments. + 35 + 36 Returns: + 37 ModuleArgumentParser.Namespace | None: The parsed arguments as a Namespace object if successful, None if there are no arguments or help is requested. + 38 """ + 39 + 40 parser = ModuleArgumentParser(prog=self.name, description=self.description) + 41 + 42 # Adding positional arguments + 43 parser.add_argument("paths", metavar="PATH", type=str, nargs="*", default=[], help="The starting point(s) for the search.") + 44 + 45 # Adding tests, actions, and options for expressions (incomplete for brevity) + 46 parser.add_argument("-name", type=str, help="Base of file name (the path with the leading directories removed).") + 47 parser.add_argument("-iname", type=str, help="Like -name, but the match is case insensitive.") + 48 parser.add_argument("-type", type=str, default=None, help="File type (e.g., f for regular file, d for directory).") + 49 parser.add_argument("-size", type=str, help="File uses n units of space.") + 50 # parser.add_argument("-mtime", type=str, help="File's data was last modified n*24 hours ago") + 51 # parser.add_argument("-ctime", type=str, help="File's status was last changed n*24 hours ago") + 52 # parser.add_argument("-atime", type=str, help="File was last accessed n*24 hours ago") + 53 + 54 # Adding actions + 55 parser.add_argument("-ls", action="store_true", default=False, help="List current file in ls -dils format on standard output.") + 56 parser.add_argument("-download", action="store_true", default=False, help="List current file in ls -dils format on standard output.") + 57 + 58 # Other options (incomplete for brevity) + 59 parser.add_argument("-maxdepth", type=int, help="Descend at most levels (a non-negative integer) levels of directories below the command line arguments.") + 60 parser.add_argument("-mindepth", type=int, help="Do not apply any tests or actions at levels less than levels (a non-negative integer).") + 61 + 62 if len(arguments.strip()) == 0: + 63 parser.print_help() + 64 return None + 65 else: + 66 self.options = self.processArguments(parser, arguments) + 67 + 68 if self.options is not None: + 69 if len(self.options.paths) == 0: + 70 parser.print_help() + 71 self.options = None + 72 + 73 return self.options + 74 + 75 def __recurse_action(self, paths=[], depth=0): + 76 """ + 77 Recursively searches for files in a directory hierarchy and prints the results based on specified criteria. + 78 + 79 Args: + 80 base_dir (str): The base directory to start the search from. + 81 paths (list): List of paths to search within the base directory. + 82 depth (int): The current depth level in the directory hierarchy. + 83 + 84 Returns: + 85 None + 86 """ + 87 + 88 next_directories_to_explore = [] + 89 + 90 for path in paths: + 91 remote_smb_path = ntpath.normpath(self.smbSession.smb_cwd + ntpath.sep + path) + 92 + 93 entries = [] + 94 try: + 95 entries = self.smbSession.smbClient.listPath( + 96 shareName=self.smbSession.smb_share, + 97 path=(remote_smb_path + ntpath.sep + '*') + 98 ) + 99 except impacket.smbconnection.SessionError as err: +100 continue +101 # Remove dot names +102 entries = [e for e in entries if e.get_longname() not in [".", ".."]] +103 # Sort the entries ignoring case +104 entries = sorted(entries, key=lambda x:x.get_longname().lower()) +105 +106 # Match and print results +107 do_print_results = True +108 if self.options.mindepth is not None: +109 if depth < self.options.mindepth: +110 do_print_results = False +111 if self.options.maxdepth is not None: +112 if depth > self.options.maxdepth: +113 do_print_results = False +114 +115 if do_print_results: +116 for entry in entries: +117 do_print_entry = False +118 # Print directory +119 if entry.is_directory(): +120 if (self.options.type == 'd' or self.options.type is None): +121 # No name filtering +122 if self.options.name is None and self.options.iname is None: +123 do_print_entry = True +124 +125 # Filtering on names case sensitive +126 elif self.options.name is not None: +127 if '*' in self.options.name: +128 regex = self.options.name +129 regex = regex.replace('.', '\\.') +130 regex = regex.replace('*', '.*') +131 regex = '^' + regex + '$' +132 if re.match(regex, entry.get_longname()): +133 do_print_entry = True +134 else: +135 do_print_entry = False +136 else: +137 do_print_entry = (entry.get_longname().lower() == self.options.name.lower()) +138 +139 # Filtering on names case insensitive +140 elif self.options.iname is not None: +141 if '*' in self.options.iname: +142 regex = self.options.iname +143 regex = regex.replace('.', '\\.') +144 regex = regex.replace('*', '.*') +145 regex = '^' + regex + '$' +146 if re.match(regex, entry.get_longname(), re.IGNORECASE): +147 do_print_entry = True +148 else: +149 do_print_entry = False +150 else: +151 do_print_entry = (entry.get_longname().lower() == self.options.iname.lower()) +152 +153 # Print file +154 else: +155 if (self.options.type == 'f' or self.options.type is None): +156 # No name filtering +157 if self.options.name is None and self.options.iname is None: +158 do_print_entry = True +159 +160 # Filtering on names case sensitive +161 elif self.options.name is not None: +162 if '*' in self.options.name: +163 regex = self.options.name +164 regex = regex.replace('.', '\\.') +165 regex = regex.replace('*', '.*') +166 regex = '^' + regex + '$' +167 if re.match(regex, entry.get_longname()): +168 do_print_entry = True +169 else: +170 do_print_entry = False +171 else: +172 do_print_entry = (entry.get_longname().lower() == self.options.name.lower()) +173 +174 # Filtering on names case insensitive +175 elif self.options.iname is not None: +176 if '*' in self.options.iname: +177 regex = self.options.iname +178 regex = regex.replace('.', '\\.') +179 regex = regex.replace('*', '.*') +180 regex = '^' + regex + '$' +181 if re.match(regex, entry.get_longname(), re.IGNORECASE): +182 do_print_entry = True +183 else: +184 do_print_entry = False +185 else: +186 do_print_entry = (entry.get_longname().lower() == self.options.iname.lower()) +187 +188 if do_print_entry: +189 # Actions on matches +190 if self.options.download: +191 if entry.is_directory(): +192 self.smbSession.get_file_recursively(path=(path + entry.get_longname() + ntpath.sep)) +193 else: +194 self.smbSession.get_file(path=(path + entry.get_longname() + ntpath.sep), keepRemotePath=True) +195 # Output formats +196 if self.options.ls: +197 if entry.is_directory(): +198 windows_ls_entry(entry, (path + entry.get_longname() + ntpath.sep)) +199 else: +200 windows_ls_entry(entry, (path + entry.get_longname())) +201 else: +202 if entry.is_directory(): +203 print("%s" % (path + entry.get_longname() + ntpath.sep)) +204 else: +205 print("%s" % (path + entry.get_longname())) +206 +207 # Next directories to explore +208 for entry in entries: +209 if entry.is_directory(): +210 next_directories_to_explore.append(path + entry.get_longname() + ntpath.sep) +211 +212 return next_directories_to_explore +213 +214 def run(self, arguments): +215 """ +216 This function recursively searches for files in a directory hierarchy and prints the results based on specified criteria. +217 +218 Args: +219 base_dir (str): The base directory to start the search from. +220 paths (list): List of paths to search within the base directory. +221 depth (int): The current depth level in the directory hierarchy. +222 +223 Returns: +224 None +225 """ +226 +227 self.options = self.parseArgs(arguments=arguments) +228 +229 if self.options is not None: +230 # Entrypoint +231 try: +232 next_directories_to_explore = [] +233 for path in list(set(self.options.paths)): +234 next_directories_to_explore.append(ntpath.normpath(path) + ntpath.sep) +235 next_directories_to_explore = sorted(list(set(next_directories_to_explore))) +236 +237 depth = 0 +238 +239 while len(next_directories_to_explore) != 0: +240 next_directories_to_explore = self.__recurse_action( +241 paths=next_directories_to_explore, +242 depth=depth +243 ) +244 depth = depth + 1 +245 +246 except (BrokenPipeError, KeyboardInterrupt) as e: +247 print("[!] Interrupted.") +248 self.smbSession.close_smb_session() +249 self.smbSession.init_smb_session() +
A class to search for files in a directory hierarchy.
+ +This class provides functionality to search for files based on various criteria in a directory hierarchy.
+27 def parseArgs(self, arguments): +28 """ +29 Parses the command line arguments provided to the module. +30 +31 This method initializes the argument parser with the module's name and description, and defines all the necessary arguments that the module accepts. It then parses the provided command line arguments based on these definitions. +32 +33 Args: +34 arguments (str): A string of command line arguments. +35 +36 Returns: +37 ModuleArgumentParser.Namespace | None: The parsed arguments as a Namespace object if successful, None if there are no arguments or help is requested. +38 """ +39 +40 parser = ModuleArgumentParser(prog=self.name, description=self.description) +41 +42 # Adding positional arguments +43 parser.add_argument("paths", metavar="PATH", type=str, nargs="*", default=[], help="The starting point(s) for the search.") +44 +45 # Adding tests, actions, and options for expressions (incomplete for brevity) +46 parser.add_argument("-name", type=str, help="Base of file name (the path with the leading directories removed).") +47 parser.add_argument("-iname", type=str, help="Like -name, but the match is case insensitive.") +48 parser.add_argument("-type", type=str, default=None, help="File type (e.g., f for regular file, d for directory).") +49 parser.add_argument("-size", type=str, help="File uses n units of space.") +50 # parser.add_argument("-mtime", type=str, help="File's data was last modified n*24 hours ago") +51 # parser.add_argument("-ctime", type=str, help="File's status was last changed n*24 hours ago") +52 # parser.add_argument("-atime", type=str, help="File was last accessed n*24 hours ago") +53 +54 # Adding actions +55 parser.add_argument("-ls", action="store_true", default=False, help="List current file in ls -dils format on standard output.") +56 parser.add_argument("-download", action="store_true", default=False, help="List current file in ls -dils format on standard output.") +57 +58 # Other options (incomplete for brevity) +59 parser.add_argument("-maxdepth", type=int, help="Descend at most levels (a non-negative integer) levels of directories below the command line arguments.") +60 parser.add_argument("-mindepth", type=int, help="Do not apply any tests or actions at levels less than levels (a non-negative integer).") +61 +62 if len(arguments.strip()) == 0: +63 parser.print_help() +64 return None +65 else: +66 self.options = self.processArguments(parser, arguments) +67 +68 if self.options is not None: +69 if len(self.options.paths) == 0: +70 parser.print_help() +71 self.options = None +72 +73 return self.options +
Parses the command line arguments provided to the module.
+ +This method initializes the argument parser with the module's name and description, and defines all the necessary arguments that the module accepts. It then parses the provided command line arguments based on these definitions.
+ +Args: + arguments (str): A string of command line arguments.
+ +Returns: + ModuleArgumentParser.Namespace | None: The parsed arguments as a Namespace object if successful, None if there are no arguments or help is requested.
+214 def run(self, arguments): +215 """ +216 This function recursively searches for files in a directory hierarchy and prints the results based on specified criteria. +217 +218 Args: +219 base_dir (str): The base directory to start the search from. +220 paths (list): List of paths to search within the base directory. +221 depth (int): The current depth level in the directory hierarchy. +222 +223 Returns: +224 None +225 """ +226 +227 self.options = self.parseArgs(arguments=arguments) +228 +229 if self.options is not None: +230 # Entrypoint +231 try: +232 next_directories_to_explore = [] +233 for path in list(set(self.options.paths)): +234 next_directories_to_explore.append(ntpath.normpath(path) + ntpath.sep) +235 next_directories_to_explore = sorted(list(set(next_directories_to_explore))) +236 +237 depth = 0 +238 +239 while len(next_directories_to_explore) != 0: +240 next_directories_to_explore = self.__recurse_action( +241 paths=next_directories_to_explore, +242 depth=depth +243 ) +244 depth = depth + 1 +245 +246 except (BrokenPipeError, KeyboardInterrupt) as e: +247 print("[!] Interrupted.") +248 self.smbSession.close_smb_session() +249 self.smbSession.init_smb_session() +
This function recursively searches for files in a directory hierarchy and prints the results based on specified criteria.
+ +Args: + base_dir (str): The base directory to start the search from. + paths (list): List of paths to search within the base directory. + depth (int): The current depth level in the directory hierarchy.
+ +Returns: + None
+