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 remote file.', "Syntax: 'bat <file>'"], 'subcommands': []}, 'cat': {'description': ['Get the contents of a remote 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': []}, 'debug': {'description': ['Command for dev debugging.', "Syntax: 'debug'"], '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']}, 'lbat': {'description': ['Pretty prints the contents of a local file.', "Syntax: 'lbat <file>'"], 'subcommands': []}, 'lcat': {'description': ['Print the contents of a local file.', "Syntax: 'lcat <file>'"], 'subcommands': []}, 'lcd': {'description': ['Changes the current local directory.', "Syntax: 'lcd <directory>'"], 'subcommands': []}, 'lcp': {'description': ['Create a copy of a local file.', "Syntax: 'lcp <srcfile> <dstfile>'"], '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': []}, 'lrename': {'description': ['Renames a local file.', "Syntax: 'lrename <oldfilename> <newfilename>'"], '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': []}, 'mount': {'description': ['Creates a mount point of the remote share on the local machine.', "Syntax: 'mount <remote_path> <local_mountpoint>'"], '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': []}, 'sizeof': {'description': ['Recursively compute the size of a folder.', "Syntax: 'sizeof [directory|file]'"], 'subcommands': []}, 'shares': {'description': ['Lists the SMB shares served by the remote machine.', "Syntax: 'shares'"], 'subcommands': ['rights']}, 'tree': {'description': ['Displays a tree view of the remote directories.', "Syntax: 'tree [directory]'"], 'subcommands': []}, 'umount': {'description': ['Removes a mount point of the remote share on the local machine.', "Syntax: 'umount <local_mount_point>'"], 'subcommands': []}, 'use': {'description': ['Use a SMB share.', "Syntax: 'use <sharename>'"], '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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
Retrieves information about a specific entry located at the provided path on the SMB share.
\n\n
This method checks if the specified path exists on the SMB share. If the path exists, it retrieves the details of the entry at that path, including the directory name and file name. If the entry is found, it returns the entry object; otherwise, it returns None.
\n\n
Args:\n path (str): The path of the entry to retrieve information about.
\n\n
Returns:\n Entry: An object representing the entry at the specified path, or None if the entry is not found.
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.
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.
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.
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.
Generates the command to mount an SMB share on different platforms.
\n\n
This method takes the local mount point and the remote path of the SMB share and generates the appropriate mount command based on the platform.\nIt constructs the mount command using the provided parameters and executes it using the os.system() function.
\n\n
Args:\n local_mount_point (str): The local directory where the SMB share will be mounted.\n remote_path (str): The remote path on the SMB share to be mounted.
\n\n
Note:\n - For Windows platform, the command uses 'net use' to mount the share.\n - For Linux platform, the command uses 'mount' to mount the share.\n - For macOS platform, the command uses 'mount_smbfs' to mount the share.\n - If the platform is not supported, an error message is displayed.
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.
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.
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.
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.
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.
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.
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.
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.
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.
Unmounts the specified local mount point of the remote share.
\n\n
This method unmounts the specified local mount point of the remote share based on the platform.\nIt supports Windows, Linux, and macOS platforms for unmounting.
\n\n
Parameters:\n local_mount_point (str): The local mount point to unmount.
Tests the read and write access rights of the current SMB session.
\n\n
This method checks the read and write access rights of the current SMB session by attempting to list paths and create/delete temporary directories.
\n\n
Returns:\n dict: A dictionary containing the read and write access rights status.\n - \"readable\" (bool): Indicates if the session has read access rights.\n - \"writable\" (bool): Indicates if the session has write access rights.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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, logger)"}, {"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 remote file.', "Syntax: 'bat <file>'"], 'subcommands': [], 'autocomplete': ['remote_file']}, 'cat': {'description': ['Get the contents of a remote file.', "Syntax: 'cat <file>'"], 'subcommands': [], 'autocomplete': ['remote_file']}, 'cd': {'description': ['Change the current working directory.', "Syntax: 'cd <directory>'"], 'subcommands': [], 'autocomplete': ['remote_directory']}, 'close': {'description': ['Closes the SMB connection to the remote machine.', "Syntax: 'close'"], 'subcommands': [], 'autocomplete': []}, 'connect': {'description': ['Connect to the remote machine (useful if connection timed out).', "Syntax: 'connect'"], 'subcommands': [], 'autocomplete': []}, 'debug': {'description': ['Command for dev debugging.', "Syntax: 'debug'"], 'subcommands': [], 'autocomplete': []}, 'dir': {'description': ['List the contents of the current working directory.', "Syntax: 'dir'"], 'subcommands': [], 'autocomplete': ['remote_directory']}, 'exit': {'description': ['Exits the smbclient-ng script.', "Syntax: 'exit'"], 'subcommands': [], 'autocomplete': []}, 'get': {'description': ['Get a remote file.', "Syntax: 'get [-r] <directory or file>'"], 'subcommands': [], 'autocomplete': ['remote_file']}, 'help': {'description': ['Displays this help message.', "Syntax: 'help'"], 'subcommands': ['format'], 'autocomplete': []}, 'info': {'description': ['Get information about the server and or the share.', "Syntax: 'info [server|share]'"], 'subcommands': ['server', 'share'], 'autocomplete': []}, 'lbat': {'description': ['Pretty prints the contents of a local file.', "Syntax: 'lbat <file>'"], 'subcommands': [], 'autocomplete': ['local_file']}, 'lcat': {'description': ['Print the contents of a local file.', "Syntax: 'lcat <file>'"], 'subcommands': [], 'autocomplete': ['local_file']}, 'lcd': {'description': ['Changes the current local directory.', "Syntax: 'lcd <directory>'"], 'subcommands': [], 'autocomplete': ['local_directory']}, 'lcp': {'description': ['Create a copy of a local file.', "Syntax: 'lcp <srcfile> <dstfile>'"], 'subcommands': [], 'autocomplete': ['remote_file']}, 'lls': {'description': ['Lists the contents of the current local directory.', "Syntax: 'lls'"], 'subcommands': [], 'autocomplete': ['local_directory']}, 'lmkdir': {'description': ['Creates a new local directory.', "Syntax: 'lmkdir <directory>'"], 'subcommands': [], 'autocomplete': ['local_directory']}, 'lpwd': {'description': ['Shows the current local directory.', "Syntax: 'lpwd'"], 'subcommands': [], 'autocomplete': []}, 'lrename': {'description': ['Renames a local file.', "Syntax: 'lrename <oldfilename> <newfilename>'"], 'subcommands': [], 'autocomplete': ['local_file']}, 'lrm': {'description': ['Removes a local file.', "Syntax: 'lrm <file>'"], 'subcommands': [], 'autocomplete': ['local_file']}, 'lrmdir': {'description': ['Removes a local directory.', "Syntax: 'lrmdir <directory>'"], 'subcommands': [], 'autocomplete': ['local_directory']}, 'ls': {'description': ['List the contents of the current remote working directory.', "Syntax: 'ls'"], 'subcommands': [], 'autocomplete': ['remote_directory']}, 'ltree': {'description': ['Displays a tree view of the local directories.', "Syntax: 'ltree [directory]'"], 'subcommands': [], 'autocomplete': ['local_directory']}, 'mkdir': {'description': ['Creates a new remote directory.', "Syntax: 'mkdir <directory>'"], 'subcommands': [], 'autocomplete': ['remote_directory']}, 'module': {'description': ['Loads a specific module for additional functionalities.', "Syntax: 'module <name>'"], 'subcommands': [], 'autocomplete': []}, 'mount': {'description': ['Creates a mount point of the remote share on the local machine.', "Syntax: 'mount <remote_path> <local_mountpoint>'"], 'subcommands': [], 'autocomplete': ['remote_directory']}, 'put': {'description': ['Put a local file or directory in a remote directory.', "Syntax: 'put [-r] <directory or file>'"], 'subcommands': [], 'autocomplete': ['local_file']}, 'reconnect': {'description': ['Reconnect to the remote machine (useful if connection timed out).', "Syntax: 'reconnect'"], 'subcommands': [], 'autocomplete': []}, 'reset': {'description': ['Reset the TTY output, useful if it was broken after printing a binary file on stdout.', "Syntax: 'reset'"], 'subcommands': [], 'autocomplete': []}, 'rmdir': {'description': ['Removes a remote directory.', "Syntax: 'rmdir <directory>'"], 'subcommands': [], 'autocomplete': ['remote_directory']}, 'rm': {'description': ['Removes a remote file.', "Syntax: 'rm <file>'"], 'subcommands': [], 'autocomplete': ['remote_file']}, 'sizeof': {'description': ['Recursively compute the size of a folder.', "Syntax: 'sizeof [directory|file]'"], 'subcommands': [], 'autocomplete': ['remote_directory']}, 'sessions': {'description': ['Manage the SMB sessions.', "Syntax: 'sessions [access|create|delete|execute|list]'"], 'subcommands': ['create', 'delete', 'execute', 'interact', 'list'], 'autocomplete': []}, 'shares': {'description': ['Lists the SMB shares served by the remote machine.', "Syntax: 'shares'"], 'subcommands': ['rights'], 'autocomplete': []}, 'tree': {'description': ['Displays a tree view of the remote directories.', "Syntax: 'tree [directory]'"], 'subcommands': [], 'autocomplete': ['remote_directory']}, 'umount': {'description': ['Removes a mount point of the remote share on the local machine.', "Syntax: 'umount <local_mount_point>'"], 'subcommands': [], 'autocomplete': ['remote_directory']}, 'use': {'description': ['Use a SMB share.', "Syntax: 'use <sharename>'"], 'subcommands': [], 'autocomplete': ['share']}}"}, {"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.logger", "modulename": "smbclientng.core.CommandCompleter", "qualname": "CommandCompleter.logger", "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.
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.
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.
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.
This method parses the provided hash string and sets the LM and NT hash values accordingly.\nIf the hash string is valid and contains both LM and NT hashes, they are set directly.\nIf only one hash is provided, the other is set to its default value.\nIf the hash string is None or invalid, both hashes are set to None.
\n\n
Args:\n hashes (str): A string containing LM and NT hashes separated by a colon.
Determines if the current credentials can be used for a pass-the-hash attack.
\n\n
This method checks if both LM and NT hashes are available and not None. If both hashes are set,\nit indicates that the credentials may be used for a pass-the-hash attack.
\n\n
Returns:\n bool: True if both LM and NT hashes are available, False otherwise.
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.
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.
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.
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.
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.
A Logger class that provides logging functionalities with various levels such as INFO, DEBUG, WARNING, ERROR, and CRITICAL.\nIt supports color-coded output, which can be disabled, and can also log messages to a file.
\n\n
Attributes:\n __debug (bool): If True, debug level messages will be printed and logged.\n __nocolors (bool): If True, disables color-coded output.\n logfile (str|None): Path to a file where logs will be written. If None, logging to a file is disabled.
\n\n
Methods:\n __init__(debug=False, logfile=None, nocolors=False): Initializes the Logger instance.\n print(message=\"\"): Prints a message to stdout and logs it to a file if logging is enabled.\n info(message): Logs a message at the INFO level.\n debug(message): Logs a message at the DEBUG level if debugging is enabled.\n error(message): Logs a message at the ERROR level.
Prints a message to stdout and logs it to a file if logging is enabled.
\n\n
This method prints the provided message to the standard output and also logs it to a file if a log file path is specified during the Logger instance initialization. The message can include color codes for color-coded output, which can be disabled by setting the nocolors attribute to True.
\n\n
Args:\n message (str): The message to be printed and logged.
This method logs the provided message at the INFO level. The message can include color codes for color-coded output, which can be disabled by setting the nocolors attribute to True. The message is also logged to a file if a log file path is specified during the Logger instance initialization.
\n\n
Args:\n message (str): The message to be logged at the INFO level.
Logs a message at the DEBUG level if debugging is enabled.
\n\n
This method logs the provided message at the DEBUG level if the debug attribute is set to True during the Logger instance initialization. The message can include color codes for color-coded output, which can be disabled by setting the nocolors attribute to True.
Logs an error message to the console and the log file.
\n\n
This method logs the provided error message to the standard error output and also logs it to a file if a log file path is specified during the Logger instance initialization. The message can include color codes for color-coded output, which can be disabled by setting the nocolors attribute to True.
\n\n
Args:\n message (str): The error message to be logged.
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.
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.
Represents an SMB session for interacting with an SMB server.
\n\n
This class provides methods to manage and interact with an SMB server, including\nconnecting to the server, listing shares, uploading and downloading files, and\nmanaging directories and files on the server. It handles session initialization,\nauthentication, and cleanup.
\n\n
Attributes:\n host (str): The hostname or IP address of the SMB server.\n port (int): The port number on which the SMB server is listening.\n credentials (dict): Authentication credentials for the SMB server.\n config (dict, optional): Configuration options for the SMB session.\n smbClient (impacket.smbconnection.SMBConnection): The SMB connection instance.\n connected (bool): Connection status to the SMB server.\n available_shares (dict): A dictionary of available SMB shares.\n smb_share (str): The current SMB share in use.\n smb_cwd (str): The current working directory on the SMB share.\n smb_tree_id (int): The tree ID of the connected SMB share.
\n\n
Methods:\n close_smb_session(): Closes the current SMB session.\n init_smb_session(): Initializes the SMB session with the server.\n list_shares(): Lists all shares available on the SMB server.\n set_share(shareName): Sets the current SMB share.\n set_cwd(path): Sets the current working directory on the SMB share.\n put_file(localpath): Uploads a file to the current SMB share.\n get_file(remotepath, localpath): Downloads a file from the SMB share.\n mkdir(path): Creates a directory on the SMB share.\n rmdir(path): Removes a directory from the SMB share.\n rm(path): Removes a file from the SMB share.\n read_file(path): Reads a file from the SMB share.\n test_rights(sharename): Tests read and write access rights on a share.
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.
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.
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.
Finds files and directories on the SMB share based on the provided paths and executes a callback function on each entry.
\n\n
This method traverses the specified paths on the SMB share, recursively exploring directories and invoking the callback\nfunction on each file or directory found. The callback function is called with three arguments: the entry object, the\nfull path of the entry, and the current depth of recursion.
\n\n
Args:\n paths (list, optional): A list of paths to start the search from. Defaults to an empty list.\n callback (function, optional): A function to be called on each entry found. The function should accept three arguments:\n the entry object, the full path of the entry, and the current depth of recursion. Defaults to None.
\n\n
Note:\n If the callback function is None, the method will print an error message and return without performing any action.
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.
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.
Retrieves information about a specific entry located at the provided path on the SMB share.
\n\n
This method checks if the specified path exists on the SMB share. If the path exists, it retrieves the details of the entry at that path, including the directory name and file name. If the entry is found, it returns the entry object; otherwise, it returns None.
\n\n
Args:\n path (str): The path of the entry to retrieve information about.
\n\n
Returns:\n Entry: An object representing the entry at the specified path, or None if the entry is not found.
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.
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.
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.
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.
Generates the command to mount an SMB share on different platforms.
\n\n
This method takes the local mount point and the remote path of the SMB share and generates the appropriate mount command based on the platform.\nIt constructs the mount command using the provided parameters and executes it using the os.system() function.
\n\n
Args:\n local_mount_point (str): The local directory where the SMB share will be mounted.\n remote_path (str): The remote path on the SMB share to be mounted.
\n\n
Note:\n - For Windows platform, the command uses 'net use' to mount the share.\n - For Linux platform, the command uses 'mount' to mount the share.\n - For macOS platform, the command uses 'mount_smbfs' to mount the share.\n - If the platform is not supported, an error message is displayed.
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.
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.
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.
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.
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.
This method attempts to read the contents of a file specified by the path parameter from the SMB share.\nIt constructs the full path to the file, checks if the path is a valid file, and then reads the file content\ninto a byte stream which is returned to the caller.
\n\n
Args:\n path (str, optional): The path of the file to be read from the SMB share. Defaults to None.
\n\n
Returns:\n bytes: The content of the file as a byte stream, or None if the file does not exist or an error occurs.
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.
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.
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.
Unmounts the specified local mount point of the remote share.
\n\n
This method unmounts the specified local mount point of the remote share based on the platform.\nIt supports Windows, Linux, and macOS platforms for unmounting.
\n\n
Parameters:\n local_mount_point (str): The local mount point to unmount.
Tests the read and write access rights of the current SMB session.
\n\n
This method checks the read and write access rights of the current SMB session by attempting to list paths and create/delete temporary directories.
\n\n
Returns:\n dict: A dictionary containing the read and write access rights status.\n - \"readable\" (bool): Indicates if the session has read access rights.\n - \"writable\" (bool): Indicates if the session has write access rights.
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.
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.
This class is responsible for creating, managing, and switching between multiple SMB sessions. It allows for the creation of new sessions with specified credentials and hosts, and provides methods to switch between existing sessions. It also keeps track of the current session and its ID.
\n\n
Attributes:\n next_session_id (int): The next available session ID.\n current_session (SMBSession): The currently active SMB session.\n current_session_id (int): The ID of the currently active session.\n sessions (dict): A dictionary of all active sessions, keyed by their session ID.
Processes command line arguments to manage SMB sessions.
\n\n
This function parses the command line arguments provided to the application and determines the appropriate action to take,\nsuch as creating, interacting, deleting, or listing SMB sessions, or executing a command in one or more sessions.
\n\n
Args:\n arguments (list of str): The command line arguments.
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.
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.
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.
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.
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.
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.
Resolves local file paths based on the provided arguments.
\n\n
This function takes a list of arguments, which can include wildcard patterns, and resolves them to actual file paths.\nIf an argument contains a wildcard ('*'), it attempts to match files in the specified directory against the pattern.\nIf the argument does not contain a wildcard, it is added to the list of resolved files as is.
\n\n
Args:\n arguments (list): A list of file path arguments, which may include wildcard patterns.
\n\n
Returns:\n list: A list of resolved file paths that match the provided arguments.
Resolves remote file paths based on the provided arguments using an SMB session.
\n\n
This function takes a list of arguments, which can include wildcard patterns, and resolves them to actual remote file paths.\nIf an argument contains a wildcard ('*'), it attempts to match files in the specified remote directory against the pattern.\nIf the argument does not contain a wildcard, it is added to the list of resolved files as is.
\n\n
Args:\n smbsession (SMBSession): The SMB session through which to access the files.\n arguments (list): A list of file path arguments, which may include wildcard patterns.
\n\n
Returns:\n list: A list of resolved remote file paths that match the provided arguments.
Check if a specific port on a target host is open.
\n\n
This function attempts to establish a TCP connection to the specified port on the target host.\nIf the connection is successful, it indicates that the port is open. If the connection fails,\nit indicates that the port is closed or the host is unreachable.
\n\n
Args:\n target (str): The hostname or IP address of the target host.\n port (int): The port number to check.
\n\n
Returns:\n bool: True if the port is open, False otherwise.
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.
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.
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.
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.
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.
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.
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.html b/documentation/smbclientng/core.html
index dc79d1e..de95d0d 100644
--- a/documentation/smbclientng/core.html
+++ b/documentation/smbclientng/core.html
@@ -31,11 +31,14 @@
7 8importntpath 9importos
- 10
+ 10importshlex 11
- 12classCommandCompleter(object):
- 13"""
- 14 A class to handle command completion for the smbclient-ng shell.
- 15
- 16 This class provides a command completion feature that suggests possible command names based on the current input.
- 17 It uses a dictionary to store commands and their descriptions, which helps in providing hints during the command line
- 18 interaction in the smbclient-ng shell.
- 19
- 20 Attributes:
- 21 smbSession (SMBSession): An instance of SMBSession which maintains the current SMB session.
- 22 commands (dict): A dictionary containing command names as keys and their descriptions and subcommands as values.
- 23
- 24 Methods:
- 25 __init__(self, smbSession): Initializes the CommandCompleter with an SMBSession.
- 26 """
- 27
- 28commands={
- 29"bat":{
- 30"description":[
- 31"Pretty prints the contents of a remote file.",
- 32"Syntax: 'bat <file>'"
- 33],
- 34"subcommands":[]
- 35},
- 36"cat":{
- 37"description":[
- 38"Get the contents of a remote file.",
- 39"Syntax: 'cat <file>'"
- 40],
- 41"subcommands":[]
- 42},
- 43"cd":{
- 44"description":[
- 45"Change the current working directory.",
- 46"Syntax: 'cd <directory>'"
- 47],
- 48"subcommands":[]
- 49},
- 50"close":{
- 51"description":[
- 52"Closes the SMB connection to the remote machine.",
- 53"Syntax: 'close'"
- 54],
- 55"subcommands":[]
- 56},
- 57"connect":{
- 58"description":[
- 59"Connect to the remote machine (useful if connection timed out).",
- 60"Syntax: 'connect'"
- 61],
- 62"subcommands":[]
- 63},
- 64"debug":{
- 65"description":[
- 66"Command for dev debugging.",
- 67"Syntax: 'debug'"
- 68],
- 69"subcommands":[]
- 70},
- 71"dir":{
- 72"description":[
- 73"List the contents of the current working directory.",
- 74"Syntax: 'dir'"
- 75],
- 76"subcommands":[]
+ 12
+ 13classCommandCompleter(object):
+ 14"""
+ 15 A class to handle command completion for the smbclient-ng shell.
+ 16
+ 17 This class provides a command completion feature that suggests possible command names based on the current input.
+ 18 It uses a dictionary to store commands and their descriptions, which helps in providing hints during the command line
+ 19 interaction in the smbclient-ng shell.
+ 20
+ 21 Attributes:
+ 22 smbSession (SMBSession): An instance of SMBSession which maintains the current SMB session.
+ 23 commands (dict): A dictionary containing command names as keys and their descriptions and subcommands as values.
+ 24
+ 25 Methods:
+ 26 __init__(self, smbSession): Initializes the CommandCompleter with an SMBSession.
+ 27 """
+ 28
+ 29commands={
+ 30"bat":{
+ 31"description":[
+ 32"Pretty prints the contents of a remote file.",
+ 33"Syntax: 'bat <file>'"
+ 34],
+ 35"subcommands":[],
+ 36"autocomplete":["remote_file"]
+ 37},
+ 38"cat":{
+ 39"description":[
+ 40"Get the contents of a remote file.",
+ 41"Syntax: 'cat <file>'"
+ 42],
+ 43"subcommands":[],
+ 44"autocomplete":["remote_file"]
+ 45},
+ 46"cd":{
+ 47"description":[
+ 48"Change the current working directory.",
+ 49"Syntax: 'cd <directory>'"
+ 50],
+ 51"subcommands":[],
+ 52"autocomplete":["remote_directory"]
+ 53},
+ 54"close":{
+ 55"description":[
+ 56"Closes the SMB connection to the remote machine.",
+ 57"Syntax: 'close'"
+ 58],
+ 59"subcommands":[],
+ 60"autocomplete":[]
+ 61},
+ 62"connect":{
+ 63"description":[
+ 64"Connect to the remote machine (useful if connection timed out).",
+ 65"Syntax: 'connect'"
+ 66],
+ 67"subcommands":[],
+ 68"autocomplete":[]
+ 69},
+ 70"debug":{
+ 71"description":[
+ 72"Command for dev debugging.",
+ 73"Syntax: 'debug'"
+ 74],
+ 75"subcommands":[],
+ 76"autocomplete":[] 77},
- 78"exit":{
+ 78"dir":{ 79"description":[
- 80"Exits the smbclient-ng script.",
- 81"Syntax: 'exit'"
+ 80"List the contents of the current working directory.",
+ 81"Syntax: 'dir'" 82],
- 83"subcommands":[]
- 84},
- 85"get":{
- 86"description":[
- 87"Get a remote file.",
- 88"Syntax: 'get [-r] <directory or file>'"
- 89],
- 90"subcommands":[]
- 91},
- 92"help":{
- 93"description":[
- 94"Displays this help message.",
- 95"Syntax: 'help'"
- 96],
- 97"subcommands":["format"]
- 98},
- 99"info":{
-100"description":[
-101"Get information about the server and or the share.",
-102"Syntax: 'info [server|share]'"
-103],
-104"subcommands":["server","share"]
-105},
-106"lbat":{
-107"description":[
-108"Pretty prints the contents of a local file.",
-109"Syntax: 'lbat <file>'"
-110],
-111"subcommands":[]
-112},
-113"lcat":{
-114"description":[
-115"Print the contents of a local file.",
-116"Syntax: 'lcat <file>'"
-117],
-118"subcommands":[]
-119},
-120"lcd":{
-121"description":[
-122"Changes the current local directory.",
-123"Syntax: 'lcd <directory>'"
-124],
-125"subcommands":[]
-126},
-127"lcp":{
-128"description":[
-129"Create a copy of a local file.",
-130"Syntax: 'lcp <srcfile> <dstfile>'"
-131],
-132"subcommands":[]
+ 83"subcommands":[],
+ 84"autocomplete":["remote_directory"]
+ 85},
+ 86"exit":{
+ 87"description":[
+ 88"Exits the smbclient-ng script.",
+ 89"Syntax: 'exit'"
+ 90],
+ 91"subcommands":[],
+ 92"autocomplete":[]
+ 93},
+ 94"get":{
+ 95"description":[
+ 96"Get a remote file.",
+ 97"Syntax: 'get [-r] <directory or file>'"
+ 98],
+ 99"subcommands":[],
+100"autocomplete":["remote_file"]
+101},
+102"help":{
+103"description":[
+104"Displays this help message.",
+105"Syntax: 'help'"
+106],
+107"subcommands":["format"],
+108"autocomplete":[]
+109},
+110"info":{
+111"description":[
+112"Get information about the server and or the share.",
+113"Syntax: 'info [server|share]'"
+114],
+115"subcommands":["server","share"],
+116"autocomplete":[]
+117},
+118"lbat":{
+119"description":[
+120"Pretty prints the contents of a local file.",
+121"Syntax: 'lbat <file>'"
+122],
+123"subcommands":[],
+124"autocomplete":["local_file"]
+125},
+126"lcat":{
+127"description":[
+128"Print the contents of a local file.",
+129"Syntax: 'lcat <file>'"
+130],
+131"subcommands":[],
+132"autocomplete":["local_file"]133},
-134"lls":{
+134"lcd":{135"description":[
-136"Lists the contents of the current local directory.",
-137"Syntax: 'lls'"
-138],
-139"subcommands":[]
-140},
-141"lmkdir":{
-142"description":[
-143"Creates a new local directory.",
-144"Syntax: 'lmkdir <directory>'"
-145],
-146"subcommands":[]
-147},
-148"lpwd":{
-149"description":[
-150"Shows the current local directory.",
-151"Syntax: 'lpwd'"
-152],
-153"subcommands":[]
-154},
-155"lrename":{
-156"description":[
-157"Renames a local file.",
-158"Syntax: 'lrename <oldfilename> <newfilename>'"
-159],
-160"subcommands":[]
-161},
-162"lrm":{
-163"description":[
-164"Removes a local file.",
-165"Syntax: 'lrm <file>'"
-166],
-167"subcommands":[]
-168},
-169"lrmdir":{
-170"description":[
-171"Removes a local directory.",
-172"Syntax: 'lrmdir <directory>'"
-173],
-174"subcommands":[]
-175},
-176"ls":{
-177"description":[
-178"List the contents of the current remote working directory.",
-179"Syntax: 'ls'"
-180],
-181"subcommands":[]
-182},
-183"ltree":{
-184"description":[
-185"Displays a tree view of the local directories.",
-186"Syntax: 'ltree [directory]'"
-187],
-188"subcommands":[]
+136"Changes the current local directory.",
+137"Syntax: 'lcd <directory>'"
+138],
+139"subcommands":[],
+140"autocomplete":["local_directory"]
+141},
+142"lcp":{
+143"description":[
+144"Create a copy of a local file.",
+145"Syntax: 'lcp <srcfile> <dstfile>'"
+146],
+147"subcommands":[],
+148"autocomplete":["remote_file"]
+149},
+150"lls":{
+151"description":[
+152"Lists the contents of the current local directory.",
+153"Syntax: 'lls'"
+154],
+155"subcommands":[],
+156"autocomplete":["local_directory"]
+157},
+158"lmkdir":{
+159"description":[
+160"Creates a new local directory.",
+161"Syntax: 'lmkdir <directory>'"
+162],
+163"subcommands":[],
+164"autocomplete":["local_directory"]
+165},
+166"lpwd":{
+167"description":[
+168"Shows the current local directory.",
+169"Syntax: 'lpwd'"
+170],
+171"subcommands":[],
+172"autocomplete":[]
+173},
+174"lrename":{
+175"description":[
+176"Renames a local file.",
+177"Syntax: 'lrename <oldfilename> <newfilename>'"
+178],
+179"subcommands":[],
+180"autocomplete":["local_file"]
+181},
+182"lrm":{
+183"description":[
+184"Removes a local file.",
+185"Syntax: 'lrm <file>'"
+186],
+187"subcommands":[],
+188"autocomplete":["local_file"]189},
-190"mkdir":{
+190"lrmdir":{191"description":[
-192"Creates a new remote directory.",
-193"Syntax: 'mkdir <directory>'"
+192"Removes a local directory.",
+193"Syntax: 'lrmdir <directory>'"194],
-195"subcommands":[]
-196},
-197"module":{
-198"description":[
-199"Loads a specific module for additional functionalities.",
-200"Syntax: 'module <name>'"
-201],
-202"subcommands":[]
-203},
-204"mount":{
-205"description":[
-206"Creates a mount point of the remote share on the local machine.",
-207"Syntax: 'mount <remote_path> <local_mountpoint>'"
-208],
-209"subcommands":[]
-210},
-211"put":{
-212"description":[
-213"Put a local file or directory in a remote directory.",
-214"Syntax: 'put [-r] <directory or file>'"
-215],
-216"subcommands":[]
-217},
-218"reconnect":{
-219"description":[
-220"Reconnect to the remote machine (useful if connection timed out).",
-221"Syntax: 'reconnect'"
-222],
-223"subcommands":[]
-224},
-225"reset":{
-226"description":[
-227"Reset the TTY output, useful if it was broken after printing a binary file on stdout.",
-228"Syntax: 'reset'"
-229],
-230"subcommands":[]
-231},
-232"rmdir":{
-233"description":[
-234"Removes a remote directory.",
-235"Syntax: 'rmdir <directory>'"
-236],
-237"subcommands":[]
-238},
-239"rm":{
-240"description":[
-241"Removes a remote file.",
-242"Syntax: 'rm <file>'"
-243],
-244"subcommands":[]
+195"subcommands":[],
+196"autocomplete":["local_directory"]
+197},
+198"ls":{
+199"description":[
+200"List the contents of the current remote working directory.",
+201"Syntax: 'ls'"
+202],
+203"subcommands":[],
+204"autocomplete":["remote_directory"]
+205},
+206"ltree":{
+207"description":[
+208"Displays a tree view of the local directories.",
+209"Syntax: 'ltree [directory]'"
+210],
+211"subcommands":[],
+212"autocomplete":["local_directory"]
+213},
+214"mkdir":{
+215"description":[
+216"Creates a new remote directory.",
+217"Syntax: 'mkdir <directory>'"
+218],
+219"subcommands":[],
+220"autocomplete":["remote_directory"]
+221},
+222"module":{
+223"description":[
+224"Loads a specific module for additional functionalities.",
+225"Syntax: 'module <name>'"
+226],
+227"subcommands":[],
+228"autocomplete":[]
+229},
+230"mount":{
+231"description":[
+232"Creates a mount point of the remote share on the local machine.",
+233"Syntax: 'mount <remote_path> <local_mountpoint>'"
+234],
+235"subcommands":[],
+236"autocomplete":["remote_directory"]
+237},
+238"put":{
+239"description":[
+240"Put a local file or directory in a remote directory.",
+241"Syntax: 'put [-r] <directory or file>'"
+242],
+243"subcommands":[],
+244"autocomplete":["local_file"]245},
-246"sizeof":{
+246"reconnect":{247"description":[
-248"Recursively compute the size of a folder.",
-249"Syntax: 'sizeof [directory|file]'"
+248"Reconnect to the remote machine (useful if connection timed out).",
+249"Syntax: 'reconnect'"250],
-251"subcommands":[]
-252},
-253"shares":{
-254"description":[
-255"Lists the SMB shares served by the remote machine.",
-256"Syntax: 'shares'"
-257],
-258"subcommands":["rights"]
-259},
-260"tree":{
-261"description":[
-262"Displays a tree view of the remote directories.",
-263"Syntax: 'tree [directory]'"
-264],
-265"subcommands":[]
-266},
-267"umount":{
-268"description":[
-269"Removes a mount point of the remote share on the local machine.",
-270"Syntax: 'umount <local_mount_point>'"
-271],
-272"subcommands":[]
-273},
-274"use":{
-275"description":[
-276"Use a SMB share.",
-277"Syntax: 'use <sharename>'"
-278],
-279"subcommands":[]
-280},
-281}
-282
-283def__init__(self,smbSession,config):
-284# Objects
-285self.smbSession=smbSession
-286self.config=config
-287# Pre computing for some commands
-288self.commands["help"]["subcommands"]=["format"]+list(self.commands.keys())
-289self.commands["help"]["subcommands"].remove("help")
-290
-291defcomplete(self,text,state):
-292"""
-293 Function to handle command completion in the LDAP console.
-294
-295 This function completes the user"s input based on the available options for commands in the LDAP console.
-296
-297 Args:
-298 text (str): The current text input by the user.
-299 state (int): The current state of completion.
-300
-301 Returns:
-302 str: The next completion suggestion based on the user"s input state.
-303 """
-304
-305ifstate==0:
-306
-307# No text typed yet, need the list of commands available
-308iflen(text)==0:
-309self.matches=[sforsinself.commands.keys()]
-310
-311eliflen(text)!=0:
-312# This is for the main command
-313iftext.count(" ")==0:
-314self.matches=[sforsinself.commands.keys()ifsands.startswith(text)]
-315
-316# This is for subcommands
-317eliftext.count(" ")>=1:
-318command,remainder=text.split(" ",1)
-319ifcommandinself.commands.keys():
-320ifcommand=="use":
-321# Choose SMB Share to connect to
-322self.matches=[
-323command+" "+s.lower()
-324forsinself.smbSession.list_shares().keys()
-325ifs.lower().startswith(remainder.lower())
-326]
+251"subcommands":[],
+252"autocomplete":[]
+253},
+254"reset":{
+255"description":[
+256"Reset the TTY output, useful if it was broken after printing a binary file on stdout.",
+257"Syntax: 'reset'"
+258],
+259"subcommands":[],
+260"autocomplete":[]
+261},
+262"rmdir":{
+263"description":[
+264"Removes a remote directory.",
+265"Syntax: 'rmdir <directory>'"
+266],
+267"subcommands":[],
+268"autocomplete":["remote_directory"]
+269},
+270"rm":{
+271"description":[
+272"Removes a remote file.",
+273"Syntax: 'rm <file>'"
+274],
+275"subcommands":[],
+276"autocomplete":["remote_file"]
+277},
+278"sizeof":{
+279"description":[
+280"Recursively compute the size of a folder.",
+281"Syntax: 'sizeof [directory|file]'"
+282],
+283"subcommands":[],
+284"autocomplete":["remote_directory"]
+285},
+286"sessions":{
+287"description":[
+288"Manage the SMB sessions.",
+289"Syntax: 'sessions [access|create|delete|execute|list]'"
+290],
+291"subcommands":["create","delete","execute","interact","list"],
+292"autocomplete":[]
+293},
+294"shares":{
+295"description":[
+296"Lists the SMB shares served by the remote machine.",
+297"Syntax: 'shares'"
+298],
+299"subcommands":["rights"],
+300"autocomplete":[]
+301},
+302"tree":{
+303"description":[
+304"Displays a tree view of the remote directories.",
+305"Syntax: 'tree [directory]'"
+306],
+307"subcommands":[],
+308"autocomplete":["remote_directory"]
+309},
+310"umount":{
+311"description":[
+312"Removes a mount point of the remote share on the local machine.",
+313"Syntax: 'umount <local_mount_point>'"
+314],
+315"subcommands":[],
+316"autocomplete":["remote_directory"]
+317},
+318"use":{
+319"description":[
+320"Use a SMB share.",
+321"Syntax: 'use <sharename>'"
+322],
+323"subcommands":[],
+324"autocomplete":["share"]
+325},
+326}327
-328elifcommandin["cd","dir","ls","mkdir","rmdir","tree"]:
-329# Choose remote directory
-330path=""
-331if'\\'inremainder.strip()or'/'inremainder.strip():
-332path=remainder.strip().replace('/',ntpath.sep)
-333path=ntpath.sep.join(path.split(ntpath.sep)[:-1])
-334
-335directory_contents=self.smbSession.list_contents(path=path).items()
+328def__init__(self,smbSession,config,logger):
+329# Objects
+330self.smbSession=smbSession
+331self.config=config
+332self.logger=logger
+333# Pre computing for some commands
+334self.commands["help"]["subcommands"]=["format"]+list(self.commands.keys())
+335self.commands["help"]["subcommands"].remove("help")336
-337matching_entries=[]
-338for_,entryindirectory_contents:
-339ifentry.is_directory()andentry.get_longname()notin[".",".."]:
-340iflen(path)!=0:
-341matching_entries.append(path+ntpath.sep+entry.get_longname()+ntpath.sep)
-342else:
-343matching_entries.append(entry.get_longname()+ntpath.sep)
-344
-345self.matches=[
-346command+" "+s
-347forsinmatching_entries
-348ifs.lower().startswith(remainder.lower())
-349]
+337defcomplete(self,text,state):
+338"""
+339 Function to handle command completion in the LDAP console.
+340
+341 This function completes the user"s input based on the available options for commands in the LDAP console.
+342
+343 Args:
+344 text (str): The current text input by the user.
+345 state (int): The current state of completion.
+346
+347 Returns:
+348 str: The next completion suggestion based on the user"s input state.
+349 """350
-351elifcommandin["bat","cat","debug","get","rm"]:
-352# Choose local files and directories
-353path=""
-354if'\\'inremainder.strip()or'/'inremainder.strip():
-355path=remainder.strip().replace('/',ntpath.sep)
-356path=ntpath.sep.join(path.split(ntpath.sep)[:-1])
-357
-358directory_contents=self.smbSession.list_contents(path=path).items()
-359
-360matching_entries=[]
-361for_,entryindirectory_contents:
-362ifentry.get_longname()notin[".",".."]:
-363iflen(path)!=0:
-364ifentry.is_directory():
-365matching_entries.append(path+ntpath.sep+entry.get_longname()+ntpath.sep)
-366else:
-367matching_entries.append(path+ntpath.sep+entry.get_longname())
-368else:
-369ifentry.is_directory():
-370matching_entries.append(entry.get_longname()+ntpath.sep)
-371else:
-372matching_entries.append(entry.get_longname())
-373
-374self.matches=[
-375command+" "+s
-376forsinmatching_entries
-377ifs.lower().startswith(remainder.lower())
-378]
-379
-380elifcommandin["lcd","lcp","lls","lrm","put","lmkdir","lrm","lrmdir"]:
-381# Choose directory
-382path=""
-383ifos.path.sepinremainder.strip():
-384path=path.split(os.path.sep)[:-1]
-385path=os.path.sep.join(path)
-386
-387# Current dir
-388iflen(path.strip())==0:
-389path="."
-390
-391directory_contents=os.listdir(path=path+os.path.sep)
+351ifstate==0:
+352
+353# No text typed yet, need the list of commands available
+354iflen(text)==0:
+355self.matches=[sforsinself.commands.keys()]
+356
+357# Parsing a command
+358eliflen(text)!=0:
+359# This is for the main command
+360iftext.count(" ")==0:
+361self.matches=[sforsinself.commands.keys()ifsands.startswith(text)]
+362
+363# This is for subcommands
+364eliftext.count(" ")>=1:
+365command,remainder=text.split(" ",1)
+366
+367ifcommandinself.commands.keys():
+368self.matches=[]
+369
+370# Autocomplete shares
+371if"share"inself.commands[command]["autocomplete"]:
+372# Choose SMB Share to connect to
+373shares=self.smbSession.list_shares()
+374matching_entries=[]
+375forsharenameinshares.keys():
+376ifsharename.lower().startswith(remainder.lower()):
+377matching_entries.append(shares[sharename]["name"])
+378# Final matches
+379forminmatching_entries:
+380self.matches.append(command+" "+shlex.quote(m))
+381
+382# Autocomplete directory
+383if"remote_directory"inself.commands[command]["autocomplete"]:
+384# Choose remote directory
+385path=""
+386if'\\'inremainder.strip()or'/'inremainder.strip():
+387path=remainder.strip().replace(ntpath.sep,'/')
+388path='/'.join(path.split('/')[:-1])
+389# Get remote directory contents
+390directory_contents=self.smbSession.list_contents(path=path).items()
+391# 392matching_entries=[]
-393forentryindirectory_contents:
-394ifentrynotin[".",".."]:
-395entry_path=path+os.path.sep+entry
-396ifos.path.isdir(entry_path):
-397matching_entries.append(entry_path+os.path.sep)
-398else:
-399matching_entries.append(entry_path)
-400
-401self.matches=[
-402command+" "+s
-403forsinmatching_entries
-404ifs.startswith(remainder)
-405]
-406
-407else:
-408# Generic case for subcommands
-409self.matches=[
-410command+" "+s
-411forsinself.commands[command]["subcommands"]
-412ifs.startswith(remainder)
-413]
-414else:
-415# Unknown subcommand, skipping autocomplete
-416pass
-417else:
-418self.matches=[]
-419else:
-420self.matches=self.commands.keys()[:]
-421
-422try:
-423returnself.matches[state]+" "
-424exceptIndexError:
-425returnNone
-426
-427defprint_help(self,command=None):
-428"""
-429 Prints help information for a specific command or all commands if no command is specified.
-430
-431 This method displays the help information for the command passed as an argument. If no command is specified,
-432 it prints the help information for all available commands. The help information includes the command syntax,
-433 description, and any subcommands associated with it. This method is designed to provide users with the necessary
-434 guidance on how to use the commands in the smbclient-ng shell.
-435
-436 Args:
-437 command (str, optional): The command to display help information for. If None, help for all commands is displayed.
-438
-439 Returns:
-440 None
-441 """
-442
-443ifcommandisnotNone:
-444ifcommandnotinlist(self.commands.keys())+["format"]:
-445command=None
-446
-447# Print help for a specific command
-448ifcommandisnotNone:
-449ifcommand=="format":
-450self.print_help_format()
-451else:
-452print("│")
-453ifself.config.no_colors:
-454command_str=command+"─"*(15-len(command))
-455iflen(self.commands[command]["description"])==0:
-456print("│ ■ %s┤ "%command_str)
-457eliflen(self.commands[command]["description"])==1:
-458print("│ ■ %s┤ %s "%(command_str,self.commands[command]["description"][0]))
-459else:
-460print("│ ■ %s┤ %s "%(command_str,self.commands[command]["description"][0]))
-461forlineinself.commands[command]["description"][1:]:
-462print("│ %s│ %s "%(" "*(15+2),line))
-463else:
-464command_str=command+" \x1b[90m"+"─"*(15-len(command))+"\x1b[0m"
-465iflen(self.commands[command]["description"])==0:
-466print("│ ■ %s\x1b[90m┤\x1b[0m "%command_str)
-467eliflen(self.commands[command]["description"])==1:
-468print("│ ■ %s\x1b[90m┤\x1b[0m %s "%(command_str,self.commands[command]["description"][0]))
-469else:
-470print("│ ■ %s\x1b[90m┤\x1b[0m %s "%(command_str,self.commands[command]["description"][0]))
-471forlineinself.commands[command]["description"][1:]:
-472print("│ %s\x1b[90m│\x1b[0m %s "%(" "*(15+3),line))
-473print("│")
-474# Generic help
-475else:
-476print("│")
-477commands=sorted(self.commands.keys())
-478forcommandincommands:
-479ifself.config.no_colors:
-480command_str=command+"─"*(15-len(command))
-481iflen(self.commands[command]["description"])==0:
-482print("│ ■ %s┤ "%command_str)
-483eliflen(self.commands[command]["description"])==1:
-484print("│ ■ %s┤ %s "%(command_str,self.commands[command]["description"][0]))
-485else:
-486print("│ ■ %s┤ %s "%(command_str,self.commands[command]["description"][0]))
-487forlineinself.commands[command]["description"][1:]:
-488print("│ %s│ %s "%(" "*(15+2),line))
-489else:
-490command_str=command+" \x1b[90m"+"─"*(15-len(command))+"\x1b[0m"
-491iflen(self.commands[command]["description"])==0:
-492print("│ ■ %s\x1b[90m┤\x1b[0m "%command_str)
-493eliflen(self.commands[command]["description"])==1:
-494print("│ ■ %s\x1b[90m┤\x1b[0m %s "%(command_str,self.commands[command]["description"][0]))
-495else:
-496print("│ ■ %s\x1b[90m┤\x1b[0m %s "%(command_str,self.commands[command]["description"][0]))
-497forlineinself.commands[command]["description"][1:]:
-498print("│ %s\x1b[90m│\x1b[0m %s "%(" "*(15+3),line))
-499print("│")
-500
-501defprint_help_format(self):
-502"""
-503 Prints the help information for the 'format' used in remote 'ls' and 'dir' commands.
-504
-505 This function displays the format of file attributes used in the smbclient-ng shell. It explains the meaning
-506 of each character in the file attribute string, such as whether a file is read-only, hidden, or a directory.
-507 """
-508ifself.config.no_colors:
-509print("File attributes format:\n")
-510print("dachnrst")
-511print("│││││││└──> Temporary")
-512print("││││││└───> System")
-513print("│││││└────> Read-Only")
-514print("││││└─────> Normal")
-515print("│││└──────> Hidden")
-516print("││└───────> Compressed")
-517print("│└────────> Archived")
-518print("└─────────> Directory")
-519else:
-520print("File attributes format:\n")
-521print("dachnrst")
-522print("\x1b[90m│││││││└──>\x1b[0m Temporary")
-523print("\x1b[90m││││││└───>\x1b[0m System")
-524print("\x1b[90m│││││└────>\x1b[0m Read-Only")
-525print("\x1b[90m││││└─────>\x1b[0m Normal")
-526print("\x1b[90m│││└──────>\x1b[0m Hidden")
-527print("\x1b[90m││└───────>\x1b[0m Compressed")
-528print("\x1b[90m│└────────>\x1b[0m Archived")
-529print("\x1b[90m└─────────>\x1b[0m Directory")
+393for_,entryindirectory_contents:
+394ifentry.is_directory()andentry.get_longname()notin[".",".."]:
+395iflen(path)!=0:
+396matching_entries.append(path+'/'+entry.get_longname()+'/')
+397else:
+398matching_entries.append(entry.get_longname()+'/')
+399#
+400forminmatching_entries:
+401ifm.lower().startswith(remainder.lower())orshlex.quote(m).lower().startswith(remainder.lower()):
+402self.matches.append(command+" "+shlex.quote(m))
+403
+404# Autocomplete file
+405if"remote_file"inself.commands[command]["autocomplete"]:
+406# Choose remote file
+407path=""
+408if'\\'inremainder.strip()or'/'inremainder.strip():
+409path=remainder.strip().replace(ntpath.sep,'/')
+410path='/'.join(path.split('/')[:-1])
+411# Get remote directory contents
+412directory_contents=self.smbSession.list_contents(path=path).items()
+413#
+414matching_entries=[]
+415for_,entryindirectory_contents:
+416if(notentry.is_directory())andentry.get_longname()notin[".",".."]:
+417iflen(path)!=0:
+418matching_entries.append(path+'/'+entry.get_longname())
+419else:
+420matching_entries.append(entry.get_longname())
+421#
+422forminmatching_entries:
+423ifm.lower().startswith(remainder.lower())orshlex.quote(m).lower().startswith(remainder.lower()):
+424self.matches.append(command+" "+shlex.quote(m))
+425
+426# Autocomplete local_directory
+427if"local_directory"inself.commands[command]["autocomplete"]:
+428# Choose directory
+429path=""
+430ifos.path.sepinremainder.strip():
+431path=path.split(os.path.sep)[:-1]
+432path=os.path.sep.join(path)
+433# Current dir
+434iflen(path.strip())==0:
+435path="."
+436#
+437directory_contents=os.listdir(path=path+os.path.sep)
+438matching_entries=[]
+439forentryindirectory_contents:
+440ifentrynotin[".",".."]:
+441entry_path=path+os.path.sep+entry
+442ifos.path.isdir(entry_path):
+443matching_entries.append(entry_path+os.path.sep)
+444#
+445forminmatching_entries:
+446ifm.lower().startswith(remainder.lower())orshlex.quote(m).lower().startswith(remainder.lower()):
+447self.matches.append(command+" "+shlex.quote(m))
+448
+449# Autocomplete local_file
+450if"local_file"inself.commands[command]["autocomplete"]:
+451# Choose file
+452path=""
+453ifos.path.sepinremainder.strip():
+454path=path.split(os.path.sep)[:-1]
+455path=os.path.sep.join(path)
+456# Current dir
+457iflen(path.strip())==0:
+458path="."
+459#
+460directory_contents=os.listdir(path=(path+os.path.sep))
+461matching_entries=[]
+462forentryindirectory_contents:
+463ifentrynotin[".",".."]:
+464entry_path=path+os.path.sep+entry
+465ifnotos.path.isdir(entry_path):
+466matching_entries.append(entry_path)
+467#
+468forminmatching_entries:
+469ifm.lower().startswith(remainder.lower())orshlex.quote(m).lower().startswith(remainder.lower()):
+470self.matches.append(command+" "+shlex.quote(m))
+471
+472else:
+473# Generic case for subcommands
+474forminself.commands[command]["subcommands"]:
+475ifm.startswith(remainder):
+476self.matches.append(command+" "+m)
+477else:
+478# Unknown subcommand, skipping autocomplete
+479pass
+480else:
+481self.matches=[]
+482else:
+483self.matches=self.commands.keys()[:]
+484
+485try:
+486returnself.matches[state]+" "
+487exceptIndexError:
+488returnNone
+489
+490defprint_help(self,command=None):
+491"""
+492 Prints help information for a specific command or all commands if no command is specified.
+493
+494 This method displays the help information for the command passed as an argument. If no command is specified,
+495 it prints the help information for all available commands. The help information includes the command syntax,
+496 description, and any subcommands associated with it. This method is designed to provide users with the necessary
+497 guidance on how to use the commands in the smbclient-ng shell.
+498
+499 Args:
+500 command (str, optional): The command to display help information for. If None, help for all commands is displayed.
+501
+502 Returns:
+503 None
+504 """
+505
+506ifcommandisnotNone:
+507ifcommandnotinlist(self.commands.keys())+["format"]:
+508command=None
+509
+510# Print help for a specific command
+511ifcommandisnotNone:
+512ifcommand=="format":
+513self.print_help_format()
+514else:
+515self.logger.print("│")
+516ifself.config.no_colors:
+517command_str=command+"─"*(15-len(command))
+518iflen(self.commands[command]["description"])==0:
+519self.logger.print("│ ■ %s┤ "%command_str)
+520eliflen(self.commands[command]["description"])==1:
+521self.logger.print("│ ■ %s┤ %s "%(command_str,self.commands[command]["description"][0]))
+522else:
+523self.logger.print("│ ■ %s┤ %s "%(command_str,self.commands[command]["description"][0]))
+524forlineinself.commands[command]["description"][1:]:
+525self.logger.print("│ %s│ %s "%(" "*(15+2),line))
+526else:
+527command_str=command+" \x1b[90m"+"─"*(15-len(command))+"\x1b[0m"
+528iflen(self.commands[command]["description"])==0:
+529self.logger.print("│ ■ %s\x1b[90m┤\x1b[0m "%command_str)
+530eliflen(self.commands[command]["description"])==1:
+531self.logger.print("│ ■ %s\x1b[90m┤\x1b[0m %s "%(command_str,self.commands[command]["description"][0]))
+532else:
+533self.logger.print("│ ■ %s\x1b[90m┤\x1b[0m %s "%(command_str,self.commands[command]["description"][0]))
+534forlineinself.commands[command]["description"][1:]:
+535self.logger.print("│ %s\x1b[90m│\x1b[0m %s "%(" "*(15+3),line))
+536self.logger.print("│")
+537# Generic help
+538else:
+539self.logger.print("│")
+540commands=sorted(self.commands.keys())
+541forcommandincommands:
+542ifself.config.no_colors:
+543command_str=command+"─"*(15-len(command))
+544iflen(self.commands[command]["description"])==0:
+545self.logger.print("│ ■ %s┤ "%command_str)
+546eliflen(self.commands[command]["description"])==1:
+547self.logger.print("│ ■ %s┤ %s "%(command_str,self.commands[command]["description"][0]))
+548else:
+549self.logger.print("│ ■ %s┤ %s "%(command_str,self.commands[command]["description"][0]))
+550forlineinself.commands[command]["description"][1:]:
+551self.logger.print("│ %s│ %s "%(" "*(15+2),line))
+552else:
+553command_str=command+" \x1b[90m"+"─"*(15-len(command))+"\x1b[0m"
+554iflen(self.commands[command]["description"])==0:
+555self.logger.print("│ ■ %s\x1b[90m┤\x1b[0m "%command_str)
+556eliflen(self.commands[command]["description"])==1:
+557self.logger.print("│ ■ %s\x1b[90m┤\x1b[0m %s "%(command_str,self.commands[command]["description"][0]))
+558else:
+559self.logger.print("│ ■ %s\x1b[90m┤\x1b[0m %s "%(command_str,self.commands[command]["description"][0]))
+560forlineinself.commands[command]["description"][1:]:
+561self.logger.print("│ %s\x1b[90m│\x1b[0m %s "%(" "*(15+3),line))
+562self.logger.print("│")
+563
+564defprint_help_format(self):
+565"""
+566 Prints the help information for the 'format' used in remote 'ls' and 'dir' commands.
+567
+568 This function displays the format of file attributes used in the smbclient-ng shell. It explains the meaning
+569 of each character in the file attribute string, such as whether a file is read-only, hidden, or a directory.
+570 """
+571ifself.config.no_colors:
+572self.logger.print("File attributes format:\n")
+573self.logger.print("dachnrst")
+574self.logger.print("│││││││└──> Temporary")
+575self.logger.print("││││││└───> System")
+576self.logger.print("│││││└────> Read-Only")
+577self.logger.print("││││└─────> Normal")
+578self.logger.print("│││└──────> Hidden")
+579self.logger.print("││└───────> Compressed")
+580self.logger.print("│└────────> Archived")
+581self.logger.print("└─────────> Directory")
+582else:
+583self.logger.print("File attributes format:\n")
+584self.logger.print("dachnrst")
+585self.logger.print("\x1b[90m│││││││└──>\x1b[0m Temporary")
+586self.logger.print("\x1b[90m││││││└───>\x1b[0m System")
+587self.logger.print("\x1b[90m│││││└────>\x1b[0m Read-Only")
+588self.logger.print("\x1b[90m││││└─────>\x1b[0m Normal")
+589self.logger.print("\x1b[90m│││└──────>\x1b[0m Hidden")
+590self.logger.print("\x1b[90m││└───────>\x1b[0m Compressed")
+591self.logger.print("\x1b[90m│└────────>\x1b[0m Archived")
+592self.logger.print("\x1b[90m└─────────>\x1b[0m Directory")
@@ -622,524 +688,586 @@
-
13classCommandCompleter(object):
- 14"""
- 15 A class to handle command completion for the smbclient-ng shell.
- 16
- 17 This class provides a command completion feature that suggests possible command names based on the current input.
- 18 It uses a dictionary to store commands and their descriptions, which helps in providing hints during the command line
- 19 interaction in the smbclient-ng shell.
- 20
- 21 Attributes:
- 22 smbSession (SMBSession): An instance of SMBSession which maintains the current SMB session.
- 23 commands (dict): A dictionary containing command names as keys and their descriptions and subcommands as values.
- 24
- 25 Methods:
- 26 __init__(self, smbSession): Initializes the CommandCompleter with an SMBSession.
- 27 """
- 28
- 29commands={
- 30"bat":{
- 31"description":[
- 32"Pretty prints the contents of a remote file.",
- 33"Syntax: 'bat <file>'"
- 34],
- 35"subcommands":[]
- 36},
- 37"cat":{
- 38"description":[
- 39"Get the contents of a remote file.",
- 40"Syntax: 'cat <file>'"
- 41],
- 42"subcommands":[]
- 43},
- 44"cd":{
- 45"description":[
- 46"Change the current working directory.",
- 47"Syntax: 'cd <directory>'"
- 48],
- 49"subcommands":[]
- 50},
- 51"close":{
- 52"description":[
- 53"Closes the SMB connection to the remote machine.",
- 54"Syntax: 'close'"
- 55],
- 56"subcommands":[]
- 57},
- 58"connect":{
- 59"description":[
- 60"Connect to the remote machine (useful if connection timed out).",
- 61"Syntax: 'connect'"
- 62],
- 63"subcommands":[]
- 64},
- 65"debug":{
- 66"description":[
- 67"Command for dev debugging.",
- 68"Syntax: 'debug'"
- 69],
- 70"subcommands":[]
- 71},
- 72"dir":{
- 73"description":[
- 74"List the contents of the current working directory.",
- 75"Syntax: 'dir'"
- 76],
- 77"subcommands":[]
+
14classCommandCompleter(object):
+ 15"""
+ 16 A class to handle command completion for the smbclient-ng shell.
+ 17
+ 18 This class provides a command completion feature that suggests possible command names based on the current input.
+ 19 It uses a dictionary to store commands and their descriptions, which helps in providing hints during the command line
+ 20 interaction in the smbclient-ng shell.
+ 21
+ 22 Attributes:
+ 23 smbSession (SMBSession): An instance of SMBSession which maintains the current SMB session.
+ 24 commands (dict): A dictionary containing command names as keys and their descriptions and subcommands as values.
+ 25
+ 26 Methods:
+ 27 __init__(self, smbSession): Initializes the CommandCompleter with an SMBSession.
+ 28 """
+ 29
+ 30commands={
+ 31"bat":{
+ 32"description":[
+ 33"Pretty prints the contents of a remote file.",
+ 34"Syntax: 'bat <file>'"
+ 35],
+ 36"subcommands":[],
+ 37"autocomplete":["remote_file"]
+ 38},
+ 39"cat":{
+ 40"description":[
+ 41"Get the contents of a remote file.",
+ 42"Syntax: 'cat <file>'"
+ 43],
+ 44"subcommands":[],
+ 45"autocomplete":["remote_file"]
+ 46},
+ 47"cd":{
+ 48"description":[
+ 49"Change the current working directory.",
+ 50"Syntax: 'cd <directory>'"
+ 51],
+ 52"subcommands":[],
+ 53"autocomplete":["remote_directory"]
+ 54},
+ 55"close":{
+ 56"description":[
+ 57"Closes the SMB connection to the remote machine.",
+ 58"Syntax: 'close'"
+ 59],
+ 60"subcommands":[],
+ 61"autocomplete":[]
+ 62},
+ 63"connect":{
+ 64"description":[
+ 65"Connect to the remote machine (useful if connection timed out).",
+ 66"Syntax: 'connect'"
+ 67],
+ 68"subcommands":[],
+ 69"autocomplete":[]
+ 70},
+ 71"debug":{
+ 72"description":[
+ 73"Command for dev debugging.",
+ 74"Syntax: 'debug'"
+ 75],
+ 76"subcommands":[],
+ 77"autocomplete":[] 78},
- 79"exit":{
+ 79"dir":{ 80"description":[
- 81"Exits the smbclient-ng script.",
- 82"Syntax: 'exit'"
+ 81"List the contents of the current working directory.",
+ 82"Syntax: 'dir'" 83],
- 84"subcommands":[]
- 85},
- 86"get":{
- 87"description":[
- 88"Get a remote file.",
- 89"Syntax: 'get [-r] <directory or file>'"
- 90],
- 91"subcommands":[]
- 92},
- 93"help":{
- 94"description":[
- 95"Displays this help message.",
- 96"Syntax: 'help'"
- 97],
- 98"subcommands":["format"]
- 99},
-100"info":{
-101"description":[
-102"Get information about the server and or the share.",
-103"Syntax: 'info [server|share]'"
-104],
-105"subcommands":["server","share"]
-106},
-107"lbat":{
-108"description":[
-109"Pretty prints the contents of a local file.",
-110"Syntax: 'lbat <file>'"
-111],
-112"subcommands":[]
-113},
-114"lcat":{
-115"description":[
-116"Print the contents of a local file.",
-117"Syntax: 'lcat <file>'"
-118],
-119"subcommands":[]
-120},
-121"lcd":{
-122"description":[
-123"Changes the current local directory.",
-124"Syntax: 'lcd <directory>'"
-125],
-126"subcommands":[]
-127},
-128"lcp":{
-129"description":[
-130"Create a copy of a local file.",
-131"Syntax: 'lcp <srcfile> <dstfile>'"
-132],
-133"subcommands":[]
+ 84"subcommands":[],
+ 85"autocomplete":["remote_directory"]
+ 86},
+ 87"exit":{
+ 88"description":[
+ 89"Exits the smbclient-ng script.",
+ 90"Syntax: 'exit'"
+ 91],
+ 92"subcommands":[],
+ 93"autocomplete":[]
+ 94},
+ 95"get":{
+ 96"description":[
+ 97"Get a remote file.",
+ 98"Syntax: 'get [-r] <directory or file>'"
+ 99],
+100"subcommands":[],
+101"autocomplete":["remote_file"]
+102},
+103"help":{
+104"description":[
+105"Displays this help message.",
+106"Syntax: 'help'"
+107],
+108"subcommands":["format"],
+109"autocomplete":[]
+110},
+111"info":{
+112"description":[
+113"Get information about the server and or the share.",
+114"Syntax: 'info [server|share]'"
+115],
+116"subcommands":["server","share"],
+117"autocomplete":[]
+118},
+119"lbat":{
+120"description":[
+121"Pretty prints the contents of a local file.",
+122"Syntax: 'lbat <file>'"
+123],
+124"subcommands":[],
+125"autocomplete":["local_file"]
+126},
+127"lcat":{
+128"description":[
+129"Print the contents of a local file.",
+130"Syntax: 'lcat <file>'"
+131],
+132"subcommands":[],
+133"autocomplete":["local_file"]134},
-135"lls":{
+135"lcd":{136"description":[
-137"Lists the contents of the current local directory.",
-138"Syntax: 'lls'"
-139],
-140"subcommands":[]
-141},
-142"lmkdir":{
-143"description":[
-144"Creates a new local directory.",
-145"Syntax: 'lmkdir <directory>'"
-146],
-147"subcommands":[]
-148},
-149"lpwd":{
-150"description":[
-151"Shows the current local directory.",
-152"Syntax: 'lpwd'"
-153],
-154"subcommands":[]
-155},
-156"lrename":{
-157"description":[
-158"Renames a local file.",
-159"Syntax: 'lrename <oldfilename> <newfilename>'"
-160],
-161"subcommands":[]
-162},
-163"lrm":{
-164"description":[
-165"Removes a local file.",
-166"Syntax: 'lrm <file>'"
-167],
-168"subcommands":[]
-169},
-170"lrmdir":{
-171"description":[
-172"Removes a local directory.",
-173"Syntax: 'lrmdir <directory>'"
-174],
-175"subcommands":[]
-176},
-177"ls":{
-178"description":[
-179"List the contents of the current remote working directory.",
-180"Syntax: 'ls'"
-181],
-182"subcommands":[]
-183},
-184"ltree":{
-185"description":[
-186"Displays a tree view of the local directories.",
-187"Syntax: 'ltree [directory]'"
-188],
-189"subcommands":[]
+137"Changes the current local directory.",
+138"Syntax: 'lcd <directory>'"
+139],
+140"subcommands":[],
+141"autocomplete":["local_directory"]
+142},
+143"lcp":{
+144"description":[
+145"Create a copy of a local file.",
+146"Syntax: 'lcp <srcfile> <dstfile>'"
+147],
+148"subcommands":[],
+149"autocomplete":["remote_file"]
+150},
+151"lls":{
+152"description":[
+153"Lists the contents of the current local directory.",
+154"Syntax: 'lls'"
+155],
+156"subcommands":[],
+157"autocomplete":["local_directory"]
+158},
+159"lmkdir":{
+160"description":[
+161"Creates a new local directory.",
+162"Syntax: 'lmkdir <directory>'"
+163],
+164"subcommands":[],
+165"autocomplete":["local_directory"]
+166},
+167"lpwd":{
+168"description":[
+169"Shows the current local directory.",
+170"Syntax: 'lpwd'"
+171],
+172"subcommands":[],
+173"autocomplete":[]
+174},
+175"lrename":{
+176"description":[
+177"Renames a local file.",
+178"Syntax: 'lrename <oldfilename> <newfilename>'"
+179],
+180"subcommands":[],
+181"autocomplete":["local_file"]
+182},
+183"lrm":{
+184"description":[
+185"Removes a local file.",
+186"Syntax: 'lrm <file>'"
+187],
+188"subcommands":[],
+189"autocomplete":["local_file"]190},
-191"mkdir":{
+191"lrmdir":{192"description":[
-193"Creates a new remote directory.",
-194"Syntax: 'mkdir <directory>'"
+193"Removes a local directory.",
+194"Syntax: 'lrmdir <directory>'"195],
-196"subcommands":[]
-197},
-198"module":{
-199"description":[
-200"Loads a specific module for additional functionalities.",
-201"Syntax: 'module <name>'"
-202],
-203"subcommands":[]
-204},
-205"mount":{
-206"description":[
-207"Creates a mount point of the remote share on the local machine.",
-208"Syntax: 'mount <remote_path> <local_mountpoint>'"
-209],
-210"subcommands":[]
-211},
-212"put":{
-213"description":[
-214"Put a local file or directory in a remote directory.",
-215"Syntax: 'put [-r] <directory or file>'"
-216],
-217"subcommands":[]
-218},
-219"reconnect":{
-220"description":[
-221"Reconnect to the remote machine (useful if connection timed out).",
-222"Syntax: 'reconnect'"
-223],
-224"subcommands":[]
-225},
-226"reset":{
-227"description":[
-228"Reset the TTY output, useful if it was broken after printing a binary file on stdout.",
-229"Syntax: 'reset'"
-230],
-231"subcommands":[]
-232},
-233"rmdir":{
-234"description":[
-235"Removes a remote directory.",
-236"Syntax: 'rmdir <directory>'"
-237],
-238"subcommands":[]
-239},
-240"rm":{
-241"description":[
-242"Removes a remote file.",
-243"Syntax: 'rm <file>'"
-244],
-245"subcommands":[]
+196"subcommands":[],
+197"autocomplete":["local_directory"]
+198},
+199"ls":{
+200"description":[
+201"List the contents of the current remote working directory.",
+202"Syntax: 'ls'"
+203],
+204"subcommands":[],
+205"autocomplete":["remote_directory"]
+206},
+207"ltree":{
+208"description":[
+209"Displays a tree view of the local directories.",
+210"Syntax: 'ltree [directory]'"
+211],
+212"subcommands":[],
+213"autocomplete":["local_directory"]
+214},
+215"mkdir":{
+216"description":[
+217"Creates a new remote directory.",
+218"Syntax: 'mkdir <directory>'"
+219],
+220"subcommands":[],
+221"autocomplete":["remote_directory"]
+222},
+223"module":{
+224"description":[
+225"Loads a specific module for additional functionalities.",
+226"Syntax: 'module <name>'"
+227],
+228"subcommands":[],
+229"autocomplete":[]
+230},
+231"mount":{
+232"description":[
+233"Creates a mount point of the remote share on the local machine.",
+234"Syntax: 'mount <remote_path> <local_mountpoint>'"
+235],
+236"subcommands":[],
+237"autocomplete":["remote_directory"]
+238},
+239"put":{
+240"description":[
+241"Put a local file or directory in a remote directory.",
+242"Syntax: 'put [-r] <directory or file>'"
+243],
+244"subcommands":[],
+245"autocomplete":["local_file"]246},
-247"sizeof":{
+247"reconnect":{248"description":[
-249"Recursively compute the size of a folder.",
-250"Syntax: 'sizeof [directory|file]'"
+249"Reconnect to the remote machine (useful if connection timed out).",
+250"Syntax: 'reconnect'"251],
-252"subcommands":[]
-253},
-254"shares":{
-255"description":[
-256"Lists the SMB shares served by the remote machine.",
-257"Syntax: 'shares'"
-258],
-259"subcommands":["rights"]
-260},
-261"tree":{
-262"description":[
-263"Displays a tree view of the remote directories.",
-264"Syntax: 'tree [directory]'"
-265],
-266"subcommands":[]
-267},
-268"umount":{
-269"description":[
-270"Removes a mount point of the remote share on the local machine.",
-271"Syntax: 'umount <local_mount_point>'"
-272],
-273"subcommands":[]
-274},
-275"use":{
-276"description":[
-277"Use a SMB share.",
-278"Syntax: 'use <sharename>'"
-279],
-280"subcommands":[]
-281},
-282}
-283
-284def__init__(self,smbSession,config):
-285# Objects
-286self.smbSession=smbSession
-287self.config=config
-288# Pre computing for some commands
-289self.commands["help"]["subcommands"]=["format"]+list(self.commands.keys())
-290self.commands["help"]["subcommands"].remove("help")
-291
-292defcomplete(self,text,state):
-293"""
-294 Function to handle command completion in the LDAP console.
-295
-296 This function completes the user"s input based on the available options for commands in the LDAP console.
-297
-298 Args:
-299 text (str): The current text input by the user.
-300 state (int): The current state of completion.
-301
-302 Returns:
-303 str: The next completion suggestion based on the user"s input state.
-304 """
-305
-306ifstate==0:
-307
-308# No text typed yet, need the list of commands available
-309iflen(text)==0:
-310self.matches=[sforsinself.commands.keys()]
-311
-312eliflen(text)!=0:
-313# This is for the main command
-314iftext.count(" ")==0:
-315self.matches=[sforsinself.commands.keys()ifsands.startswith(text)]
-316
-317# This is for subcommands
-318eliftext.count(" ")>=1:
-319command,remainder=text.split(" ",1)
-320ifcommandinself.commands.keys():
-321ifcommand=="use":
-322# Choose SMB Share to connect to
-323self.matches=[
-324command+" "+s.lower()
-325forsinself.smbSession.list_shares().keys()
-326ifs.lower().startswith(remainder.lower())
-327]
+252"subcommands":[],
+253"autocomplete":[]
+254},
+255"reset":{
+256"description":[
+257"Reset the TTY output, useful if it was broken after printing a binary file on stdout.",
+258"Syntax: 'reset'"
+259],
+260"subcommands":[],
+261"autocomplete":[]
+262},
+263"rmdir":{
+264"description":[
+265"Removes a remote directory.",
+266"Syntax: 'rmdir <directory>'"
+267],
+268"subcommands":[],
+269"autocomplete":["remote_directory"]
+270},
+271"rm":{
+272"description":[
+273"Removes a remote file.",
+274"Syntax: 'rm <file>'"
+275],
+276"subcommands":[],
+277"autocomplete":["remote_file"]
+278},
+279"sizeof":{
+280"description":[
+281"Recursively compute the size of a folder.",
+282"Syntax: 'sizeof [directory|file]'"
+283],
+284"subcommands":[],
+285"autocomplete":["remote_directory"]
+286},
+287"sessions":{
+288"description":[
+289"Manage the SMB sessions.",
+290"Syntax: 'sessions [access|create|delete|execute|list]'"
+291],
+292"subcommands":["create","delete","execute","interact","list"],
+293"autocomplete":[]
+294},
+295"shares":{
+296"description":[
+297"Lists the SMB shares served by the remote machine.",
+298"Syntax: 'shares'"
+299],
+300"subcommands":["rights"],
+301"autocomplete":[]
+302},
+303"tree":{
+304"description":[
+305"Displays a tree view of the remote directories.",
+306"Syntax: 'tree [directory]'"
+307],
+308"subcommands":[],
+309"autocomplete":["remote_directory"]
+310},
+311"umount":{
+312"description":[
+313"Removes a mount point of the remote share on the local machine.",
+314"Syntax: 'umount <local_mount_point>'"
+315],
+316"subcommands":[],
+317"autocomplete":["remote_directory"]
+318},
+319"use":{
+320"description":[
+321"Use a SMB share.",
+322"Syntax: 'use <sharename>'"
+323],
+324"subcommands":[],
+325"autocomplete":["share"]
+326},
+327}328
-329elifcommandin["cd","dir","ls","mkdir","rmdir","tree"]:
-330# Choose remote directory
-331path=""
-332if'\\'inremainder.strip()or'/'inremainder.strip():
-333path=remainder.strip().replace('/',ntpath.sep)
-334path=ntpath.sep.join(path.split(ntpath.sep)[:-1])
-335
-336directory_contents=self.smbSession.list_contents(path=path).items()
+329def__init__(self,smbSession,config,logger):
+330# Objects
+331self.smbSession=smbSession
+332self.config=config
+333self.logger=logger
+334# Pre computing for some commands
+335self.commands["help"]["subcommands"]=["format"]+list(self.commands.keys())
+336self.commands["help"]["subcommands"].remove("help")337
-338matching_entries=[]
-339for_,entryindirectory_contents:
-340ifentry.is_directory()andentry.get_longname()notin[".",".."]:
-341iflen(path)!=0:
-342matching_entries.append(path+ntpath.sep+entry.get_longname()+ntpath.sep)
-343else:
-344matching_entries.append(entry.get_longname()+ntpath.sep)
-345
-346self.matches=[
-347command+" "+s
-348forsinmatching_entries
-349ifs.lower().startswith(remainder.lower())
-350]
+338defcomplete(self,text,state):
+339"""
+340 Function to handle command completion in the LDAP console.
+341
+342 This function completes the user"s input based on the available options for commands in the LDAP console.
+343
+344 Args:
+345 text (str): The current text input by the user.
+346 state (int): The current state of completion.
+347
+348 Returns:
+349 str: The next completion suggestion based on the user"s input state.
+350 """351
-352elifcommandin["bat","cat","debug","get","rm"]:
-353# Choose local files and directories
-354path=""
-355if'\\'inremainder.strip()or'/'inremainder.strip():
-356path=remainder.strip().replace('/',ntpath.sep)
-357path=ntpath.sep.join(path.split(ntpath.sep)[:-1])
-358
-359directory_contents=self.smbSession.list_contents(path=path).items()
-360
-361matching_entries=[]
-362for_,entryindirectory_contents:
-363ifentry.get_longname()notin[".",".."]:
-364iflen(path)!=0:
-365ifentry.is_directory():
-366matching_entries.append(path+ntpath.sep+entry.get_longname()+ntpath.sep)
-367else:
-368matching_entries.append(path+ntpath.sep+entry.get_longname())
-369else:
-370ifentry.is_directory():
-371matching_entries.append(entry.get_longname()+ntpath.sep)
-372else:
-373matching_entries.append(entry.get_longname())
-374
-375self.matches=[
-376command+" "+s
-377forsinmatching_entries
-378ifs.lower().startswith(remainder.lower())
-379]
-380
-381elifcommandin["lcd","lcp","lls","lrm","put","lmkdir","lrm","lrmdir"]:
-382# Choose directory
-383path=""
-384ifos.path.sepinremainder.strip():
-385path=path.split(os.path.sep)[:-1]
-386path=os.path.sep.join(path)
-387
-388# Current dir
-389iflen(path.strip())==0:
-390path="."
-391
-392directory_contents=os.listdir(path=path+os.path.sep)
+352ifstate==0:
+353
+354# No text typed yet, need the list of commands available
+355iflen(text)==0:
+356self.matches=[sforsinself.commands.keys()]
+357
+358# Parsing a command
+359eliflen(text)!=0:
+360# This is for the main command
+361iftext.count(" ")==0:
+362self.matches=[sforsinself.commands.keys()ifsands.startswith(text)]
+363
+364# This is for subcommands
+365eliftext.count(" ")>=1:
+366command,remainder=text.split(" ",1)
+367
+368ifcommandinself.commands.keys():
+369self.matches=[]
+370
+371# Autocomplete shares
+372if"share"inself.commands[command]["autocomplete"]:
+373# Choose SMB Share to connect to
+374shares=self.smbSession.list_shares()
+375matching_entries=[]
+376forsharenameinshares.keys():
+377ifsharename.lower().startswith(remainder.lower()):
+378matching_entries.append(shares[sharename]["name"])
+379# Final matches
+380forminmatching_entries:
+381self.matches.append(command+" "+shlex.quote(m))
+382
+383# Autocomplete directory
+384if"remote_directory"inself.commands[command]["autocomplete"]:
+385# Choose remote directory
+386path=""
+387if'\\'inremainder.strip()or'/'inremainder.strip():
+388path=remainder.strip().replace(ntpath.sep,'/')
+389path='/'.join(path.split('/')[:-1])
+390# Get remote directory contents
+391directory_contents=self.smbSession.list_contents(path=path).items()
+392# 393matching_entries=[]
-394forentryindirectory_contents:
-395ifentrynotin[".",".."]:
-396entry_path=path+os.path.sep+entry
-397ifos.path.isdir(entry_path):
-398matching_entries.append(entry_path+os.path.sep)
-399else:
-400matching_entries.append(entry_path)
-401
-402self.matches=[
-403command+" "+s
-404forsinmatching_entries
-405ifs.startswith(remainder)
-406]
-407
-408else:
-409# Generic case for subcommands
-410self.matches=[
-411command+" "+s
-412forsinself.commands[command]["subcommands"]
-413ifs.startswith(remainder)
-414]
-415else:
-416# Unknown subcommand, skipping autocomplete
-417pass
-418else:
-419self.matches=[]
-420else:
-421self.matches=self.commands.keys()[:]
-422
-423try:
-424returnself.matches[state]+" "
-425exceptIndexError:
-426returnNone
-427
-428defprint_help(self,command=None):
-429"""
-430 Prints help information for a specific command or all commands if no command is specified.
-431
-432 This method displays the help information for the command passed as an argument. If no command is specified,
-433 it prints the help information for all available commands. The help information includes the command syntax,
-434 description, and any subcommands associated with it. This method is designed to provide users with the necessary
-435 guidance on how to use the commands in the smbclient-ng shell.
-436
-437 Args:
-438 command (str, optional): The command to display help information for. If None, help for all commands is displayed.
-439
-440 Returns:
-441 None
-442 """
-443
-444ifcommandisnotNone:
-445ifcommandnotinlist(self.commands.keys())+["format"]:
-446command=None
-447
-448# Print help for a specific command
-449ifcommandisnotNone:
-450ifcommand=="format":
-451self.print_help_format()
-452else:
-453print("│")
-454ifself.config.no_colors:
-455command_str=command+"─"*(15-len(command))
-456iflen(self.commands[command]["description"])==0:
-457print("│ ■ %s┤ "%command_str)
-458eliflen(self.commands[command]["description"])==1:
-459print("│ ■ %s┤ %s "%(command_str,self.commands[command]["description"][0]))
-460else:
-461print("│ ■ %s┤ %s "%(command_str,self.commands[command]["description"][0]))
-462forlineinself.commands[command]["description"][1:]:
-463print("│ %s│ %s "%(" "*(15+2),line))
-464else:
-465command_str=command+" \x1b[90m"+"─"*(15-len(command))+"\x1b[0m"
-466iflen(self.commands[command]["description"])==0:
-467print("│ ■ %s\x1b[90m┤\x1b[0m "%command_str)
-468eliflen(self.commands[command]["description"])==1:
-469print("│ ■ %s\x1b[90m┤\x1b[0m %s "%(command_str,self.commands[command]["description"][0]))
-470else:
-471print("│ ■ %s\x1b[90m┤\x1b[0m %s "%(command_str,self.commands[command]["description"][0]))
-472forlineinself.commands[command]["description"][1:]:
-473print("│ %s\x1b[90m│\x1b[0m %s "%(" "*(15+3),line))
-474print("│")
-475# Generic help
-476else:
-477print("│")
-478commands=sorted(self.commands.keys())
-479forcommandincommands:
-480ifself.config.no_colors:
-481command_str=command+"─"*(15-len(command))
-482iflen(self.commands[command]["description"])==0:
-483print("│ ■ %s┤ "%command_str)
-484eliflen(self.commands[command]["description"])==1:
-485print("│ ■ %s┤ %s "%(command_str,self.commands[command]["description"][0]))
-486else:
-487print("│ ■ %s┤ %s "%(command_str,self.commands[command]["description"][0]))
-488forlineinself.commands[command]["description"][1:]:
-489print("│ %s│ %s "%(" "*(15+2),line))
-490else:
-491command_str=command+" \x1b[90m"+"─"*(15-len(command))+"\x1b[0m"
-492iflen(self.commands[command]["description"])==0:
-493print("│ ■ %s\x1b[90m┤\x1b[0m "%command_str)
-494eliflen(self.commands[command]["description"])==1:
-495print("│ ■ %s\x1b[90m┤\x1b[0m %s "%(command_str,self.commands[command]["description"][0]))
-496else:
-497print("│ ■ %s\x1b[90m┤\x1b[0m %s "%(command_str,self.commands[command]["description"][0]))
-498forlineinself.commands[command]["description"][1:]:
-499print("│ %s\x1b[90m│\x1b[0m %s "%(" "*(15+3),line))
-500print("│")
-501
-502defprint_help_format(self):
-503"""
-504 Prints the help information for the 'format' used in remote 'ls' and 'dir' commands.
-505
-506 This function displays the format of file attributes used in the smbclient-ng shell. It explains the meaning
-507 of each character in the file attribute string, such as whether a file is read-only, hidden, or a directory.
-508 """
-509ifself.config.no_colors:
-510print("File attributes format:\n")
-511print("dachnrst")
-512print("│││││││└──> Temporary")
-513print("││││││└───> System")
-514print("│││││└────> Read-Only")
-515print("││││└─────> Normal")
-516print("│││└──────> Hidden")
-517print("││└───────> Compressed")
-518print("│└────────> Archived")
-519print("└─────────> Directory")
-520else:
-521print("File attributes format:\n")
-522print("dachnrst")
-523print("\x1b[90m│││││││└──>\x1b[0m Temporary")
-524print("\x1b[90m││││││└───>\x1b[0m System")
-525print("\x1b[90m│││││└────>\x1b[0m Read-Only")
-526print("\x1b[90m││││└─────>\x1b[0m Normal")
-527print("\x1b[90m│││└──────>\x1b[0m Hidden")
-528print("\x1b[90m││└───────>\x1b[0m Compressed")
-529print("\x1b[90m│└────────>\x1b[0m Archived")
-530print("\x1b[90m└─────────>\x1b[0m Directory")
+394for_,entryindirectory_contents:
+395ifentry.is_directory()andentry.get_longname()notin[".",".."]:
+396iflen(path)!=0:
+397matching_entries.append(path+'/'+entry.get_longname()+'/')
+398else:
+399matching_entries.append(entry.get_longname()+'/')
+400#
+401forminmatching_entries:
+402ifm.lower().startswith(remainder.lower())orshlex.quote(m).lower().startswith(remainder.lower()):
+403self.matches.append(command+" "+shlex.quote(m))
+404
+405# Autocomplete file
+406if"remote_file"inself.commands[command]["autocomplete"]:
+407# Choose remote file
+408path=""
+409if'\\'inremainder.strip()or'/'inremainder.strip():
+410path=remainder.strip().replace(ntpath.sep,'/')
+411path='/'.join(path.split('/')[:-1])
+412# Get remote directory contents
+413directory_contents=self.smbSession.list_contents(path=path).items()
+414#
+415matching_entries=[]
+416for_,entryindirectory_contents:
+417if(notentry.is_directory())andentry.get_longname()notin[".",".."]:
+418iflen(path)!=0:
+419matching_entries.append(path+'/'+entry.get_longname())
+420else:
+421matching_entries.append(entry.get_longname())
+422#
+423forminmatching_entries:
+424ifm.lower().startswith(remainder.lower())orshlex.quote(m).lower().startswith(remainder.lower()):
+425self.matches.append(command+" "+shlex.quote(m))
+426
+427# Autocomplete local_directory
+428if"local_directory"inself.commands[command]["autocomplete"]:
+429# Choose directory
+430path=""
+431ifos.path.sepinremainder.strip():
+432path=path.split(os.path.sep)[:-1]
+433path=os.path.sep.join(path)
+434# Current dir
+435iflen(path.strip())==0:
+436path="."
+437#
+438directory_contents=os.listdir(path=path+os.path.sep)
+439matching_entries=[]
+440forentryindirectory_contents:
+441ifentrynotin[".",".."]:
+442entry_path=path+os.path.sep+entry
+443ifos.path.isdir(entry_path):
+444matching_entries.append(entry_path+os.path.sep)
+445#
+446forminmatching_entries:
+447ifm.lower().startswith(remainder.lower())orshlex.quote(m).lower().startswith(remainder.lower()):
+448self.matches.append(command+" "+shlex.quote(m))
+449
+450# Autocomplete local_file
+451if"local_file"inself.commands[command]["autocomplete"]:
+452# Choose file
+453path=""
+454ifos.path.sepinremainder.strip():
+455path=path.split(os.path.sep)[:-1]
+456path=os.path.sep.join(path)
+457# Current dir
+458iflen(path.strip())==0:
+459path="."
+460#
+461directory_contents=os.listdir(path=(path+os.path.sep))
+462matching_entries=[]
+463forentryindirectory_contents:
+464ifentrynotin[".",".."]:
+465entry_path=path+os.path.sep+entry
+466ifnotos.path.isdir(entry_path):
+467matching_entries.append(entry_path)
+468#
+469forminmatching_entries:
+470ifm.lower().startswith(remainder.lower())orshlex.quote(m).lower().startswith(remainder.lower()):
+471self.matches.append(command+" "+shlex.quote(m))
+472
+473else:
+474# Generic case for subcommands
+475forminself.commands[command]["subcommands"]:
+476ifm.startswith(remainder):
+477self.matches.append(command+" "+m)
+478else:
+479# Unknown subcommand, skipping autocomplete
+480pass
+481else:
+482self.matches=[]
+483else:
+484self.matches=self.commands.keys()[:]
+485
+486try:
+487returnself.matches[state]+" "
+488exceptIndexError:
+489returnNone
+490
+491defprint_help(self,command=None):
+492"""
+493 Prints help information for a specific command or all commands if no command is specified.
+494
+495 This method displays the help information for the command passed as an argument. If no command is specified,
+496 it prints the help information for all available commands. The help information includes the command syntax,
+497 description, and any subcommands associated with it. This method is designed to provide users with the necessary
+498 guidance on how to use the commands in the smbclient-ng shell.
+499
+500 Args:
+501 command (str, optional): The command to display help information for. If None, help for all commands is displayed.
+502
+503 Returns:
+504 None
+505 """
+506
+507ifcommandisnotNone:
+508ifcommandnotinlist(self.commands.keys())+["format"]:
+509command=None
+510
+511# Print help for a specific command
+512ifcommandisnotNone:
+513ifcommand=="format":
+514self.print_help_format()
+515else:
+516self.logger.print("│")
+517ifself.config.no_colors:
+518command_str=command+"─"*(15-len(command))
+519iflen(self.commands[command]["description"])==0:
+520self.logger.print("│ ■ %s┤ "%command_str)
+521eliflen(self.commands[command]["description"])==1:
+522self.logger.print("│ ■ %s┤ %s "%(command_str,self.commands[command]["description"][0]))
+523else:
+524self.logger.print("│ ■ %s┤ %s "%(command_str,self.commands[command]["description"][0]))
+525forlineinself.commands[command]["description"][1:]:
+526self.logger.print("│ %s│ %s "%(" "*(15+2),line))
+527else:
+528command_str=command+" \x1b[90m"+"─"*(15-len(command))+"\x1b[0m"
+529iflen(self.commands[command]["description"])==0:
+530self.logger.print("│ ■ %s\x1b[90m┤\x1b[0m "%command_str)
+531eliflen(self.commands[command]["description"])==1:
+532self.logger.print("│ ■ %s\x1b[90m┤\x1b[0m %s "%(command_str,self.commands[command]["description"][0]))
+533else:
+534self.logger.print("│ ■ %s\x1b[90m┤\x1b[0m %s "%(command_str,self.commands[command]["description"][0]))
+535forlineinself.commands[command]["description"][1:]:
+536self.logger.print("│ %s\x1b[90m│\x1b[0m %s "%(" "*(15+3),line))
+537self.logger.print("│")
+538# Generic help
+539else:
+540self.logger.print("│")
+541commands=sorted(self.commands.keys())
+542forcommandincommands:
+543ifself.config.no_colors:
+544command_str=command+"─"*(15-len(command))
+545iflen(self.commands[command]["description"])==0:
+546self.logger.print("│ ■ %s┤ "%command_str)
+547eliflen(self.commands[command]["description"])==1:
+548self.logger.print("│ ■ %s┤ %s "%(command_str,self.commands[command]["description"][0]))
+549else:
+550self.logger.print("│ ■ %s┤ %s "%(command_str,self.commands[command]["description"][0]))
+551forlineinself.commands[command]["description"][1:]:
+552self.logger.print("│ %s│ %s "%(" "*(15+2),line))
+553else:
+554command_str=command+" \x1b[90m"+"─"*(15-len(command))+"\x1b[0m"
+555iflen(self.commands[command]["description"])==0:
+556self.logger.print("│ ■ %s\x1b[90m┤\x1b[0m "%command_str)
+557eliflen(self.commands[command]["description"])==1:
+558self.logger.print("│ ■ %s\x1b[90m┤\x1b[0m %s "%(command_str,self.commands[command]["description"][0]))
+559else:
+560self.logger.print("│ ■ %s\x1b[90m┤\x1b[0m %s "%(command_str,self.commands[command]["description"][0]))
+561forlineinself.commands[command]["description"][1:]:
+562self.logger.print("│ %s\x1b[90m│\x1b[0m %s "%(" "*(15+3),line))
+563self.logger.print("│")
+564
+565defprint_help_format(self):
+566"""
+567 Prints the help information for the 'format' used in remote 'ls' and 'dir' commands.
+568
+569 This function displays the format of file attributes used in the smbclient-ng shell. It explains the meaning
+570 of each character in the file attribute string, such as whether a file is read-only, hidden, or a directory.
+571 """
+572ifself.config.no_colors:
+573self.logger.print("File attributes format:\n")
+574self.logger.print("dachnrst")
+575self.logger.print("│││││││└──> Temporary")
+576self.logger.print("││││││└───> System")
+577self.logger.print("│││││└────> Read-Only")
+578self.logger.print("││││└─────> Normal")
+579self.logger.print("│││└──────> Hidden")
+580self.logger.print("││└───────> Compressed")
+581self.logger.print("│└────────> Archived")
+582self.logger.print("└─────────> Directory")
+583else:
+584self.logger.print("File attributes format:\n")
+585self.logger.print("dachnrst")
+586self.logger.print("\x1b[90m│││││││└──>\x1b[0m Temporary")
+587self.logger.print("\x1b[90m││││││└───>\x1b[0m System")
+588self.logger.print("\x1b[90m│││││└────>\x1b[0m Read-Only")
+589self.logger.print("\x1b[90m││││└─────>\x1b[0m Normal")
+590self.logger.print("\x1b[90m│││└──────>\x1b[0m Hidden")
+591self.logger.print("\x1b[90m││└───────>\x1b[0m Compressed")
+592self.logger.print("\x1b[90m│└────────>\x1b[0m Archived")
+593self.logger.print("\x1b[90m└─────────>\x1b[0m Directory")
284def__init__(self,smbSession,config):
-285# Objects
-286self.smbSession=smbSession
-287self.config=config
-288# Pre computing for some commands
-289self.commands["help"]["subcommands"]=["format"]+list(self.commands.keys())
-290self.commands["help"]["subcommands"].remove("help")
+
329def__init__(self,smbSession,config,logger):
+330# Objects
+331self.smbSession=smbSession
+332self.config=config
+333self.logger=logger
+334# Pre computing for some commands
+335self.commands["help"]["subcommands"]=["format"]+list(self.commands.keys())
+336self.commands["help"]["subcommands"].remove("help")
@@ -1185,7 +1314,7 @@
commands =
- {'bat': {'description': ['Pretty prints the contents of a remote file.', "Syntax: 'bat <file>'"], 'subcommands': []}, 'cat': {'description': ['Get the contents of a remote 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': []}, 'debug': {'description': ['Command for dev debugging.', "Syntax: 'debug'"], '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']}, 'lbat': {'description': ['Pretty prints the contents of a local file.', "Syntax: 'lbat <file>'"], 'subcommands': []}, 'lcat': {'description': ['Print the contents of a local file.', "Syntax: 'lcat <file>'"], 'subcommands': []}, 'lcd': {'description': ['Changes the current local directory.', "Syntax: 'lcd <directory>'"], 'subcommands': []}, 'lcp': {'description': ['Create a copy of a local file.', "Syntax: 'lcp <srcfile> <dstfile>'"], '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': []}, 'lrename': {'description': ['Renames a local file.', "Syntax: 'lrename <oldfilename> <newfilename>'"], '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': []}, 'mount': {'description': ['Creates a mount point of the remote share on the local machine.', "Syntax: 'mount <remote_path> <local_mountpoint>'"], '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': []}, 'sizeof': {'description': ['Recursively compute the size of a folder.', "Syntax: 'sizeof [directory|file]'"], 'subcommands': []}, 'shares': {'description': ['Lists the SMB shares served by the remote machine.', "Syntax: 'shares'"], 'subcommands': ['rights']}, 'tree': {'description': ['Displays a tree view of the remote directories.', "Syntax: 'tree [directory]'"], 'subcommands': []}, 'umount': {'description': ['Removes a mount point of the remote share on the local machine.', "Syntax: 'umount <local_mount_point>'"], 'subcommands': []}, 'use': {'description': ['Use a SMB share.', "Syntax: 'use <sharename>'"], 'subcommands': []}}
+ {'bat': {'description': ['Pretty prints the contents of a remote file.', "Syntax: 'bat <file>'"], 'subcommands': [], 'autocomplete': ['remote_file']}, 'cat': {'description': ['Get the contents of a remote file.', "Syntax: 'cat <file>'"], 'subcommands': [], 'autocomplete': ['remote_file']}, 'cd': {'description': ['Change the current working directory.', "Syntax: 'cd <directory>'"], 'subcommands': [], 'autocomplete': ['remote_directory']}, 'close': {'description': ['Closes the SMB connection to the remote machine.', "Syntax: 'close'"], 'subcommands': [], 'autocomplete': []}, 'connect': {'description': ['Connect to the remote machine (useful if connection timed out).', "Syntax: 'connect'"], 'subcommands': [], 'autocomplete': []}, 'debug': {'description': ['Command for dev debugging.', "Syntax: 'debug'"], 'subcommands': [], 'autocomplete': []}, 'dir': {'description': ['List the contents of the current working directory.', "Syntax: 'dir'"], 'subcommands': [], 'autocomplete': ['remote_directory']}, 'exit': {'description': ['Exits the smbclient-ng script.', "Syntax: 'exit'"], 'subcommands': [], 'autocomplete': []}, 'get': {'description': ['Get a remote file.', "Syntax: 'get [-r] <directory or file>'"], 'subcommands': [], 'autocomplete': ['remote_file']}, 'help': {'description': ['Displays this help message.', "Syntax: 'help'"], 'subcommands': ['format'], 'autocomplete': []}, 'info': {'description': ['Get information about the server and or the share.', "Syntax: 'info [server|share]'"], 'subcommands': ['server', 'share'], 'autocomplete': []}, 'lbat': {'description': ['Pretty prints the contents of a local file.', "Syntax: 'lbat <file>'"], 'subcommands': [], 'autocomplete': ['local_file']}, 'lcat': {'description': ['Print the contents of a local file.', "Syntax: 'lcat <file>'"], 'subcommands': [], 'autocomplete': ['local_file']}, 'lcd': {'description': ['Changes the current local directory.', "Syntax: 'lcd <directory>'"], 'subcommands': [], 'autocomplete': ['local_directory']}, 'lcp': {'description': ['Create a copy of a local file.', "Syntax: 'lcp <srcfile> <dstfile>'"], 'subcommands': [], 'autocomplete': ['remote_file']}, 'lls': {'description': ['Lists the contents of the current local directory.', "Syntax: 'lls'"], 'subcommands': [], 'autocomplete': ['local_directory']}, 'lmkdir': {'description': ['Creates a new local directory.', "Syntax: 'lmkdir <directory>'"], 'subcommands': [], 'autocomplete': ['local_directory']}, 'lpwd': {'description': ['Shows the current local directory.', "Syntax: 'lpwd'"], 'subcommands': [], 'autocomplete': []}, 'lrename': {'description': ['Renames a local file.', "Syntax: 'lrename <oldfilename> <newfilename>'"], 'subcommands': [], 'autocomplete': ['local_file']}, 'lrm': {'description': ['Removes a local file.', "Syntax: 'lrm <file>'"], 'subcommands': [], 'autocomplete': ['local_file']}, 'lrmdir': {'description': ['Removes a local directory.', "Syntax: 'lrmdir <directory>'"], 'subcommands': [], 'autocomplete': ['local_directory']}, 'ls': {'description': ['List the contents of the current remote working directory.', "Syntax: 'ls'"], 'subcommands': [], 'autocomplete': ['remote_directory']}, 'ltree': {'description': ['Displays a tree view of the local directories.', "Syntax: 'ltree [directory]'"], 'subcommands': [], 'autocomplete': ['local_directory']}, 'mkdir': {'description': ['Creates a new remote directory.', "Syntax: 'mkdir <directory>'"], 'subcommands': [], 'autocomplete': ['remote_directory']}, 'module': {'description': ['Loads a specific module for additional functionalities.', "Syntax: 'module <name>'"], 'subcommands': [], 'autocomplete': []}, 'mount': {'description': ['Creates a mount point of the remote share on the local machine.', "Syntax: 'mount <remote_path> <local_mountpoint>'"], 'subcommands': [], 'autocomplete': ['remote_directory']}, 'put': {'description': ['Put a local file or directory in a remote directory.', "Syntax: 'put [-r] <directory or file>'"], 'subcommands': [], 'autocomplete': ['local_file']}, 'reconnect': {'description': ['Reconnect to the remote machine (useful if connection timed out).', "Syntax: 'reconnect'"], 'subcommands': [], 'autocomplete': []}, 'reset': {'description': ['Reset the TTY output, useful if it was broken after printing a binary file on stdout.', "Syntax: 'reset'"], 'subcommands': [], 'autocomplete': []}, 'rmdir': {'description': ['Removes a remote directory.', "Syntax: 'rmdir <directory>'"], 'subcommands': [], 'autocomplete': ['remote_directory']}, 'rm': {'description': ['Removes a remote file.', "Syntax: 'rm <file>'"], 'subcommands': [], 'autocomplete': ['remote_file']}, 'sizeof': {'description': ['Recursively compute the size of a folder.', "Syntax: 'sizeof [directory|file]'"], 'subcommands': [], 'autocomplete': ['remote_directory']}, 'sessions': {'description': ['Manage the SMB sessions.', "Syntax: 'sessions [access|create|delete|execute|list]'"], 'subcommands': ['create', 'delete', 'execute', 'interact', 'list'], 'autocomplete': []}, 'shares': {'description': ['Lists the SMB shares served by the remote machine.', "Syntax: 'shares'"], 'subcommands': ['rights'], 'autocomplete': []}, 'tree': {'description': ['Displays a tree view of the remote directories.', "Syntax: 'tree [directory]'"], 'subcommands': [], 'autocomplete': ['remote_directory']}, 'umount': {'description': ['Removes a mount point of the remote share on the local machine.', "Syntax: 'umount <local_mount_point>'"], 'subcommands': [], 'autocomplete': ['remote_directory']}, 'use': {'description': ['Use a SMB share.', "Syntax: 'use <sharename>'"], 'subcommands': [], 'autocomplete': ['share']}}
@@ -1215,6 +1344,17 @@
+
+
+
+ logger
+
+
+
+
+
+
+
@@ -1227,141 +1367,158 @@
-
292defcomplete(self,text,state):
-293"""
-294 Function to handle command completion in the LDAP console.
-295
-296 This function completes the user"s input based on the available options for commands in the LDAP console.
-297
-298 Args:
-299 text (str): The current text input by the user.
-300 state (int): The current state of completion.
-301
-302 Returns:
-303 str: The next completion suggestion based on the user"s input state.
-304 """
-305
-306ifstate==0:
-307
-308# No text typed yet, need the list of commands available
-309iflen(text)==0:
-310self.matches=[sforsinself.commands.keys()]
-311
-312eliflen(text)!=0:
-313# This is for the main command
-314iftext.count(" ")==0:
-315self.matches=[sforsinself.commands.keys()ifsands.startswith(text)]
-316
-317# This is for subcommands
-318eliftext.count(" ")>=1:
-319command,remainder=text.split(" ",1)
-320ifcommandinself.commands.keys():
-321ifcommand=="use":
-322# Choose SMB Share to connect to
-323self.matches=[
-324command+" "+s.lower()
-325forsinself.smbSession.list_shares().keys()
-326ifs.lower().startswith(remainder.lower())
-327]
-328
-329elifcommandin["cd","dir","ls","mkdir","rmdir","tree"]:
-330# Choose remote directory
-331path=""
-332if'\\'inremainder.strip()or'/'inremainder.strip():
-333path=remainder.strip().replace('/',ntpath.sep)
-334path=ntpath.sep.join(path.split(ntpath.sep)[:-1])
-335
-336directory_contents=self.smbSession.list_contents(path=path).items()
-337
-338matching_entries=[]
-339for_,entryindirectory_contents:
-340ifentry.is_directory()andentry.get_longname()notin[".",".."]:
-341iflen(path)!=0:
-342matching_entries.append(path+ntpath.sep+entry.get_longname()+ntpath.sep)
-343else:
-344matching_entries.append(entry.get_longname()+ntpath.sep)
-345
-346self.matches=[
-347command+" "+s
-348forsinmatching_entries
-349ifs.lower().startswith(remainder.lower())
-350]
+
338defcomplete(self,text,state):
+339"""
+340 Function to handle command completion in the LDAP console.
+341
+342 This function completes the user"s input based on the available options for commands in the LDAP console.
+343
+344 Args:
+345 text (str): The current text input by the user.
+346 state (int): The current state of completion.
+347
+348 Returns:
+349 str: The next completion suggestion based on the user"s input state.
+350 """351
-352elifcommandin["bat","cat","debug","get","rm"]:
-353# Choose local files and directories
-354path=""
-355if'\\'inremainder.strip()or'/'inremainder.strip():
-356path=remainder.strip().replace('/',ntpath.sep)
-357path=ntpath.sep.join(path.split(ntpath.sep)[:-1])
-358
-359directory_contents=self.smbSession.list_contents(path=path).items()
-360
-361matching_entries=[]
-362for_,entryindirectory_contents:
-363ifentry.get_longname()notin[".",".."]:
-364iflen(path)!=0:
-365ifentry.is_directory():
-366matching_entries.append(path+ntpath.sep+entry.get_longname()+ntpath.sep)
-367else:
-368matching_entries.append(path+ntpath.sep+entry.get_longname())
-369else:
-370ifentry.is_directory():
-371matching_entries.append(entry.get_longname()+ntpath.sep)
-372else:
-373matching_entries.append(entry.get_longname())
-374
-375self.matches=[
-376command+" "+s
-377forsinmatching_entries
-378ifs.lower().startswith(remainder.lower())
-379]
-380
-381elifcommandin["lcd","lcp","lls","lrm","put","lmkdir","lrm","lrmdir"]:
-382# Choose directory
-383path=""
-384ifos.path.sepinremainder.strip():
-385path=path.split(os.path.sep)[:-1]
-386path=os.path.sep.join(path)
-387
-388# Current dir
-389iflen(path.strip())==0:
-390path="."
-391
-392directory_contents=os.listdir(path=path+os.path.sep)
+352ifstate==0:
+353
+354# No text typed yet, need the list of commands available
+355iflen(text)==0:
+356self.matches=[sforsinself.commands.keys()]
+357
+358# Parsing a command
+359eliflen(text)!=0:
+360# This is for the main command
+361iftext.count(" ")==0:
+362self.matches=[sforsinself.commands.keys()ifsands.startswith(text)]
+363
+364# This is for subcommands
+365eliftext.count(" ")>=1:
+366command,remainder=text.split(" ",1)
+367
+368ifcommandinself.commands.keys():
+369self.matches=[]
+370
+371# Autocomplete shares
+372if"share"inself.commands[command]["autocomplete"]:
+373# Choose SMB Share to connect to
+374shares=self.smbSession.list_shares()
+375matching_entries=[]
+376forsharenameinshares.keys():
+377ifsharename.lower().startswith(remainder.lower()):
+378matching_entries.append(shares[sharename]["name"])
+379# Final matches
+380forminmatching_entries:
+381self.matches.append(command+" "+shlex.quote(m))
+382
+383# Autocomplete directory
+384if"remote_directory"inself.commands[command]["autocomplete"]:
+385# Choose remote directory
+386path=""
+387if'\\'inremainder.strip()or'/'inremainder.strip():
+388path=remainder.strip().replace(ntpath.sep,'/')
+389path='/'.join(path.split('/')[:-1])
+390# Get remote directory contents
+391directory_contents=self.smbSession.list_contents(path=path).items()
+392# 393matching_entries=[]
-394forentryindirectory_contents:
-395ifentrynotin[".",".."]:
-396entry_path=path+os.path.sep+entry
-397ifos.path.isdir(entry_path):
-398matching_entries.append(entry_path+os.path.sep)
-399else:
-400matching_entries.append(entry_path)
-401
-402self.matches=[
-403command+" "+s
-404forsinmatching_entries
-405ifs.startswith(remainder)
-406]
-407
-408else:
-409# Generic case for subcommands
-410self.matches=[
-411command+" "+s
-412forsinself.commands[command]["subcommands"]
-413ifs.startswith(remainder)
-414]
-415else:
-416# Unknown subcommand, skipping autocomplete
-417pass
-418else:
-419self.matches=[]
-420else:
-421self.matches=self.commands.keys()[:]
-422
-423try:
-424returnself.matches[state]+" "
-425exceptIndexError:
-426returnNone
+394for_,entryindirectory_contents:
+395ifentry.is_directory()andentry.get_longname()notin[".",".."]:
+396iflen(path)!=0:
+397matching_entries.append(path+'/'+entry.get_longname()+'/')
+398else:
+399matching_entries.append(entry.get_longname()+'/')
+400#
+401forminmatching_entries:
+402ifm.lower().startswith(remainder.lower())orshlex.quote(m).lower().startswith(remainder.lower()):
+403self.matches.append(command+" "+shlex.quote(m))
+404
+405# Autocomplete file
+406if"remote_file"inself.commands[command]["autocomplete"]:
+407# Choose remote file
+408path=""
+409if'\\'inremainder.strip()or'/'inremainder.strip():
+410path=remainder.strip().replace(ntpath.sep,'/')
+411path='/'.join(path.split('/')[:-1])
+412# Get remote directory contents
+413directory_contents=self.smbSession.list_contents(path=path).items()
+414#
+415matching_entries=[]
+416for_,entryindirectory_contents:
+417if(notentry.is_directory())andentry.get_longname()notin[".",".."]:
+418iflen(path)!=0:
+419matching_entries.append(path+'/'+entry.get_longname())
+420else:
+421matching_entries.append(entry.get_longname())
+422#
+423forminmatching_entries:
+424ifm.lower().startswith(remainder.lower())orshlex.quote(m).lower().startswith(remainder.lower()):
+425self.matches.append(command+" "+shlex.quote(m))
+426
+427# Autocomplete local_directory
+428if"local_directory"inself.commands[command]["autocomplete"]:
+429# Choose directory
+430path=""
+431ifos.path.sepinremainder.strip():
+432path=path.split(os.path.sep)[:-1]
+433path=os.path.sep.join(path)
+434# Current dir
+435iflen(path.strip())==0:
+436path="."
+437#
+438directory_contents=os.listdir(path=path+os.path.sep)
+439matching_entries=[]
+440forentryindirectory_contents:
+441ifentrynotin[".",".."]:
+442entry_path=path+os.path.sep+entry
+443ifos.path.isdir(entry_path):
+444matching_entries.append(entry_path+os.path.sep)
+445#
+446forminmatching_entries:
+447ifm.lower().startswith(remainder.lower())orshlex.quote(m).lower().startswith(remainder.lower()):
+448self.matches.append(command+" "+shlex.quote(m))
+449
+450# Autocomplete local_file
+451if"local_file"inself.commands[command]["autocomplete"]:
+452# Choose file
+453path=""
+454ifos.path.sepinremainder.strip():
+455path=path.split(os.path.sep)[:-1]
+456path=os.path.sep.join(path)
+457# Current dir
+458iflen(path.strip())==0:
+459path="."
+460#
+461directory_contents=os.listdir(path=(path+os.path.sep))
+462matching_entries=[]
+463forentryindirectory_contents:
+464ifentrynotin[".",".."]:
+465entry_path=path+os.path.sep+entry
+466ifnotos.path.isdir(entry_path):
+467matching_entries.append(entry_path)
+468#
+469forminmatching_entries:
+470ifm.lower().startswith(remainder.lower())orshlex.quote(m).lower().startswith(remainder.lower()):
+471self.matches.append(command+" "+shlex.quote(m))
+472
+473else:
+474# Generic case for subcommands
+475forminself.commands[command]["subcommands"]:
+476ifm.startswith(remainder):
+477self.matches.append(command+" "+m)
+478else:
+479# Unknown subcommand, skipping autocomplete
+480pass
+481else:
+482self.matches=[]
+483else:
+484self.matches=self.commands.keys()[:]
+485
+486try:
+487returnself.matches[state]+" "
+488exceptIndexError:
+489returnNone
@@ -1390,79 +1547,79 @@
-
428defprint_help(self,command=None):
-429"""
-430 Prints help information for a specific command or all commands if no command is specified.
-431
-432 This method displays the help information for the command passed as an argument. If no command is specified,
-433 it prints the help information for all available commands. The help information includes the command syntax,
-434 description, and any subcommands associated with it. This method is designed to provide users with the necessary
-435 guidance on how to use the commands in the smbclient-ng shell.
-436
-437 Args:
-438 command (str, optional): The command to display help information for. If None, help for all commands is displayed.
-439
-440 Returns:
-441 None
-442 """
-443
-444ifcommandisnotNone:
-445ifcommandnotinlist(self.commands.keys())+["format"]:
-446command=None
-447
-448# Print help for a specific command
-449ifcommandisnotNone:
-450ifcommand=="format":
-451self.print_help_format()
-452else:
-453print("│")
-454ifself.config.no_colors:
-455command_str=command+"─"*(15-len(command))
-456iflen(self.commands[command]["description"])==0:
-457print("│ ■ %s┤ "%command_str)
-458eliflen(self.commands[command]["description"])==1:
-459print("│ ■ %s┤ %s "%(command_str,self.commands[command]["description"][0]))
-460else:
-461print("│ ■ %s┤ %s "%(command_str,self.commands[command]["description"][0]))
-462forlineinself.commands[command]["description"][1:]:
-463print("│ %s│ %s "%(" "*(15+2),line))
-464else:
-465command_str=command+" \x1b[90m"+"─"*(15-len(command))+"\x1b[0m"
-466iflen(self.commands[command]["description"])==0:
-467print("│ ■ %s\x1b[90m┤\x1b[0m "%command_str)
-468eliflen(self.commands[command]["description"])==1:
-469print("│ ■ %s\x1b[90m┤\x1b[0m %s "%(command_str,self.commands[command]["description"][0]))
-470else:
-471print("│ ■ %s\x1b[90m┤\x1b[0m %s "%(command_str,self.commands[command]["description"][0]))
-472forlineinself.commands[command]["description"][1:]:
-473print("│ %s\x1b[90m│\x1b[0m %s "%(" "*(15+3),line))
-474print("│")
-475# Generic help
-476else:
-477print("│")
-478commands=sorted(self.commands.keys())
-479forcommandincommands:
-480ifself.config.no_colors:
-481command_str=command+"─"*(15-len(command))
-482iflen(self.commands[command]["description"])==0:
-483print("│ ■ %s┤ "%command_str)
-484eliflen(self.commands[command]["description"])==1:
-485print("│ ■ %s┤ %s "%(command_str,self.commands[command]["description"][0]))
-486else:
-487print("│ ■ %s┤ %s "%(command_str,self.commands[command]["description"][0]))
-488forlineinself.commands[command]["description"][1:]:
-489print("│ %s│ %s "%(" "*(15+2),line))
-490else:
-491command_str=command+" \x1b[90m"+"─"*(15-len(command))+"\x1b[0m"
-492iflen(self.commands[command]["description"])==0:
-493print("│ ■ %s\x1b[90m┤\x1b[0m "%command_str)
-494eliflen(self.commands[command]["description"])==1:
-495print("│ ■ %s\x1b[90m┤\x1b[0m %s "%(command_str,self.commands[command]["description"][0]))
-496else:
-497print("│ ■ %s\x1b[90m┤\x1b[0m %s "%(command_str,self.commands[command]["description"][0]))
-498forlineinself.commands[command]["description"][1:]:
-499print("│ %s\x1b[90m│\x1b[0m %s "%(" "*(15+3),line))
-500print("│")
+
491defprint_help(self,command=None):
+492"""
+493 Prints help information for a specific command or all commands if no command is specified.
+494
+495 This method displays the help information for the command passed as an argument. If no command is specified,
+496 it prints the help information for all available commands. The help information includes the command syntax,
+497 description, and any subcommands associated with it. This method is designed to provide users with the necessary
+498 guidance on how to use the commands in the smbclient-ng shell.
+499
+500 Args:
+501 command (str, optional): The command to display help information for. If None, help for all commands is displayed.
+502
+503 Returns:
+504 None
+505 """
+506
+507ifcommandisnotNone:
+508ifcommandnotinlist(self.commands.keys())+["format"]:
+509command=None
+510
+511# Print help for a specific command
+512ifcommandisnotNone:
+513ifcommand=="format":
+514self.print_help_format()
+515else:
+516self.logger.print("│")
+517ifself.config.no_colors:
+518command_str=command+"─"*(15-len(command))
+519iflen(self.commands[command]["description"])==0:
+520self.logger.print("│ ■ %s┤ "%command_str)
+521eliflen(self.commands[command]["description"])==1:
+522self.logger.print("│ ■ %s┤ %s "%(command_str,self.commands[command]["description"][0]))
+523else:
+524self.logger.print("│ ■ %s┤ %s "%(command_str,self.commands[command]["description"][0]))
+525forlineinself.commands[command]["description"][1:]:
+526self.logger.print("│ %s│ %s "%(" "*(15+2),line))
+527else:
+528command_str=command+" \x1b[90m"+"─"*(15-len(command))+"\x1b[0m"
+529iflen(self.commands[command]["description"])==0:
+530self.logger.print("│ ■ %s\x1b[90m┤\x1b[0m "%command_str)
+531eliflen(self.commands[command]["description"])==1:
+532self.logger.print("│ ■ %s\x1b[90m┤\x1b[0m %s "%(command_str,self.commands[command]["description"][0]))
+533else:
+534self.logger.print("│ ■ %s\x1b[90m┤\x1b[0m %s "%(command_str,self.commands[command]["description"][0]))
+535forlineinself.commands[command]["description"][1:]:
+536self.logger.print("│ %s\x1b[90m│\x1b[0m %s "%(" "*(15+3),line))
+537self.logger.print("│")
+538# Generic help
+539else:
+540self.logger.print("│")
+541commands=sorted(self.commands.keys())
+542forcommandincommands:
+543ifself.config.no_colors:
+544command_str=command+"─"*(15-len(command))
+545iflen(self.commands[command]["description"])==0:
+546self.logger.print("│ ■ %s┤ "%command_str)
+547eliflen(self.commands[command]["description"])==1:
+548self.logger.print("│ ■ %s┤ %s "%(command_str,self.commands[command]["description"][0]))
+549else:
+550self.logger.print("│ ■ %s┤ %s "%(command_str,self.commands[command]["description"][0]))
+551forlineinself.commands[command]["description"][1:]:
+552self.logger.print("│ %s│ %s "%(" "*(15+2),line))
+553else:
+554command_str=command+" \x1b[90m"+"─"*(15-len(command))+"\x1b[0m"
+555iflen(self.commands[command]["description"])==0:
+556self.logger.print("│ ■ %s\x1b[90m┤\x1b[0m "%command_str)
+557eliflen(self.commands[command]["description"])==1:
+558self.logger.print("│ ■ %s\x1b[90m┤\x1b[0m %s "%(command_str,self.commands[command]["description"][0]))
+559else:
+560self.logger.print("│ ■ %s\x1b[90m┤\x1b[0m %s "%(command_str,self.commands[command]["description"][0]))
+561forlineinself.commands[command]["description"][1:]:
+562self.logger.print("│ %s\x1b[90m│\x1b[0m %s "%(" "*(15+3),line))
+563self.logger.print("│")
@@ -1493,35 +1650,35 @@
-
502defprint_help_format(self):
-503"""
-504 Prints the help information for the 'format' used in remote 'ls' and 'dir' commands.
-505
-506 This function displays the format of file attributes used in the smbclient-ng shell. It explains the meaning
-507 of each character in the file attribute string, such as whether a file is read-only, hidden, or a directory.
-508 """
-509ifself.config.no_colors:
-510print("File attributes format:\n")
-511print("dachnrst")
-512print("│││││││└──> Temporary")
-513print("││││││└───> System")
-514print("│││││└────> Read-Only")
-515print("││││└─────> Normal")
-516print("│││└──────> Hidden")
-517print("││└───────> Compressed")
-518print("│└────────> Archived")
-519print("└─────────> Directory")
-520else:
-521print("File attributes format:\n")
-522print("dachnrst")
-523print("\x1b[90m│││││││└──>\x1b[0m Temporary")
-524print("\x1b[90m││││││└───>\x1b[0m System")
-525print("\x1b[90m│││││└────>\x1b[0m Read-Only")
-526print("\x1b[90m││││└─────>\x1b[0m Normal")
-527print("\x1b[90m│││└──────>\x1b[0m Hidden")
-528print("\x1b[90m││└───────>\x1b[0m Compressed")
-529print("\x1b[90m│└────────>\x1b[0m Archived")
-530print("\x1b[90m└─────────>\x1b[0m Directory")
+
565defprint_help_format(self):
+566"""
+567 Prints the help information for the 'format' used in remote 'ls' and 'dir' commands.
+568
+569 This function displays the format of file attributes used in the smbclient-ng shell. It explains the meaning
+570 of each character in the file attribute string, such as whether a file is read-only, hidden, or a directory.
+571 """
+572ifself.config.no_colors:
+573self.logger.print("File attributes format:\n")
+574self.logger.print("dachnrst")
+575self.logger.print("│││││││└──> Temporary")
+576self.logger.print("││││││└───> System")
+577self.logger.print("│││││└────> Read-Only")
+578self.logger.print("││││└─────> Normal")
+579self.logger.print("│││└──────> Hidden")
+580self.logger.print("││└───────> Compressed")
+581self.logger.print("│└────────> Archived")
+582self.logger.print("└─────────> Directory")
+583else:
+584self.logger.print("File attributes format:\n")
+585self.logger.print("dachnrst")
+586self.logger.print("\x1b[90m│││││││└──>\x1b[0m Temporary")
+587self.logger.print("\x1b[90m││││││└───>\x1b[0m System")
+588self.logger.print("\x1b[90m│││││└────>\x1b[0m Read-Only")
+589self.logger.print("\x1b[90m││││└─────>\x1b[0m Normal")
+590self.logger.print("\x1b[90m│││└──────>\x1b[0m Hidden")
+591self.logger.print("\x1b[90m││└───────>\x1b[0m Compressed")
+592self.logger.print("\x1b[90m│└────────>\x1b[0m Archived")
+593self.logger.print("\x1b[90m└─────────>\x1b[0m Directory")
1#!/usr/bin/env python3
+ 2# -*- coding: utf-8 -*-
+ 3# File name : Credentials.py
+ 4# Author : Podalirius (@podalirius_)
+ 5# Date created : 22 June 2024
+ 6
+ 7
+ 8fromsmbclientng.core.utilsimportparse_lm_nt_hashes
+ 9importre
+ 10importbinascii
+ 11
+ 12
+ 13classCredentials(object):
+ 14"""
+ 15 Documentation for class Credentials
+ 16 """
+ 17
+ 18# Identity
+ 19domain=None
+ 20username=None
+ 21password=None
+ 22# Hashes
+ 23nt_hex=""
+ 24nt_raw=""
+ 25lm_hex=""
+ 26lm_raw=""
+ 27# Kerberos
+ 28use_kerberos=False
+ 29aesKey=None
+ 30kdcHost=None
+ 31
+ 32def__init__(self,domain,username,password,hashes=None,use_kerberos=False,aesKey=None,kdcHost=None):
+ 33super(Credentials,self).__init__()
+ 34# Identity
+ 35self.domain=domain
+ 36self.username=username
+ 37self.password=password
+ 38# Hashes
+ 39self.nt_hex=""
+ 40self.nt_raw=""
+ 41self.lm_hex=""
+ 42self.lm_raw=""
+ 43self.set_hashes(hashes=hashes)
+ 44# Kerberos
+ 45self.use_kerberos=use_kerberos
+ 46self.kdcHost=kdcHost
+ 47self.aesKey=aesKey
+ 48
+ 49defset_hashes(self,hashes):
+ 50"""
+ 51 Sets the LM and NT hashes for the credentials.
+ 52
+ 53 This method parses the provided hash string and sets the LM and NT hash values accordingly.
+ 54 If the hash string is valid and contains both LM and NT hashes, they are set directly.
+ 55 If only one hash is provided, the other is set to its default value.
+ 56 If the hash string is None or invalid, both hashes are set to None.
+ 57
+ 58 Args:
+ 59 hashes (str): A string containing LM and NT hashes separated by a colon.
+ 60 """
+ 61
+ 62self.nt_hex=""
+ 63self.nt_raw=""
+ 64self.lm_hex=""
+ 65self.lm_raw=""
+ 66
+ 67lmhash,nthash=None,None
+ 68ifhashesisnotNone:
+ 69matched=re.search("([0-9a-f]{32})(:)?([0-9a-f]{32})?",hashes.lower(),re.IGNORECASE)
+ 70ifmatchedisnotNone:
+ 71lmhash=matched.groups()[0]
+ 72nthash=matched.groups()[2]
+ 73iflmhashisNone:
+ 74lmhash="aad3b435b51404eeaad3b435b51404ee"
+ 75ifnthashisNone:
+ 76nthash="31d6cfe0d16ae931b73c59d7e0c089c0"
+ 77self.lm_hex=lmhash
+ 78self.lm_raw=binascii.unhexlify(lmhash)
+ 79self.nt_hex=nthash
+ 80self.nt_raw=binascii.unhexlify(nthash)
+ 81
+ 82defcanPassTheHash(self):
+ 83"""
+ 84 Determines if the current credentials can be used for a pass-the-hash attack.
+ 85
+ 86 This method checks if both LM and NT hashes are available and not None. If both hashes are set,
+ 87 it indicates that the credentials may be used for a pass-the-hash attack.
+ 88
+ 89 Returns:
+ 90 bool: True if both LM and NT hashes are available, False otherwise.
+ 91 """
+ 92
+ 93returnbool(
+ 94(self.nt_hexisnotNone)
+ 95and(self.nt_rawisnotNone)
+ 96and(self.lm_hexisnotNone)
+ 97and(self.lm_rawisnotNone)
+ 98)
+ 99
+100def__dict__(self):
+101return{
+102"domain":self.domain,
+103"username":self.username,
+104"password":self.password,
+105"hashes":{
+106"lm_hash":self.lm_hex,
+107"nt_hash":self.nt_hex
+108},
+109"use_kerberos":self.use_kerberos,
+110"aesKey":self.aesKey,
+111"kdcHost":self.kdcHost
+112}
+113
+114def__repr__(self):
+115returnf"<Credentials for '{self.domain}\\{self.username}'>"
+
+
+
+
+
+
+
+
+ class
+ Credentials:
+
+
+
+
+
+
14classCredentials(object):
+ 15"""
+ 16 Documentation for class Credentials
+ 17 """
+ 18
+ 19# Identity
+ 20domain=None
+ 21username=None
+ 22password=None
+ 23# Hashes
+ 24nt_hex=""
+ 25nt_raw=""
+ 26lm_hex=""
+ 27lm_raw=""
+ 28# Kerberos
+ 29use_kerberos=False
+ 30aesKey=None
+ 31kdcHost=None
+ 32
+ 33def__init__(self,domain,username,password,hashes=None,use_kerberos=False,aesKey=None,kdcHost=None):
+ 34super(Credentials,self).__init__()
+ 35# Identity
+ 36self.domain=domain
+ 37self.username=username
+ 38self.password=password
+ 39# Hashes
+ 40self.nt_hex=""
+ 41self.nt_raw=""
+ 42self.lm_hex=""
+ 43self.lm_raw=""
+ 44self.set_hashes(hashes=hashes)
+ 45# Kerberos
+ 46self.use_kerberos=use_kerberos
+ 47self.kdcHost=kdcHost
+ 48self.aesKey=aesKey
+ 49
+ 50defset_hashes(self,hashes):
+ 51"""
+ 52 Sets the LM and NT hashes for the credentials.
+ 53
+ 54 This method parses the provided hash string and sets the LM and NT hash values accordingly.
+ 55 If the hash string is valid and contains both LM and NT hashes, they are set directly.
+ 56 If only one hash is provided, the other is set to its default value.
+ 57 If the hash string is None or invalid, both hashes are set to None.
+ 58
+ 59 Args:
+ 60 hashes (str): A string containing LM and NT hashes separated by a colon.
+ 61 """
+ 62
+ 63self.nt_hex=""
+ 64self.nt_raw=""
+ 65self.lm_hex=""
+ 66self.lm_raw=""
+ 67
+ 68lmhash,nthash=None,None
+ 69ifhashesisnotNone:
+ 70matched=re.search("([0-9a-f]{32})(:)?([0-9a-f]{32})?",hashes.lower(),re.IGNORECASE)
+ 71ifmatchedisnotNone:
+ 72lmhash=matched.groups()[0]
+ 73nthash=matched.groups()[2]
+ 74iflmhashisNone:
+ 75lmhash="aad3b435b51404eeaad3b435b51404ee"
+ 76ifnthashisNone:
+ 77nthash="31d6cfe0d16ae931b73c59d7e0c089c0"
+ 78self.lm_hex=lmhash
+ 79self.lm_raw=binascii.unhexlify(lmhash)
+ 80self.nt_hex=nthash
+ 81self.nt_raw=binascii.unhexlify(nthash)
+ 82
+ 83defcanPassTheHash(self):
+ 84"""
+ 85 Determines if the current credentials can be used for a pass-the-hash attack.
+ 86
+ 87 This method checks if both LM and NT hashes are available and not None. If both hashes are set,
+ 88 it indicates that the credentials may be used for a pass-the-hash attack.
+ 89
+ 90 Returns:
+ 91 bool: True if both LM and NT hashes are available, False otherwise.
+ 92 """
+ 93
+ 94returnbool(
+ 95(self.nt_hexisnotNone)
+ 96and(self.nt_rawisnotNone)
+ 97and(self.lm_hexisnotNone)
+ 98and(self.lm_rawisnotNone)
+ 99)
+100
+101def__dict__(self):
+102return{
+103"domain":self.domain,
+104"username":self.username,
+105"password":self.password,
+106"hashes":{
+107"lm_hash":self.lm_hex,
+108"nt_hash":self.nt_hex
+109},
+110"use_kerberos":self.use_kerberos,
+111"aesKey":self.aesKey,
+112"kdcHost":self.kdcHost
+113}
+114
+115def__repr__(self):
+116returnf"<Credentials for '{self.domain}\\{self.username}'>"
+
50defset_hashes(self,hashes):
+51"""
+52 Sets the LM and NT hashes for the credentials.
+53
+54 This method parses the provided hash string and sets the LM and NT hash values accordingly.
+55 If the hash string is valid and contains both LM and NT hashes, they are set directly.
+56 If only one hash is provided, the other is set to its default value.
+57 If the hash string is None or invalid, both hashes are set to None.
+58
+59 Args:
+60 hashes (str): A string containing LM and NT hashes separated by a colon.
+61 """
+62
+63self.nt_hex=""
+64self.nt_raw=""
+65self.lm_hex=""
+66self.lm_raw=""
+67
+68lmhash,nthash=None,None
+69ifhashesisnotNone:
+70matched=re.search("([0-9a-f]{32})(:)?([0-9a-f]{32})?",hashes.lower(),re.IGNORECASE)
+71ifmatchedisnotNone:
+72lmhash=matched.groups()[0]
+73nthash=matched.groups()[2]
+74iflmhashisNone:
+75lmhash="aad3b435b51404eeaad3b435b51404ee"
+76ifnthashisNone:
+77nthash="31d6cfe0d16ae931b73c59d7e0c089c0"
+78self.lm_hex=lmhash
+79self.lm_raw=binascii.unhexlify(lmhash)
+80self.nt_hex=nthash
+81self.nt_raw=binascii.unhexlify(nthash)
+
+
+
+
Sets the LM and NT hashes for the credentials.
+
+
This method parses the provided hash string and sets the LM and NT hash values accordingly.
+If the hash string is valid and contains both LM and NT hashes, they are set directly.
+If only one hash is provided, the other is set to its default value.
+If the hash string is None or invalid, both hashes are set to None.
+
+
Args:
+ hashes (str): A string containing LM and NT hashes separated by a colon.
+
+
+
+
+
+
+
+
+ def
+ canPassTheHash(self):
+
+
+
+
+
+
83defcanPassTheHash(self):
+84"""
+85 Determines if the current credentials can be used for a pass-the-hash attack.
+86
+87 This method checks if both LM and NT hashes are available and not None. If both hashes are set,
+88 it indicates that the credentials may be used for a pass-the-hash attack.
+89
+90 Returns:
+91 bool: True if both LM and NT hashes are available, False otherwise.
+92 """
+93
+94returnbool(
+95(self.nt_hexisnotNone)
+96and(self.nt_rawisnotNone)
+97and(self.lm_hexisnotNone)
+98and(self.lm_rawisnotNone)
+99)
+
+
+
+
Determines if the current credentials can be used for a pass-the-hash attack.
+
+
This method checks if both LM and NT hashes are available and not None. If both hashes are set,
+it indicates that the credentials may be used for a pass-the-hash attack.
+
+
Returns:
+ bool: True if both LM and NT hashes are available, False otherwise.
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/documentation/smbclientng/core/InteractiveShell.html b/documentation/smbclientng/core/InteractiveShell.html
index 62b6897..181a535 100644
--- a/documentation/smbclientng/core/InteractiveShell.html
+++ b/documentation/smbclientng/core/InteractiveShell.html
@@ -46,22 +46,28 @@
50defsmb_share_is_set(func):
-51defwrapper(*args,**kwargs):
-52self,arguments,command=args[0],args[1],args[2]
-53ifself.smbSession.smb_shareisnotNone:
-54returnfunc(*args,**kwargs)
-55else:
-56print("[!] You must open a share first, try the 'use <share>' command.")
-57returnNone
-58returnwrapper
+
51defsmb_share_is_set(func):
+52defwrapper(*args,**kwargs):
+53self,arguments,command=args[0],args[1],args[2]
+54ifself.sessionsManager.current_session.smb_shareisnotNone:
+55returnfunc(*args,**kwargs)
+56else:
+57print("[!] You must open a share first, try the 'use <share>' command.")
+58returnNone
+59returnwrapper
@@ -1277,943 +1429,1088 @@
-
61classInteractiveShell(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
- 81def__init__(self,smbSession,config):
- 82# Objects
- 83self.smbSession=smbSession
- 84self.config=config
- 85self.commandCompleterObject=CommandCompleter(smbSession=self.smbSession,config=self.config)
- 86readline.set_completer(self.commandCompleterObject.complete)
- 87readline.parse_and_bind("tab: complete")
- 88readline.set_completer_delims("\n")
- 89# Additional modules
- 90self.modules={}
- 91self.__load_modules()
- 92
- 93defrun(self):
- 94running=True
- 95whilerunning:
- 96try:
- 97user_input=input(self.__prompt()).strip().split(" ")
- 98command,arguments=user_input[0].lower(),user_input[1:]
- 99
-100# Exit the command line
-101ifcommand=="exit":
-102running=False
-103
-104elifcommandinself.commandCompleterObject.commands.keys():
-105self.process_command(
-106command=command,
-107arguments=arguments
-108)
-109
-110elifcommand.strip()=="":
-111pass
-112
-113# Fallback to unknown command
-114else:
-115print("Unknown command. Type \"help\" for help.")
-116
-117exceptKeyboardInterruptase:
-118print()
-119
-120exceptEOFErrorase:
-121print()
-122running=False
-123
-124exceptExceptionase:
-125ifself.config.debug:
-126traceback.print_exc()
-127print("[!] Error: %s"%str(e))
-128
-129defprocess_command(self,command,arguments=[]):
-130# Skip
-131ifcommand.strip()=="":
-132pass
-133
-134# Display help
-135elifcommand=="help":
-136self.command_help(arguments,command)
-137
-138# Cat the contents of a file
-139elifcommand=="bat":
-140self.command_bat(arguments,command)
-141
-142# Cat the contents of a file
-143elifcommand=="cat":
-144self.command_cat(arguments,command)
-145
-146# Closes the current SMB session
-147elifcommand=="close":
-148self.command_close(arguments,command)
-149
-150# Change directory in the current share
-151elifcommand=="cd":
-152self.command_cd(arguments,command)
-153
-154# debug
-155elifcommand=="debug":
-156self.command_debug(arguments,command)
-157
-158# Get a file
-159elifcommand=="get":
-160self.command_get(arguments,command)
-161
-162# SMB server info
-163elifcommand=="info":
-164self.command_info(arguments,command)
-165
-166# List directory contents in a share
-167elifcommandin["ls","dir"]:
-168self.command_ls(arguments,command)
-169
-170# Creates a new remote directory
-171elifcommand=="mkdir":
-172self.command_mkdir(arguments,command)
-173
-174# Put a file
-175elifcommand=="put":
-176self.command_put(arguments,command)
-177
-178# Shows the content of a local file
-179elifcommand=="lcat":
-180self.command_lcat(arguments,command)
-181
-182# Changes the current local directory
-183elifcommand=="lcd":
-184self.command_lcd(arguments,command)
-185
-186# Creates a copy of a local file
-187elifcommand=="lcp":
-188self.command_lcp(arguments,command)
-189
-190# Pretty prints the content of a local file
-191elifcommand=="lbat":
-192self.command_lbat(arguments,command)
-193
-194# Lists the contents of the current local directory
-195elifcommand=="lls":
-196self.command_lls(arguments,command)
-197
-198# Creates a new local directory
-199elifcommand=="lmkdir":
-200self.command_lmkdir(arguments,command)
-201
-202# Shows the current local directory
-203elifcommand=="lpwd":
-204self.command_lpwd(arguments,command)
-205
-206# Renames a local file
-207elifcommand=="lrename":
-208self.command_lrename(arguments,command)
-209
-210# Removes a local file
-211elifcommand=="lrm":
-212self.command_lrm(arguments,command)
-213
-214# Removes a local directory
-215elifcommand=="lrmdir":
-216self.command_lrmdir(arguments,command)
-217
-218# Shows the current local directory
-219elifcommand=="ltree":
-220self.command_ltree(arguments,command)
-221
-222# Modules
-223elifcommand=="module":
-224self.command_module(arguments,command)
-225
-226# Creates a mount point of the remote share on the local machine
-227elifcommand=="mount":
-228self.command_mount(arguments,command)
-229
-230# Reconnects the current SMB session
-231elifcommandin["connect","reconnect"]:
-232self.command_reconnect(arguments,command)
-233
-234# Reset the TTY output
-235elifcommand=="reset":
-236self.command_reset(arguments,command)
-237
-238# Removes a remote file
-239elifcommand=="rm":
-240self.command_rm(arguments,command)
-241
-242# Removes a remote directory
-243elifcommand=="rmdir":
-244self.command_rmdir(arguments,command)
-245
-246# List shares
-247elifcommand=="sizeof":
-248self.command_sizeof(arguments,command)
-249
-250# List shares
-251elifcommand=="shares":
-252self.command_shares(arguments,command)
-253
-254# Displays a tree view of the CWD
-255elifcommand=="tree":
-256self.command_tree(arguments,command)
-257
-258# Use a share
-259elifcommand=="use":
-260self.command_use(arguments,command)
-261
-262# Commands ================================================================
-263
-264defcommand_debug(self,arguments,command):
-265try:
-266pass
-267exceptExceptionase:
-268traceback.print_exc()
-269
-270@command_arguments_required
-271@active_smb_connection_needed
-272@smb_share_is_set
-273defcommand_bat(self,arguments,command):
-274# Command arguments required : Yes
-275# Active SMB connection needed : Yes
-276# SMB share needed : Yes
-277
-278path=' '.join(arguments)
-279try:
-280rawcontents=self.smbSession.read_file(path=path)
-281ifrawcontentsisnotNone:
-282encoding=charset_normalizer.detect(rawcontents)["encoding"]
-283ifencodingisnotNone:
-284filecontent=rawcontents.decode(encoding).rstrip()
-285lexer=Syntax.guess_lexer(path=ntpath.basename(path),code=filecontent)
-286# Some trickery for the files undetected by the lexer
-287iflexer=="default":
-288if'<?xml'infilecontent:
-289lexer="xml"
-290elif'<html>'infilecontent:
-291lexer="html"
-292syntax=Syntax(code=filecontent,line_numbers=True,lexer=lexer)
-293Console().print(syntax)
-294else:
-295print("[!] Could not detect charset of '%s'."%path)
-296exceptimpacket.smbconnection.SessionErrorase:
-297print("[!] SMB Error: %s"%e)
-298
-299@command_arguments_required
-300@active_smb_connection_needed
-301@smb_share_is_set
-302defcommand_cd(self,arguments,command):
-303# Command arguments required : Yes
-304# Active SMB connection needed : Yes
-305# SMB share needed : Yes
-306
-307path=' '.join(arguments)
-308try:
-309self.smbSession.set_cwd(path=path)
-310exceptimpacket.smbconnection.SessionErrorase:
-311print("[!] SMB Error: %s"%e)
-312
-313@command_arguments_required
-314@active_smb_connection_needed
-315@smb_share_is_set
-316defcommand_cat(self,arguments,command):
-317# Command arguments required : Yes
-318# Active SMB connection needed : Yes
-319# SMB share needed : Yes
-320
-321path=' '.join(arguments)
-322try:
-323rawcontents=self.smbSession.read_file(path=path)
-324ifrawcontentsisnotNone:
-325encoding=charset_normalizer.detect(rawcontents)["encoding"]
-326ifencodingisnotNone:
-327filecontent=rawcontents.decode(encoding).rstrip()
-328print(filecontent)
-329else:
-330print("[!] Could not detect charset of '%s'."%path)
-331exceptimpacket.smbconnection.SessionErrorase:
-332print("[!] SMB Error: %s"%e)
-333
-334defcommand_close(self,arguments,command):
-335# Command arguments required : No
-336# Active SMB connection needed : No
-337# SMB share needed : No
-338
-339self.smbSession.ping_smb_session()
-340ifself.smbSession.connected:
-341self.smbSession.close_smb_session()
-342
-343@command_arguments_required
-344@active_smb_connection_needed
-345@smb_share_is_set
-346defcommand_get(self,arguments,command):
-347# Command arguments required : Yes
-348# Active SMB connection needed : Yes
-349# SMB share needed : Yes
-350
-351# Get files recursively
-352ifarguments[0]=="-r":
-353path=' '.join(arguments[1:]).replace('/',ntpath.sep)
-354try:
-355self.smbSession.get_file_recursively(path=path)
-356exceptimpacket.smbconnection.SessionErrorase:
-357print("[!] SMB Error: %s"%e)
-358# Get a single file
-359else:
-360path=' '.join(arguments).replace('/',ntpath.sep)
-361try:
-362self.smbSession.get_file(path=path)
-363exceptimpacket.smbconnection.SessionErrorase:
-364print("[!] SMB Error: %s"%e)
-365
-366defcommand_help(self,arguments,command):
-367# Command arguments required : No
-368# Active SMB connection needed : No
-369# SMB share needed : No
-370
-371iflen(arguments)!=0:
-372self.commandCompleterObject.print_help(command=arguments[0])
-373else:
-374self.commandCompleterObject.print_help(command=None)
-375
-376@active_smb_connection_needed
-377defcommand_info(self,arguments,command):
-378# Command arguments required : No
-379# Active SMB connection needed : Yes
-380# SMB share needed : No
-381
-382print_server_info=False
-383print_share_info=False
-384iflen(arguments)!=0:
-385print_server_info=(arguments[0].lower()=="server")
-386print_share_info=(arguments[0].lower()=="share")
-387else:
-388print_server_info=True
-389print_share_info=True
-390
-391try:
-392self.smbSession.info(
-393share=print_share_info,
-394server=print_server_info
-395)
-396exceptimpacket.smbconnection.SessionErrorase:
-397print("[!] SMB Error: %s"%e)
-398
-399@command_arguments_required
-400defcommand_lcat(self,arguments,command):
-401# Command arguments required : Yes
-402# Active SMB connection needed : No
-403# SMB share needed : No
-404
-405path=' '.join(arguments)
-406try:
-407ifos.path.exists(path=path):
-408f=open(path,'rb')
-409rawcontents=f.read()
-410ifrawcontentsisnotNone:
-411encoding=charset_normalizer.detect(rawcontents)["encoding"]
-412ifencodingisnotNone:
-413filecontent=rawcontents.decode(encoding).rstrip()
-414print(filecontent)
-415else:
-416print("[!] Could not detect charset of '%s'."%path)
-417else:
-418print("[!] Local file '%s' does not exist."%path)
-419exceptimpacket.smbconnection.SessionErrorase:
-420print("[!] SMB Error: %s"%e)
-421
-422@command_arguments_required
-423defcommand_lcd(self,arguments,command):
-424# Command arguments required : Yes
-425# Active SMB connection needed : No
-426# SMB share needed : No
-427
-428path=' '.join(arguments)
-429ifos.path.exists(path=path):
-430ifos.path.isdir(s=path):
-431os.chdir(path=path)
-432else:
-433print("[!] Path '%s' is not a directory."%path)
-434else:
-435print("[!] Directory '%s' does not exists."%path)
-436
-437@command_arguments_required
-438defcommand_lcp(self,arguments,command):
-439# Command arguments required : Yes
-440# Active SMB connection needed : No
-441# SMB share needed : No
-442
-443iflen(arguments)==2:
-444src_path=arguments[0]
-445dst_path=arguments[1]
-446ifos.path.exists(path=src_path):
-447try:
-448shutil.copyfile(src=src_path,dst=dst_path)
-449exceptshutil.SameFileErroraserr:
-450print("[!] Error: %s"%err)
-451else:
-452print("[!] File '%s' does not exists."%src_path)
-453else:
-454self.commandCompleterObject.print_help(command=command)
-455
-456@command_arguments_required
-457defcommand_lbat(self,arguments,command):
-458# Command arguments required : Yes
-459# Active SMB connection needed : No
-460# SMB share needed : No
-461
-462path=' '.join(arguments)
-463try:
-464ifos.path.exists(path=path):
-465f=open(path,'rb')
-466rawcontents=f.read()
-467ifrawcontentsisnotNone:
-468encoding=charset_normalizer.detect(rawcontents)["encoding"]
-469ifencodingisnotNone:
-470filecontent=rawcontents.decode(encoding).rstrip()
-471lexer=Syntax.guess_lexer(path=ntpath.basename(path),code=filecontent)
-472# Some trickery for the files undetected by the lexer
-473iflexer=="default":
-474if'<?xml'infilecontent:
-475lexer="xml"
-476elif'<html>'infilecontent:
-477lexer="html"
-478syntax=Syntax(code=filecontent,line_numbers=True,lexer=lexer)
-479Console().print(syntax)
-480else:
-481print("[!] Could not detect charset of '%s'."%path)
-482else:
-483print("[!] Local file '%s' does not exist."%path)
-484exceptimpacket.smbconnection.SessionErrorase:
-485print("[!] SMB Error: %s"%e)
-486
-487defcommand_lls(self,arguments,command):
-488# Command arguments required : No
-489# Active SMB connection needed : No
-490# SMB share needed : No
-491
-492iflen(arguments)==0:
-493path='.'
-494else:
-495path=' '.join(arguments)
-496
-497# lls <directory>
-498ifos.path.isdir(path):
-499directory_contents=os.listdir(path=path)
-500forentrynameinsorted(directory_contents):
-501path_to_file=path+os.path.sep+entryname
-502rights_str=unix_permissions(path_to_file)
-503size_str=b_filesize(os.path.getsize(filename=path_to_file))
-504date_str=datetime.datetime.fromtimestamp(os.path.getmtime(filename=path_to_file)).strftime("%Y-%m-%d %H:%M")
-505
-506ifos.path.isdir(s=entryname):
-507ifself.config.no_colors:
-508print("%s%10s%s%s%s"%(rights_str,size_str,date_str,entryname,os.path.sep))
-509else:
-510print("%s%10s%s\x1b[1;96m%s\x1b[0m%s"%(rights_str,size_str,date_str,entryname,os.path.sep))
-511else:
-512ifself.config.no_colors:
-513print("%s%10s%s%s"%(rights_str,size_str,date_str,entryname))
-514else:
-515print("%s%10s%s\x1b[1m%s\x1b[0m"%(rights_str,size_str,date_str,entryname))
-516# lls <file>
-517elifos.path.isfile(path):
-518rights_str=unix_permissions(path)
-519size_str=b_filesize(os.path.getsize(filename=path))
-520date_str=datetime.datetime.fromtimestamp(os.path.getmtime(filename=path)).strftime("%Y-%m-%d %H:%M")
-521ifself.config.no_colors:
-522print("%s%10s%s%s"%(rights_str,size_str,date_str,os.path.basename(path)))
-523else:
-524print("%s%10s%s\x1b[1m%s\x1b[0m"%(rights_str,size_str,date_str,os.path.basename(path)))
-525else:
-526print("[!] No such file or directory.")
-527
-528@command_arguments_required
-529defcommand_lmkdir(self,arguments,command):
-530# Command arguments required : Yes
-531# Active SMB connection needed : No
-532# SMB share needed : No
-533
-534path=' '.join(arguments)
-535
-536# Split each dir
-537ifos.path.sepinpath:
-538path=path.strip(os.path.sep).split(os.path.sep)
-539else:
-540path=[path]
-541
-542# Create each dir in the path
-543fordepthinrange(1,len(path)+1):
-544tmp_path=os.path.sep.join(path[:depth])
-545ifnotos.path.exists(tmp_path):
-546os.mkdir(path=tmp_path)
-547
-548defcommand_lpwd(self,arguments,command):
-549# Command arguments required : No
-550# Active SMB connection needed : No
-551# SMB share needed : No
-552
-553print(os.getcwd())
-554
-555@command_arguments_required
-556defcommand_lrename(self,arguments,command):
-557# Command arguments required : Yes
-558# Active SMB connection needed : No
-559# SMB share needed : No
-560
-561iflen(arguments)==2:
-562os.rename(src=arguments[0],dst=arguments[1])
-563else:
-564self.commandCompleterObject.print_help(command=command)
-565
-566@command_arguments_required
-567defcommand_lrm(self,arguments,command):
-568# Command arguments required : Yes
-569# Active SMB connection needed : No
-570# SMB share needed : No
-571
-572path=' '.join(arguments)
-573ifos.path.exists(path):
-574ifnotos.path.isdir(s=path):
-575try:
-576os.remove(path=path)
-577exceptExceptionase:
-578print("[!] Error removing file '%s' : %s"%path)
-579else:
-580print("[!] Cannot delete '%s'. It is a directory, use 'lrmdir <directory>' instead."%path)
-581else:
-582print("[!] Path '%s' does not exist."%path)
-583
-584@command_arguments_required
-585defcommand_lrmdir(self,arguments,command):
-586# Command arguments required : Yes
-587# Active SMB connection needed : No
-588# SMB share needed : No
-589
-590path=' '.join(arguments)
-591ifos.path.exists(path):
-592ifos.path.isdir(s=path):
-593try:
-594shutil.rmtree(path=path)
-595exceptExceptionase:
-596print("[!] Error removing directory '%s' : %s"%path)
-597else:
-598print("[!] Cannot delete '%s'. It is a file, use 'lrm <file>' instead."%path)
-599else:
-600print("[!] Path '%s' does not exist."%path)
-601
-602defcommand_ltree(self,arguments,command):
-603# Command arguments required : No
-604# Active SMB connection needed : No
-605# SMB share needed : No
-606
-607iflen(arguments)==0:
-608local_tree(path='.',config=self.config)
-609else:
-610local_tree(path=' '.join(arguments),config=self.config)
-611
-612@active_smb_connection_needed
-613@smb_share_is_set
-614defcommand_ls(self,arguments,command):
-615# Command arguments required : No
-616# Active SMB connection needed : Yes
-617# SMB share needed : Yes
-618
-619# Read the files
-620directory_contents=self.smbSession.list_contents(path=' '.join(arguments))
-621
-622forlongnameinsorted(directory_contents.keys(),key=lambdax:x.lower()):
-623windows_ls_entry(directory_contents[longname],self.config)
-624
-625@command_arguments_required
-626@active_smb_connection_needed
-627@smb_share_is_set
-628defcommand_mkdir(self,arguments,command):
-629# Command arguments required : Yes
-630# Active SMB connection needed : Yes
-631# SMB share needed : Yes
-632
-633path=' '.join(arguments)
-634self.smbSession.mkdir(path=path)
-635
-636@command_arguments_required
-637@active_smb_connection_needed
-638@smb_share_is_set
-639defcommand_module(self,arguments,command):
-640module_name=arguments[0]
-641
-642ifmodule_nameinself.modules.keys():
-643module=self.modules[module_name](self.smbSession,self.config)
-644module.run(' '.join(arguments[1:]))
-645else:
-646print("[!] Module '%s' does not exist."%module_name)
-647
-648@command_arguments_required
-649@active_smb_connection_needed
-650@smb_share_is_set
-651defcommand_mount(self,arguments,command):
-652# Command arguments required : Yes
-653# Active SMB connection needed : Yes
-654# SMB share needed : Yes
-655
-656iflen(arguments)==2:
-657remote_path=arguments[0]
-658ifnotremote_path.startswith(ntpath.sep):
-659remote_path=self.smbSession.smb_cwd+ntpath.sep+remote_path
-660
-661local_mount_point=arguments[1]
-662
-663ifself.config.debug:
-664print("[debug] Trying to mount remote '%s' onto local '%s'"%(remote_path,local_mount_point))
-665
-666try:
-667self.smbSession.mount(local_mount_point,remote_path)
-668except(impacket.smbconnection.SessionError,impacket.smb3.SessionError)ase:
-669self.smbSession.umount(local_mount_point)
-670else:
-671self.commandCompleterObject.print_help(command=command)
-672
-673@command_arguments_required
-674@active_smb_connection_needed
-675@smb_share_is_set
-676defcommand_put(self,arguments,command):
-677# Command arguments required : Yes
-678# Active SMB connection needed : Yes
-679# SMB share needed : Yes
-680
-681# Put files recursively
-682ifarguments[0]=="-r":
-683localpath=' '.join(arguments[1:])
-684try:
-685self.smbSession.put_file_recursively(localpath=localpath)
-686exceptimpacket.smbconnection.SessionErrorase:
-687print("[!] SMB Error: %s"%e)
-688
-689# Put a single file
-690else:
-691localpath=' '.join(arguments)
-692try:
-693self.smbSession.put_file(localpath=localpath)
-694exceptimpacket.smbconnection.SessionErrorase:
-695print("[!] SMB Error: %s"%e)
-696
-697defcommand_reconnect(self,arguments,command):
-698# Command arguments required : No
-699# Active SMB connection needed : No
-700# SMB share needed : No
-701
-702self.smbSession.ping_smb_session()
-703ifself.smbSession.connected:
-704self.smbSession.close_smb_session()
-705self.smbSession.init_smb_session()
-706else:
-707self.smbSession.init_smb_session()
-708
-709defcommand_reset(self,arguments,command):
-710# Command arguments required : No
-711# Active SMB connection needed : No
-712# SMB share needed : No
-713sys.stdout.write('\x1b[?25h')# Sets the cursor to on
-714sys.stdout.write('\x1b[v')
-715sys.stdout.write('\x1b[o')# Reset
-716sys.stdout.flush()
-717
-718@command_arguments_required
-719@active_smb_connection_needed
-720@smb_share_is_set
-721defcommand_rm(self,arguments,command):
-722# Command arguments required : Yes
-723# Active SMB connection needed : Yes
-724# SMB share needed : Yes
-725
-726path=' '.join(arguments)
-727if'*'inpath:
-728self.smbSession.rm(path=path)
-729elifself.smbSession.path_exists(path):
-730ifself.smbSession.path_isfile(path):
-731try:
-732self.smbSession.rm(path=path)
-733exceptExceptionase:
-734print("[!] Error removing file '%s' : %s"%path)
-735else:
-736print("[!] Cannot delete '%s': This is a directory, use 'rmdir <directory>' instead."%path)
-737else:
-738print("[!] Remote file '%s' does not exist."%path)
-739
-740@command_arguments_required
-741@active_smb_connection_needed
-742@smb_share_is_set
-743defcommand_rmdir(self,arguments,command):
-744# Command arguments required : Yes
-745# Active SMB connection needed : Yes
-746# SMB share needed : Yes
-747
-748path=' '.join(arguments)
-749ifself.smbSession.path_exists(path):
-750ifself.smbSession.path_isdir(path):
-751try:
-752self.smbSession.rmdir(path=path)
-753exceptExceptionase:
-754print("[!] Error removing directory '%s' : %s"%path)
-755else:
-756print("[!] Cannot delete '%s': This is a file, use 'rm <file>' instead."%path)
-757else:
-758print("[!] Remote directory '%s' does not exist."%path)
-759
-760@active_smb_connection_needed
-761@smb_share_is_set
-762defcommand_sizeof(self,arguments,command):
-763# Command arguments required : Yes
-764# Active SMB connection needed : Yes
-765# SMB share needed : Yes
-766
-767classRecursiveSizeOfPrint(object):
-768def__init__(self,entry,smbSession,config):
-769self.entry=entry
-770self.config=config
-771self.smbSession=smbSession
-772self.size=0
-773
-774defupdate(self,entry,fullpath,depth):
-775self.size+=entry.get_filesize()
-776self.print(end='\r')
-777
-778defprint(self,end='\n'):
-779#
-780ifself.entry.is_directory():
-781ifself.config.no_colors:
-782path="%s\\"%self.entry.get_longname()
-783else:
-784path="\x1b[1;96m%s\x1b[0m\\"%self.entry.get_longname()
-785#
-786else:
-787ifself.config.no_colors:
-788path="%s"%self.entry.get_longname()
-789else:
-790path="\x1b[1m%s\x1b[0m"%self.entry.get_longname()
-791print("%10s%s"%(b_filesize(self.size),path),end=end)
-792
-793entries=[]
-794iflen(arguments)==0:
-795entries=self.smbSession.list_contents()
-796entries=[entryforname,entryinentries.items()ifnamenotin['.','..']]
-797else:
-798entry=self.smbSession.get_entry(path=' '.join(arguments))
-799entries=[]
-800ifentryisnotNone:
-801entries=[entry]
-802else:
-803print("[!] Path '%s' does not exist."%' '.join(arguments))
-804
-805total=0
-806forentryinentries:
-807rsop=RecursiveSizeOfPrint(entry=entry,smbSession=self.smbSession,config=self.config)
-808# Directory
-809ifentry.is_directory():
-810self.smbSession.find(
-811paths=[entry.get_longname()],
-812callback=rsop.update
-813)
-814# File
-815else:
-816rsop.update(entry=entry,fullpath=entry.get_longname(),depth=0)
-817# Close the print
-818rsop.print()
-819total+=rsop.size
-820
-821iflen(entries)>1:
-822print("──────────────────────")
-823print(" total %s"%b_filesize(total))
-824
-825@active_smb_connection_needed
-826defcommand_shares(self,arguments,command):
-827# Command arguments required : No
-828# Active SMB connection needed : Yes
-829# SMB share needed : No
-830
-831do_check_rights=False
-832iflen(arguments)!=0:
-833ifarguments[0]=="rights":
-834do_check_rights=True
-835
-836shares=self.smbSession.list_shares()
-837iflen(shares.keys())!=0:
-838table=Table(title=None)
-839table.add_column("Share")
-840table.add_column("Visibility")
-841table.add_column("Type")
-842table.add_column("Description",justify="left")
-843ifdo_check_rights:
-844table.add_column("Rights")
-845
-846forsharenameinsorted(shares.keys()):
-847types=', '.join([s.replace("STYPE_","")forsinshares[sharename]["type"]])
-848
-849is_hidden=bool(sharename.endswith('$'))
-850ifis_hidden:
-851str_hidden="[bold bright_blue]Hidden[/bold bright_blue]"
-852str_sharename="[bold bright_blue]"+shares[sharename]["name"]+"[/bold bright_blue]"
-853str_types="[bold bright_blue]"+types+"[/bold bright_blue]"
-854str_comment="[bold bright_blue]"+shares[sharename]["comment"]+"[/bold bright_blue]"
-855else:
-856str_hidden="[bold bright_yellow]Visible[/bold bright_yellow]"
-857str_sharename="[bold bright_yellow]"+shares[sharename]["name"]+"[/bold bright_yellow]"
-858str_types="[bold bright_yellow]"+types+"[/bold bright_yellow]"
-859str_comment="[bold bright_yellow]"+shares[sharename]["comment"]+"[/bold bright_yellow]"
-860
-861ifdo_check_rights:
-862access_rights=self.smbSession.test_rights(sharename=shares[sharename]["name"])
-863str_access_rights="[bold yellow]NO ACCESS[/bold yellow]"
-864ifaccess_rights["readable"]andaccess_rights["writable"]:
-865str_access_rights="[bold green]READ[/bold green], [bold red]WRITE[/bold red]"
-866elifaccess_rights["readable"]:
-867str_access_rights="[bold green]READ[/bold green]"
-868elifaccess_rights["writable"]:
-869# Without READ?? This should not happen IMHO
-870str_access_rights="[bold red]WRITE[/bold red]"
-871else:
-872str_access_rights="[bold yellow]NO ACCESS[/bold yellow]"
-873
-874ifdo_check_rights:
-875table.add_row(str_sharename,str_hidden,str_types,str_comment,str_access_rights)
-876else:
-877table.add_row(str_sharename,str_hidden,str_types,str_comment)
-878
-879Console().print(table)
-880else:
-881print("[!] No share served on '%s'"%self.smbSession.address)
-882
-883@active_smb_connection_needed
-884@smb_share_is_set
-885defcommand_tree(self,arguments,command):
-886# Command arguments required : No
-887# Active SMB connection needed : Yes
-888# SMB share needed : Yes
-889
-890iflen(arguments)==0:
-891self.smbSession.tree(path='.')
-892else:
-893self.smbSession.tree(path=' '.join(arguments))
-894
-895@command_arguments_required
-896@active_smb_connection_needed
-897@smb_share_is_set
-898defcommand_umount(self,arguments,command):
-899# Command arguments required : Yes
-900# Active SMB connection needed : Yes
-901# SMB share needed : Yes
-902
-903local_mount_point=arguments[0]
-904
-905ifself.config.debug:
-906print("[debug] Trying to unmount local mount point '%s'"%(local_mount_point))
-907
-908self.smbSession.umount(local_mount_point)
-909
-910@command_arguments_required
-911@active_smb_connection_needed
-912defcommand_use(self,arguments,command):
-913# Command arguments required : Yes
-914# Active SMB connection needed : Yes
-915# SMB share needed : No
-916
-917sharename=' '.join(arguments)
-918# Reload the list of shares
-919shares=self.smbSession.list_shares()
-920shares=[s.lower()forsinshares.keys()]
-921ifsharename.lower()inshares:
-922self.smbSession.set_share(sharename)
-923else:
-924print("[!] No share named '%s' on '%s'"%(sharename,self.smbSession.address))
-925
-926# Private functions =======================================================
-927
-928def__load_modules(self):
-929
-930self.modules.clear()
-931
-932modules_dir=os.path.normpath(os.path.dirname(__file__)+os.path.sep+".."+os.path.sep+"modules")
-933ifself.config.debug:
-934print("[debug] Loading modules from %s ..."%modules_dir)
-935sys.path.extend([modules_dir])
-936
-937forfileinos.listdir(modules_dir):
-938filepath=os.path.normpath(modules_dir+os.path.sep+file)
-939iffile.endswith('.py'):
-940ifos.path.isfile(filepath)andfilenotin["__init__.py"]:
-941try:
-942module_file=import_module('smbclientng.modules.%s'%(file[:-3]))
-943module=module_file.__getattribute__(file[:-3])
-944self.modules[module.name.lower()]=module
-945exceptAttributeErrorase:
-946pass
-947
-948ifself.config.debug:
-949iflen(self.modules.keys())==0:
-950print("[debug] Loaded 0 modules.")
-951eliflen(self.modules.keys())==1:
-952print("[debug] Loaded 1 module:")
-953else:
-954print("[debug] Loaded %d modules:"%len(self.modules.keys()))
-955formodulenameinsorted(self.modules.keys()):
-956print("[debug] %s : \"%s\" (%s)"%(self.modules[modulename].name,self.modules[modulename].description,self.modules[modulename]))
-957
-958ifself.commandCompleterObjectisnotNone:
-959self.commandCompleterObject.commands["module"]["subcommands"]=list(self.modules.keys())
-960
-961def__prompt(self):
-962"""
-963 Prints the command prompt for the interactive shell.
-964
-965 This method constructs and returns the command prompt string based on the current state of the SMB session.
-966 The prompt indicates the connection status with a visual symbol and displays the current working directory
-967 or the SMB share path. The prompt appearance changes based on whether colors are enabled in the configuration.
-968
-969 Returns:
-970 str: The formatted command prompt string.
-971 """
-972
-973self.smbSession.ping_smb_session()
-974ifself.smbSession.connected:
-975ifself.config.no_colors:
-976connected_dot="[v]"
-977else:
-978connected_dot="\x1b[1;92m⏺\x1b[0m"
-979else:
-980ifself.config.no_colors:
-981connected_dot="[x]"
-982else:
-983connected_dot="\x1b[1;91m⏺\x1b[0m"
-984
-985ifself.smbSession.smb_shareisNone:
-986ifself.config.no_colors:
-987str_prompt="%s[\\\\%s\\]> "%(connected_dot,self.smbSession.address)
-988else:
-989str_prompt="%s[\x1b[1;94m\\\\%s\\\x1b[0m]> "%(connected_dot,self.smbSession.address)
-990else:
-991str_path="\\\\%s\\%s\\%s"%(self.smbSession.address,self.smbSession.smb_share,self.smbSession.smb_cwd.lstrip(ntpath.sep))
-992ifself.config.no_colors:
-993str_prompt="%s[%s]> "%(connected_dot,str_path)
-994else:
-995str_prompt="%s[\x1b[1;94m%s\x1b[0m]> "%(connected_dot,str_path)
-996
-997returnstr_prompt
+
62classInteractiveShell(object):
+ 63"""
+ 64 Class InteractiveShell is designed to manage the interactive command line interface for smbclient-ng.
+ 65
+ 66 This class handles user input, executes commands, and manages the state of the SMB session. It provides
+ 67 a command line interface for users to interact with SMB shares, execute commands like directory listing,
+ 68 file transfer, and more.
+ 69
+ 70 Attributes:
+ 71 smbSession (SMBConnection): The active SMB connection session.
+ 72 debug (bool): Flag to enable or disable debug mode.
+ 73 smb_share (str): The current SMB share in use.
+ 74 smb_path (str): The current path within the SMB share.
+ 75 commandCompleterObject (CommandCompleter): Object to handle command completion and help generation.
+ 76
+ 77 Methods:
+ 78 __init__(self, smbSession, debug=False): Initializes the InteractiveShell with the given SMB session and debug mode.
+ 79 run(self): Starts the command line interface loop, processing user input until exit.
+ 80 """
+ 81
+ 82running=True
+ 83modules={}
+ 84
+ 85def__init__(self,sessionsManager,config,logger):
+ 86# Objects
+ 87self.sessionsManager=sessionsManager
+ 88self.config=config
+ 89self.logger=logger
+ 90# Internals
+ 91self.commandCompleterObject=CommandCompleter(
+ 92smbSession=self.sessionsManager.current_session,
+ 93config=self.config,
+ 94logger=self.logger,
+ 95)
+ 96readline.set_completer(self.commandCompleterObject.complete)
+ 97readline.parse_and_bind("tab: complete")
+ 98readline.set_completer_delims("\n")
+ 99# Additional modules
+ 100self.__load_modules()
+ 101
+ 102defrun(self):
+ 103# Read commands from script file first
+ 104ifself.config.startup_script:
+ 105f=open(self.config.startup_script,'r')
+ 106forlineinf.readlines():
+ 107try:
+ 108self.logger.print("%s%s"%(self.__prompt(),line.strip()))
+ 109readline.add_history(line.strip())
+ 110self.process_line(commandLine=line.strip())
+ 111exceptKeyboardInterruptase:
+ 112self.logger.print()
+ 113
+ 114exceptEOFErrorase:
+ 115self.logger.print()
+ 116running=False
+ 117
+ 118exceptExceptionaserr:
+ 119ifself.config.debug:
+ 120traceback.print_exc()
+ 121self.logger.error(str(err))
+ 122
+ 123# Then interactive console
+ 124ifnotself.config.not_interactive:
+ 125whileself.running:
+ 126try:
+ 127user_input=input(self.__prompt()).strip()
+ 128self.process_line(commandLine=user_input)
+ 129exceptKeyboardInterruptase:
+ 130self.logger.print()
+ 131
+ 132exceptEOFErrorase:
+ 133self.logger.print()
+ 134running=False
+ 135
+ 136exceptExceptionaserr:
+ 137ifself.config.debug:
+ 138traceback.print_exc()
+ 139self.logger.error(str(err))
+ 140
+ 141defprocess_line(self,commandLine):
+ 142# Split and parse the commandLine
+ 143tokens=shlex.split(commandLine)
+ 144iflen(tokens)==0:
+ 145command=""
+ 146arguments=[]
+ 147eliflen(tokens)==1:
+ 148command=tokens[0].lower()
+ 149arguments=[]
+ 150else:
+ 151command=tokens[0].lower()
+ 152arguments=tokens[1:]
+ 153
+ 154# Skip
+ 155ifcommand.strip()=="":
+ 156pass
+ 157# Execute the command
+ 158elifcommandinself.commandCompleterObject.commands.keys():
+ 159
+ 160# Exit the command line
+ 161ifcommandin["exit","quit"]:
+ 162self.running=False
+ 163
+ 164# Display help
+ 165elifcommand=="help":
+ 166self.command_help(arguments,command)
+ 167
+ 168# Cat the contents of a file
+ 169elifcommand=="bat":
+ 170self.command_bat(arguments,command)
+ 171
+ 172# Cat the contents of a file
+ 173elifcommand=="cat":
+ 174self.command_cat(arguments,command)
+ 175
+ 176# Closes the current SMB session
+ 177elifcommand=="close":
+ 178self.command_close(arguments,command)
+ 179
+ 180# Change directory in the current share
+ 181elifcommand=="cd":
+ 182self.command_cd(arguments,command)
+ 183
+ 184# debug
+ 185elifcommand=="debug":
+ 186self.command_debug(arguments,command)
+ 187
+ 188# Get a file
+ 189elifcommand=="get":
+ 190self.command_get(arguments,command)
+ 191
+ 192# SMB server info
+ 193elifcommand=="info":
+ 194self.command_info(arguments,command)
+ 195
+ 196# List directory contents in a share
+ 197elifcommandin["ls","dir"]:
+ 198self.command_ls(arguments,command)
+ 199
+ 200# Creates a new remote directory
+ 201elifcommand=="mkdir":
+ 202self.command_mkdir(arguments,command)
+ 203
+ 204# Put a file
+ 205elifcommand=="put":
+ 206self.command_put(arguments,command)
+ 207
+ 208# Shows the content of a local file
+ 209elifcommand=="lcat":
+ 210self.command_lcat(arguments,command)
+ 211
+ 212# Changes the current local directory
+ 213elifcommand=="lcd":
+ 214self.command_lcd(arguments,command)
+ 215
+ 216# Creates a copy of a local file
+ 217elifcommand=="lcp":
+ 218self.command_lcp(arguments,command)
+ 219
+ 220# Pretty prints the content of a local file
+ 221elifcommand=="lbat":
+ 222self.command_lbat(arguments,command)
+ 223
+ 224# Lists the contents of the current local directory
+ 225elifcommand=="lls":
+ 226self.command_lls(arguments,command)
+ 227
+ 228# Creates a new local directory
+ 229elifcommand=="lmkdir":
+ 230self.command_lmkdir(arguments,command)
+ 231
+ 232# Shows the current local directory
+ 233elifcommand=="lpwd":
+ 234self.command_lpwd(arguments,command)
+ 235
+ 236# Renames a local file
+ 237elifcommand=="lrename":
+ 238self.command_lrename(arguments,command)
+ 239
+ 240# Removes a local file
+ 241elifcommand=="lrm":
+ 242self.command_lrm(arguments,command)
+ 243
+ 244# Removes a local directory
+ 245elifcommand=="lrmdir":
+ 246self.command_lrmdir(arguments,command)
+ 247
+ 248# Shows the current local directory
+ 249elifcommand=="ltree":
+ 250self.command_ltree(arguments,command)
+ 251
+ 252# Modules
+ 253elifcommand=="module":
+ 254self.command_module(arguments,command)
+ 255
+ 256# Creates a mount point of the remote share on the local machine
+ 257elifcommand=="mount":
+ 258self.command_mount(arguments,command)
+ 259
+ 260# Reconnects the current SMB session
+ 261elifcommandin["connect","reconnect"]:
+ 262self.command_reconnect(arguments,command)
+ 263
+ 264# Reset the TTY output
+ 265elifcommand=="reset":
+ 266self.command_reset(arguments,command)
+ 267
+ 268# Removes a remote file
+ 269elifcommand=="rm":
+ 270self.command_rm(arguments,command)
+ 271
+ 272# Removes a remote directory
+ 273elifcommand=="rmdir":
+ 274self.command_rmdir(arguments,command)
+ 275
+ 276# Sessions management
+ 277elifcommand=="sessions":
+ 278self.sessionsManager.process_command_line(arguments)
+ 279
+ 280# List shares
+ 281elifcommand=="sizeof":
+ 282self.command_sizeof(arguments,command)
+ 283
+ 284# List shares
+ 285elifcommand=="shares":
+ 286self.command_shares(arguments,command)
+ 287
+ 288# Displays a tree view of the CWD
+ 289elifcommand=="tree":
+ 290self.command_tree(arguments,command)
+ 291
+ 292# Use a share
+ 293elifcommand=="use":
+ 294self.command_use(arguments,command)
+ 295
+ 296# Fallback to unknown command
+ 297else:
+ 298self.logger.print("Unknown command. Type \"help\" for help.")
+ 299
+ 300# Commands ================================================================
+ 301
+ 302defcommand_debug(self,arguments,command):
+ 303try:
+ 304self.logger.print("[debug] command = '%s'"%command)
+ 305self.logger.print("[debug] arguments = %s"%arguments)
+ 306exceptExceptionase:
+ 307traceback.print_exc()
+ 308
+ 309@command_arguments_required
+ 310@active_smb_connection_needed
+ 311@smb_share_is_set
+ 312defcommand_bat(self,arguments,command):
+ 313# Command arguments required : Yes
+ 314# Active SMB connection needed : Yes
+ 315# SMB share needed : Yes
+ 316
+ 317# Parse wildcards
+ 318files_and_directories=resolve_local_files(arguments)
+ 319
+ 320forpath_to_fileinfiles_and_directories:
+ 321ifself.sessionsManager.current_session.path_isfile(pathFromRoot=path_to_file):
+ 322# Read the file
+ 323try:
+ 324rawcontents=self.sessionsManager.current_session.read_file(path=path_to_file)
+ 325ifrawcontentsisnotNone:
+ 326encoding=charset_normalizer.detect(rawcontents)["encoding"]
+ 327ifencodingisnotNone:
+ 328filecontent=rawcontents.decode(encoding).rstrip()
+ 329lexer=Syntax.guess_lexer(path=ntpath.basename(path_to_file),code=filecontent)
+ 330# Some trickery for the files undetected by the lexer
+ 331iflexer=="default":
+ 332if'<?xml'infilecontent:
+ 333lexer="xml"
+ 334elif'<html>'infilecontent:
+ 335lexer="html"
+ 336syntax=Syntax(code=filecontent,line_numbers=True,lexer=lexer)
+ 337iflen(files_and_directories)>1:
+ 338self.logger.print("\x1b[1;93m[>] %s\x1b[0m"%(path_to_file+' ').ljust(80,'='))
+ 339Console().print(syntax)
+ 340else:
+ 341self.logger.error("[!] Could not detect charset of '%s'."%path_to_file)
+ 342exceptimpacket.smbconnection.SessionErrorase:
+ 343self.logger.error("[!] SMB Error: %s"%e)
+ 344
+ 345@command_arguments_required
+ 346@active_smb_connection_needed
+ 347@smb_share_is_set
+ 348defcommand_cd(self,arguments,command):
+ 349# Command arguments required : Yes
+ 350# Active SMB connection needed : Yes
+ 351# SMB share needed : Yes
+ 352
+ 353try:
+ 354self.sessionsManager.current_session.set_cwd(path=arguments[0])
+ 355exceptimpacket.smbconnection.SessionErrorase:
+ 356self.logger.error("[!] SMB Error: %s"%e)
+ 357
+ 358@command_arguments_required
+ 359@active_smb_connection_needed
+ 360@smb_share_is_set
+ 361defcommand_cat(self,arguments,command):
+ 362# Command arguments required : Yes
+ 363# Active SMB connection needed : Yes
+ 364# SMB share needed : Yes
+ 365
+ 366# Parse wildcards
+ 367files_and_directories=resolve_local_files(arguments)
+ 368
+ 369forpath_to_fileinfiles_and_directories:
+ 370ifself.sessionsManager.current_session.path_isfile(pathFromRoot=path_to_file):
+ 371# Read the file
+ 372try:
+ 373rawcontents=self.sessionsManager.current_session.read_file(path=path_to_file)
+ 374ifrawcontentsisnotNone:
+ 375encoding=charset_normalizer.detect(rawcontents)["encoding"]
+ 376ifencodingisnotNone:
+ 377filecontent=rawcontents.decode(encoding).rstrip()
+ 378iflen(files_and_directories)>1:
+ 379self.logger.print("\x1b[1;93m[>] %s\x1b[0m"%(path_to_file+' ').ljust(80,'='))
+ 380self.logger.print(filecontent)
+ 381else:
+ 382self.logger.error("[!] Could not detect charset of '%s'."%path_to_file)
+ 383exceptimpacket.smbconnection.SessionErrorase:
+ 384self.logger.error("[!] SMB Error: %s"%e)
+ 385
+ 386defcommand_close(self,arguments,command):
+ 387# Command arguments required : No
+ 388# Active SMB connection needed : No
+ 389# SMB share needed : No
+ 390
+ 391self.sessionsManager.current_session.ping_smb_session()
+ 392ifself.sessionsManager.current_session.connected:
+ 393self.sessionsManager.current_session.close_smb_session()
+ 394
+ 395@command_arguments_required
+ 396@active_smb_connection_needed
+ 397@smb_share_is_set
+ 398defcommand_get(self,arguments,command):
+ 399# Command arguments required : Yes
+ 400# Active SMB connection needed : Yes
+ 401# SMB share needed : Yes
+ 402
+ 403is_recursive=False
+ 404while'-r'inarguments:
+ 405is_recursive=True
+ 406arguments.remove('-r')
+ 407
+ 408# Parse wildcards
+ 409files_and_directories=resolve_remote_files(self.sessionsManager.current_session,arguments)
+ 410
+ 411#
+ 412forremotepathinfiles_and_directories:
+ 413try:
+ 414ifis_recursiveandself.sessionsManager.current_session.path_isdir(remotepath):
+ 415# Get files recursively
+ 416self.sessionsManager.current_session.get_file_recursively(path=remotepath)
+ 417else:
+ 418# Get this single file
+ 419self.sessionsManager.current_session.get_file(path=remotepath)
+ 420exceptimpacket.smbconnection.SessionErrorase:
+ 421self.logger.error("[!] SMB Error: %s"%e)
+ 422
+ 423defcommand_help(self,arguments,command):
+ 424# Command arguments required : No
+ 425# Active SMB connection needed : No
+ 426# SMB share needed : No
+ 427
+ 428iflen(arguments)!=0:
+ 429self.commandCompleterObject.print_help(command=arguments[0])
+ 430else:
+ 431self.commandCompleterObject.print_help(command=None)
+ 432
+ 433@active_smb_connection_needed
+ 434defcommand_info(self,arguments,command):
+ 435# Command arguments required : No
+ 436# Active SMB connection needed : Yes
+ 437# SMB share needed : No
+ 438
+ 439print_server_info=False
+ 440print_share_info=False
+ 441iflen(arguments)!=0:
+ 442print_server_info=(arguments[0].lower()=="server")
+ 443print_share_info=(arguments[0].lower()=="share")
+ 444else:
+ 445print_server_info=True
+ 446print_share_info=True
+ 447
+ 448try:
+ 449self.sessionsManager.current_session.info(
+ 450share=print_share_info,
+ 451server=print_server_info
+ 452)
+ 453exceptimpacket.smbconnection.SessionErrorase:
+ 454self.logger.error("[!] SMB Error: %s"%e)
+ 455
+ 456@command_arguments_required
+ 457defcommand_lbat(self,arguments,command):
+ 458# Command arguments required : Yes
+ 459# Active SMB connection needed : No
+ 460# SMB share needed : No
+ 461
+ 462# Parse wildcards
+ 463files_and_directories=resolve_remote_files(self.sessionsManager.current_session,arguments)
+ 464
+ 465forpath_to_fileinfiles_and_directories:
+ 466# Read the file
+ 467try:
+ 468ifos.path.exists(path=path_to_file):
+ 469f=open(path_to_file,'rb')
+ 470rawcontents=f.read()
+ 471#
+ 472ifrawcontentsisnotNone:
+ 473encoding=charset_normalizer.detect(rawcontents)["encoding"]
+ 474ifencodingisnotNone:
+ 475filecontent=rawcontents.decode(encoding).rstrip()
+ 476lexer=Syntax.guess_lexer(path=ntpath.basename(path_to_file),code=filecontent)
+ 477# Some trickery for the files undetected by the lexer
+ 478iflexer=="default":
+ 479if'<?xml'infilecontent:
+ 480lexer="xml"
+ 481elif'<html>'infilecontent:
+ 482lexer="html"
+ 483syntax=Syntax(code=filecontent,line_numbers=True,lexer=lexer)
+ 484iflen(files_and_directories)>1:
+ 485self.logger.print("\x1b[1;93m[>] %s\x1b[0m"%(path_to_file+' ').ljust(80,'='))
+ 486Console().print(syntax)
+ 487else:
+ 488self.logger.error("[!] Could not detect charset of '%s'."%path_to_file)
+ 489else:
+ 490self.logger.error("[!] Local file '%s' does not exist."%path_to_file)
+ 491exceptimpacket.smbconnection.SessionErrorase:
+ 492self.logger.error("[!] SMB Error: %s"%e)
+ 493
+ 494@command_arguments_required
+ 495defcommand_lcat(self,arguments,command):
+ 496# Command arguments required : Yes
+ 497# Active SMB connection needed : No
+ 498# SMB share needed : No
+ 499
+ 500# Parse wildcards
+ 501files_and_directories=resolve_remote_files(self.sessionsManager.current_session,arguments)
+ 502
+ 503forpath_to_fileinfiles_and_directories:
+ 504# Read the file
+ 505try:
+ 506ifos.path.exists(path=path_to_file):
+ 507f=open(path_to_file,'rb')
+ 508rawcontents=f.read()
+ 509#
+ 510ifrawcontentsisnotNone:
+ 511encoding=charset_normalizer.detect(rawcontents)["encoding"]
+ 512ifencodingisnotNone:
+ 513filecontent=rawcontents.decode(encoding).rstrip()
+ 514iflen(files_and_directories)>1:
+ 515self.logger.print("\x1b[1;93m[>] %s\x1b[0m"%(path_to_file+' ').ljust(80,'='))
+ 516self.logger.print(filecontent)
+ 517else:
+ 518self.logger.error("[!] Could not detect charset of '%s'."%path_to_file)
+ 519else:
+ 520self.logger.error("[!] Local file '%s' does not exist."%path_to_file)
+ 521exceptimpacket.smbconnection.SessionErrorase:
+ 522self.logger.error("[!] SMB Error: %s"%e)
+ 523
+ 524@command_arguments_required
+ 525defcommand_lcd(self,arguments,command):
+ 526# Command arguments required : Yes
+ 527# Active SMB connection needed : No
+ 528# SMB share needed : No
+ 529
+ 530path=arguments[0]
+ 531
+ 532ifos.path.exists(path=path):
+ 533ifos.path.isdir(s=path):
+ 534os.chdir(path=path)
+ 535else:
+ 536self.logger.error("Path '%s' is not a directory."%path)
+ 537else:
+ 538self.logger.error("Directory '%s' does not exists."%path)
+ 539
+ 540@command_arguments_required
+ 541defcommand_lcp(self,arguments,command):
+ 542# Command arguments required : Yes
+ 543# Active SMB connection needed : No
+ 544# SMB share needed : No
+ 545
+ 546iflen(arguments)==2:
+ 547src_path=arguments[0]
+ 548dst_path=arguments[1]
+ 549ifos.path.exists(path=src_path):
+ 550try:
+ 551shutil.copyfile(src=src_path,dst=dst_path)
+ 552exceptshutil.SameFileErroraserr:
+ 553self.logger.error("[!] Error: %s"%err)
+ 554else:
+ 555self.logger.error("[!] File '%s' does not exists."%src_path)
+ 556else:
+ 557self.commandCompleterObject.print_help(command=command)
+ 558
+ 559defcommand_lls(self,arguments,command):
+ 560# Command arguments required : No
+ 561# Active SMB connection needed : No
+ 562# SMB share needed : No
+ 563
+ 564iflen(arguments)==0:
+ 565arguments=['.']
+ 566else:
+ 567arguments=resolve_local_files(arguments)
+ 568
+ 569forpathinarguments:
+ 570iflen(arguments)>1:
+ 571self.logger.print("%s:"%path)
+ 572# lls <directory>
+ 573ifos.path.isdir(path):
+ 574directory_contents=os.listdir(path=path)
+ 575forentrynameinsorted(directory_contents):
+ 576path_to_file=path+os.path.sep+entryname
+ 577rights_str=unix_permissions(path_to_file)
+ 578size_str=b_filesize(os.path.getsize(filename=path_to_file))
+ 579date_str=datetime.datetime.fromtimestamp(os.path.getmtime(filename=path_to_file)).strftime("%Y-%m-%d %H:%M")
+ 580
+ 581ifos.path.isdir(s=entryname):
+ 582ifself.config.no_colors:
+ 583self.logger.print("%s%10s%s%s%s"%(rights_str,size_str,date_str,entryname,os.path.sep))
+ 584else:
+ 585self.logger.print("%s%10s%s\x1b[1;96m%s\x1b[0m%s"%(rights_str,size_str,date_str,entryname,os.path.sep))
+ 586else:
+ 587ifself.config.no_colors:
+ 588self.logger.print("%s%10s%s%s"%(rights_str,size_str,date_str,entryname))
+ 589else:
+ 590self.logger.print("%s%10s%s\x1b[1m%s\x1b[0m"%(rights_str,size_str,date_str,entryname))
+ 591# lls <file>
+ 592elifos.path.isfile(path):
+ 593rights_str=unix_permissions(path)
+ 594size_str=b_filesize(os.path.getsize(filename=path))
+ 595date_str=datetime.datetime.fromtimestamp(os.path.getmtime(filename=path)).strftime("%Y-%m-%d %H:%M")
+ 596ifself.config.no_colors:
+ 597self.logger.print("%s%10s%s%s"%(rights_str,size_str,date_str,os.path.basename(path)))
+ 598else:
+ 599self.logger.print("%s%10s%s\x1b[1m%s\x1b[0m"%(rights_str,size_str,date_str,os.path.basename(path)))
+ 600
+ 601iflen(arguments)>1:
+ 602self.logger.print()
+ 603
+ 604@command_arguments_required
+ 605defcommand_lmkdir(self,arguments,command):
+ 606# Command arguments required : Yes
+ 607# Active SMB connection needed : No
+ 608# SMB share needed : No
+ 609
+ 610forpathinarguments:
+ 611ifos.path.sepinpath:
+ 612path=path.strip(os.path.sep).split(os.path.sep)
+ 613else:
+ 614path=[path]
+ 615
+ 616# Create each dir in the path
+ 617fordepthinrange(1,len(path)+1):
+ 618tmp_path=os.path.sep.join(path[:depth])
+ 619ifnotos.path.exists(tmp_path):
+ 620os.mkdir(path=tmp_path)
+ 621
+ 622defcommand_lpwd(self,arguments,command):
+ 623# Command arguments required : No
+ 624# Active SMB connection needed : No
+ 625# SMB share needed : No
+ 626
+ 627self.logger.print(os.getcwd())
+ 628
+ 629@command_arguments_required
+ 630defcommand_lrename(self,arguments,command):
+ 631# Command arguments required : Yes
+ 632# Active SMB connection needed : No
+ 633# SMB share needed : No
+ 634
+ 635iflen(arguments)==2:
+ 636os.rename(src=arguments[0],dst=arguments[1])
+ 637else:
+ 638self.commandCompleterObject.print_help(command=command)
+ 639
+ 640@command_arguments_required
+ 641defcommand_lrm(self,arguments,command):
+ 642# Command arguments required : Yes
+ 643# Active SMB connection needed : No
+ 644# SMB share needed : No
+ 645
+ 646path=arguments[0]
+ 647
+ 648ifos.path.exists(path):
+ 649ifnotos.path.isdir(s=path):
+ 650try:
+ 651os.remove(path=path)
+ 652exceptExceptionase:
+ 653self.logger.error("Error removing file '%s' : %s"%path)
+ 654else:
+ 655self.logger.error("Cannot delete '%s'. It is a directory, use 'lrmdir <directory>' instead."%path)
+ 656else:
+ 657self.logger.error("Path '%s' does not exist."%path)
+ 658
+ 659@command_arguments_required
+ 660defcommand_lrmdir(self,arguments,command):
+ 661# Command arguments required : Yes
+ 662# Active SMB connection needed : No
+ 663# SMB share needed : No
+ 664
+ 665iflen(arguments)==0:
+ 666path='.'
+ 667else:
+ 668path=arguments[0]
+ 669
+ 670ifos.path.exists(path):
+ 671ifos.path.isdir(s=path):
+ 672try:
+ 673shutil.rmtree(path=path)
+ 674exceptExceptionase:
+ 675self.logger.error("Error removing directory '%s' : %s"%path)
+ 676else:
+ 677self.logger.error("Cannot delete '%s'. It is a file, use 'lrm <file>' instead."%path)
+ 678else:
+ 679self.logger.error("Path '%s' does not exist."%path)
+ 680
+ 681defcommand_ltree(self,arguments,command):
+ 682# Command arguments required : No
+ 683# Active SMB connection needed : No
+ 684# SMB share needed : No
+ 685
+ 686iflen(arguments)==0:
+ 687path='.'
+ 688else:
+ 689path=arguments[0]
+ 690
+ 691iflen(arguments)==0:
+ 692local_tree(path='.',config=self.config)
+ 693else:
+ 694local_tree(path=path,config=self.config)
+ 695
+ 696@active_smb_connection_needed
+ 697@smb_share_is_set
+ 698defcommand_ls(self,arguments,command):
+ 699# Command arguments required : No
+ 700# Active SMB connection needed : Yes
+ 701# SMB share needed : Yes
+ 702
+ 703iflen(arguments)==0:
+ 704arguments=['.']
+ 705else:
+ 706arguments=resolve_remote_files(self.sessionsManager.current_session,arguments)
+ 707
+ 708forpathinarguments:
+ 709iflen(arguments)>1:
+ 710self.logger.print("%s:"%path)
+ 711
+ 712ifself.sessionsManager.current_session.path_isdir(pathFromRoot=path):
+ 713# Read the files
+ 714directory_contents=self.sessionsManager.current_session.list_contents(path=path)
+ 715else:
+ 716entry=self.sessionsManager.current_session.get_entry(path=path)
+ 717ifentryisnotNone:
+ 718directory_contents={entry.get_longname():entry}
+ 719else:
+ 720directory_contents={}
+ 721
+ 722forlongnameinsorted(directory_contents.keys(),key=lambdax:x.lower()):
+ 723windows_ls_entry(directory_contents[longname],self.config)
+ 724
+ 725iflen(arguments)>1:
+ 726self.logger.print()
+ 727
+ 728@command_arguments_required
+ 729@active_smb_connection_needed
+ 730@smb_share_is_set
+ 731defcommand_mkdir(self,arguments,command):
+ 732# Command arguments required : Yes
+ 733# Active SMB connection needed : Yes
+ 734# SMB share needed : Yes
+ 735
+ 736self.sessionsManager.current_session.mkdir(path=arguments[0])
+ 737
+ 738@command_arguments_required
+ 739@active_smb_connection_needed
+ 740@smb_share_is_set
+ 741defcommand_module(self,arguments,command):
+ 742module_name=arguments[0]
+ 743
+ 744ifmodule_nameinself.modules.keys():
+ 745module=self.modules[module_name](self.sessionsManager.current_session,self.config,self.logger)
+ 746arguments_string=' '.join(arguments[1:])
+ 747module.run(arguments_string)
+ 748else:
+ 749self.logger.error("Module '%s' does not exist."%module_name)
+ 750
+ 751@command_arguments_required
+ 752@active_smb_connection_needed
+ 753@smb_share_is_set
+ 754defcommand_mount(self,arguments,command):
+ 755# Command arguments required : Yes
+ 756# Active SMB connection needed : Yes
+ 757# SMB share needed : Yes
+ 758
+ 759iflen(arguments)==2:
+ 760remote_path=arguments[0]
+ 761ifnotremote_path.startswith(ntpath.sep):
+ 762remote_path=self.sessionsManager.current_session.smb_cwd+ntpath.sep+remote_path
+ 763
+ 764local_mount_point=arguments[1]
+ 765
+ 766self.logger.debug("Trying to mount remote '%s' onto local '%s'"%(remote_path,local_mount_point))
+ 767
+ 768try:
+ 769self.sessionsManager.current_session.mount(local_mount_point,remote_path)
+ 770except(impacket.smbconnection.SessionError,impacket.smb3.SessionError)ase:
+ 771self.sessionsManager.current_session.umount(local_mount_point)
+ 772else:
+ 773self.commandCompleterObject.print_help(command=command)
+ 774
+ 775@command_arguments_required
+ 776@active_smb_connection_needed
+ 777@smb_share_is_set
+ 778defcommand_put(self,arguments,command):
+ 779# Command arguments required : Yes
+ 780# Active SMB connection needed : Yes
+ 781# SMB share needed : Yes
+ 782
+ 783is_recursive=False
+ 784while'-r'inarguments:
+ 785is_recursive=True
+ 786arguments.remove('-r')
+ 787
+ 788# Parse wildcards
+ 789files_and_directories=resolve_local_files(arguments)
+ 790
+ 791#
+ 792forlocalpathinfiles_and_directories:
+ 793try:
+ 794self.logger.print(localpath)
+ 795ifis_recursiveandos.path.isdir(s=localpath):
+ 796# Put files recursively
+ 797self.sessionsManager.current_session.put_file_recursively(localpath=localpath)
+ 798else:
+ 799# Put this single file
+ 800self.sessionsManager.current_session.put_file(localpath=localpath)
+ 801exceptimpacket.smbconnection.SessionErrorase:
+ 802self.logger.error("[!] SMB Error: %s"%e)
+ 803
+ 804defcommand_reconnect(self,arguments,command):
+ 805# Command arguments required : No
+ 806# Active SMB connection needed : No
+ 807# SMB share needed : No
+ 808
+ 809self.sessionsManager.current_session.ping_smb_session()
+ 810ifself.sessionsManager.current_session.connected:
+ 811self.sessionsManager.current_session.close_smb_session()
+ 812self.sessionsManager.current_session.init_smb_session()
+ 813else:
+ 814self.sessionsManager.current_session.init_smb_session()
+ 815
+ 816defcommand_reset(self,arguments,command):
+ 817# Command arguments required : No
+ 818# Active SMB connection needed : No
+ 819# SMB share needed : No
+ 820sys.stdout.write('\x1b[?25h')# Sets the cursor to on
+ 821sys.stdout.write('\x1b[v')
+ 822sys.stdout.write('\x1b[o')# Reset
+ 823sys.stdout.flush()
+ 824
+ 825@command_arguments_required
+ 826@active_smb_connection_needed
+ 827@smb_share_is_set
+ 828defcommand_rm(self,arguments,command):
+ 829# Command arguments required : Yes
+ 830# Active SMB connection needed : Yes
+ 831# SMB share needed : Yes
+ 832
+ 833forpath_to_fileinarguments:
+ 834# Wildcard
+ 835if'*'inpath_to_file:
+ 836self.sessionsManager.current_session.rm(path=path_to_file)
+ 837# File
+ 838elifself.sessionsManager.current_session.path_exists(path_to_file):
+ 839ifself.sessionsManager.current_session.path_isfile(path_to_file):
+ 840try:
+ 841self.sessionsManager.current_session.rm(path=path_to_file)
+ 842exceptExceptionase:
+ 843self.logger.error("Error removing file '%s' : %s"%path_to_file)
+ 844else:
+ 845self.logger.error("Cannot delete '%s': This is a directory, use 'rmdir <directory>' instead."%path_to_file)
+ 846# File does not exist
+ 847else:
+ 848self.logger.error("Remote file '%s' does not exist."%path_to_file)
+ 849
+ 850@command_arguments_required
+ 851@active_smb_connection_needed
+ 852@smb_share_is_set
+ 853defcommand_rmdir(self,arguments,command):
+ 854# Command arguments required : Yes
+ 855# Active SMB connection needed : Yes
+ 856# SMB share needed : Yes
+ 857
+ 858forpath_to_directoryinarguments:
+ 859ifself.sessionsManager.current_session.path_exists(path_to_directory):
+ 860ifself.sessionsManager.current_session.path_isdir(path_to_directory):
+ 861try:
+ 862self.sessionsManager.current_session.rmdir(path=path_to_directory)
+ 863exceptExceptionase:
+ 864self.logger.error("Error removing directory '%s' : %s"%path_to_directory)
+ 865else:
+ 866self.logger.error("Cannot delete '%s': This is a file, use 'rm <file>' instead."%path_to_directory)
+ 867else:
+ 868self.logger.error("Remote directory '%s' does not exist."%path_to_directory)
+ 869
+ 870@active_smb_connection_needed
+ 871@smb_share_is_set
+ 872defcommand_sizeof(self,arguments,command):
+ 873# Command arguments required : Yes
+ 874# Active SMB connection needed : Yes
+ 875# SMB share needed : Yes
+ 876
+ 877classRecursiveSizeOfPrint(object):
+ 878def__init__(self,entry,sessionsManager,config,logger):
+ 879self.entry=entry
+ 880self.config=config
+ 881self.logger=logger
+ 882self.sessionsManager=sessionsManager
+ 883self.size=0
+ 884
+ 885defupdate(self,entry,fullpath,depth):
+ 886self.size+=entry.get_filesize()
+ 887self.print(end='\r')
+ 888
+ 889defprint(self,end='\n'):
+ 890# Directory
+ 891ifself.entry.is_directory():
+ 892ifself.config.no_colors:
+ 893path="%s\\"%self.entry.get_longname()
+ 894else:
+ 895path="\x1b[1;96m%s\x1b[0m\\"%self.entry.get_longname()
+ 896# File
+ 897else:
+ 898ifself.config.no_colors:
+ 899path="%s"%self.entry.get_longname()
+ 900else:
+ 901path="\x1b[1m%s\x1b[0m"%self.entry.get_longname()
+ 902self.logger.print("%10s%s"%(b_filesize(self.size),path),end=end)
+ 903
+ 904entries=[]
+ 905iflen(arguments)==0:
+ 906entries=self.sessionsManager.current_session.list_contents()
+ 907entries=[entryforname,entryinentries.items()ifnamenotin['.','..']]
+ 908else:
+ 909entry=self.sessionsManager.current_session.get_entry(path=' '.join(arguments))
+ 910entries=[]
+ 911ifentryisnotNone:
+ 912entries=[entry]
+ 913else:
+ 914self.logger.print("[!] Path '%s' does not exist."%' '.join(arguments))
+ 915
+ 916total=0
+ 917forentryinentries:
+ 918rsop=RecursiveSizeOfPrint(
+ 919entry=entry,
+ 920sessionsManager=self.sessionsManager,
+ 921config=self.config,
+ 922logger=self.logger
+ 923)
+ 924# Directory
+ 925ifentry.is_directory():
+ 926self.sessionsManager.current_session.find(
+ 927paths=[entry.get_longname()],
+ 928callback=rsop.update
+ 929)
+ 930# File
+ 931else:
+ 932rsop.update(entry=entry,fullpath=entry.get_longname(),depth=0)
+ 933# Close the print
+ 934rsop.print()
+ 935total+=rsop.size
+ 936
+ 937iflen(entries)>1:
+ 938self.logger.print("──────────────────────")
+ 939self.logger.print(" total %s"%b_filesize(total))
+ 940
+ 941@active_smb_connection_needed
+ 942defcommand_shares(self,arguments,command):
+ 943# Command arguments required : No
+ 944# Active SMB connection needed : Yes
+ 945# SMB share needed : No
+ 946
+ 947do_check_rights=False
+ 948iflen(arguments)!=0:
+ 949ifarguments[0]=="rights":
+ 950do_check_rights=True
+ 951
+ 952shares=self.sessionsManager.current_session.list_shares()
+ 953iflen(shares.keys())!=0:
+ 954table=Table(title=None)
+ 955table.add_column("Share")
+ 956table.add_column("Visibility")
+ 957table.add_column("Type")
+ 958table.add_column("Description",justify="left")
+ 959ifdo_check_rights:
+ 960table.add_column("Rights")
+ 961
+ 962forsharenameinsorted(shares.keys()):
+ 963types=', '.join([s.replace("STYPE_","")forsinshares[sharename]["type"]])
+ 964
+ 965is_hidden=bool(sharename.endswith('$'))
+ 966ifis_hidden:
+ 967str_hidden="[bold bright_blue]Hidden[/bold bright_blue]"
+ 968str_sharename="[bold bright_blue]"+shares[sharename]["name"]+"[/bold bright_blue]"
+ 969str_types="[bold bright_blue]"+types+"[/bold bright_blue]"
+ 970str_comment="[bold bright_blue]"+shares[sharename]["comment"]+"[/bold bright_blue]"
+ 971else:
+ 972str_hidden="[bold bright_yellow]Visible[/bold bright_yellow]"
+ 973str_sharename="[bold bright_yellow]"+shares[sharename]["name"]+"[/bold bright_yellow]"
+ 974str_types="[bold bright_yellow]"+types+"[/bold bright_yellow]"
+ 975str_comment="[bold bright_yellow]"+shares[sharename]["comment"]+"[/bold bright_yellow]"
+ 976
+ 977ifdo_check_rights:
+ 978access_rights=self.sessionsManager.current_session.test_rights(sharename=shares[sharename]["name"])
+ 979str_access_rights="[bold yellow]NO ACCESS[/bold yellow]"
+ 980ifaccess_rights["readable"]andaccess_rights["writable"]:
+ 981str_access_rights="[bold green]READ[/bold green], [bold red]WRITE[/bold red]"
+ 982elifaccess_rights["readable"]:
+ 983str_access_rights="[bold green]READ[/bold green]"
+ 984elifaccess_rights["writable"]:
+ 985# Without READ?? This should not happen IMHO
+ 986str_access_rights="[bold red]WRITE[/bold red]"
+ 987else:
+ 988str_access_rights="[bold yellow]NO ACCESS[/bold yellow]"
+ 989
+ 990ifdo_check_rights:
+ 991table.add_row(str_sharename,str_hidden,str_types,str_comment,str_access_rights)
+ 992else:
+ 993table.add_row(str_sharename,str_hidden,str_types,str_comment)
+ 994
+ 995Console().print(table)
+ 996else:
+ 997self.logger.error("No share served on '%s'"%self.sessionsManager.current_session.host)
+ 998
+ 999@active_smb_connection_needed
+1000@smb_share_is_set
+1001defcommand_tree(self,arguments,command):
+1002# Command arguments required : No
+1003# Active SMB connection needed : Yes
+1004# SMB share needed : Yes
+1005
+1006iflen(arguments)==0:
+1007self.sessionsManager.current_session.tree(path='.')
+1008else:
+1009self.sessionsManager.current_session.tree(path=arguments[0])
+1010
+1011@command_arguments_required
+1012@active_smb_connection_needed
+1013@smb_share_is_set
+1014defcommand_umount(self,arguments,command):
+1015# Command arguments required : Yes
+1016# Active SMB connection needed : Yes
+1017# SMB share needed : Yes
+1018
+1019local_mount_point=arguments[0]
+1020
+1021self.logger.debug("Trying to unmount local mount point '%s'"%(local_mount_point))
+1022
+1023self.sessionsManager.current_session.umount(local_mount_point)
+1024
+1025@command_arguments_required
+1026@active_smb_connection_needed
+1027defcommand_use(self,arguments,command):
+1028# Command arguments required : Yes
+1029# Active SMB connection needed : Yes
+1030# SMB share needed : No
+1031
+1032sharename=arguments[0]
+1033
+1034# Reload the list of shares
+1035shares=self.sessionsManager.current_session.list_shares()
+1036shares=[s.lower()forsinshares.keys()]
+1037
+1038ifsharename.lower()inshares:
+1039self.sessionsManager.current_session.set_share(sharename)
+1040else:
+1041self.logger.error("No share named '%s' on '%s'"%(sharename,self.sessionsManager.current_session.host))
+1042
+1043# Private functions =======================================================
+1044
+1045def__load_modules(self):
+1046"""
+1047 Dynamically loads all Python modules from the 'modules' directory and stores them in the 'modules' dictionary.
+1048 Each module is expected to be a Python file that contains a class with the same name as the file (minus the .py extension).
+1049 The class must have at least two attributes: 'name' and 'description'.
+1050
+1051 This method clears any previously loaded modules, constructs the path to the modules directory, and iterates over
+1052 each file in that directory. If the file is a Python file (ends with .py and is not '__init__.py'), it attempts to
+1053 import the module and access the class within it to add to the 'modules' dictionary.
+1054
+1055 If debug mode is enabled in the configuration, it prints debug information about the loading process and the loaded modules.
+1056 """
+1057
+1058self.modules.clear()
+1059
+1060modules_dir=os.path.normpath(os.path.dirname(__file__)+os.path.sep+".."+os.path.sep+"modules")
+1061self.logger.debug("Loading modules from %s ..."%modules_dir)
+1062sys.path.extend([modules_dir])
+1063
+1064forfileinos.listdir(modules_dir):
+1065filepath=os.path.normpath(modules_dir+os.path.sep+file)
+1066iffile.endswith('.py'):
+1067ifos.path.isfile(filepath)andfilenotin["__init__.py"]:
+1068try:
+1069module_file=import_module('smbclientng.modules.%s'%(file[:-3]))
+1070module=module_file.__getattribute__(file[:-3])
+1071self.modules[module.name.lower()]=module
+1072exceptAttributeErrorase:
+1073pass
+1074
+1075ifself.config.debug:
+1076iflen(self.modules.keys())==0:
+1077self.logger.debug("Loaded 0 modules.")
+1078eliflen(self.modules.keys())==1:
+1079self.logger.debug("Loaded 1 module:")
+1080else:
+1081self.logger.debug("Loaded %d modules:"%len(self.modules.keys()))
+1082formodulenameinsorted(self.modules.keys()):
+1083self.logger.debug("%s : \"%s\" (%s)"%(self.modules[modulename].name,self.modules[modulename].description,self.modules[modulename]))
+1084
+1085ifself.commandCompleterObjectisnotNone:
+1086self.commandCompleterObject.commands["module"]["subcommands"]=list(self.modules.keys())
+1087
+1088def__prompt(self):
+1089"""
+1090 Prints the command prompt for the interactive shell.
+1091
+1092 This method constructs and returns the command prompt string based on the current state of the SMB session.
+1093 The prompt indicates the connection status with a visual symbol and displays the current working directory
+1094 or the SMB share path. The prompt appearance changes based on whether colors are enabled in the configuration.
+1095
+1096 Returns:
+1097 str: The formatted command prompt string.
+1098 """
+1099
+1100# A session exists
+1101ifself.sessionsManager.current_sessionisnotNone:
+1102# Check if the session is still active
+1103self.sessionsManager.current_session.ping_smb_session()
+1104ifself.sessionsManager.current_session.connected:
+1105ifself.config.no_colors:
+1106connected_dot="[v]"
+1107else:
+1108connected_dot="\x1b[1;92m⏺\x1b[0m"
+1109else:
+1110ifself.config.no_colors:
+1111connected_dot="[x]"
+1112else:
+1113connected_dot="\x1b[1;91m⏺\x1b[0m"
+1114
+1115# Session ID if
+1116session_prompt=""
+1117iflen(self.sessionsManager.sessions.keys())>=2:
+1118session_prompt="[#%d]"%self.sessionsManager.current_session_id
+1119
+1120# No share set yet
+1121ifself.sessionsManager.current_session.smb_shareisNone:
+1122str_path="\\\\%s\\"%self.sessionsManager.current_session.host
+1123# A share is set
+1124else:
+1125iflen(self.sessionsManager.current_session.smb_cwd)==0:
+1126current_path=""
+1127else:
+1128current_path=self.sessionsManager.current_session.smb_cwd.strip(ntpath.sep)+ntpath.sep
+1129
+1130str_path="\\\\%s\\%s\\%s"%(self.sessionsManager.current_session.host,self.sessionsManager.current_session.smb_share,current_path)
+1131# No active session
+1132else:
+1133connected_dot=""
+1134session_prompt=""
+1135str_path="No active session"
+1136
+1137# Build final prompt string
+1138ifself.config.no_colors:
+1139str_prompt="%s%s[%s]> "%(connected_dot,session_prompt,str_path)
+1140else:
+1141str_prompt="%s%s[\x1b[1;94m%s\x1b[0m]> "%(connected_dot,session_prompt,str_path)
+1142
+1143returnstr_prompt
129defprocess_command(self,command,arguments=[]):
-130# Skip
-131ifcommand.strip()=="":
-132pass
-133
-134# Display help
-135elifcommand=="help":
-136self.command_help(arguments,command)
-137
-138# Cat the contents of a file
-139elifcommand=="bat":
-140self.command_bat(arguments,command)
-141
-142# Cat the contents of a file
-143elifcommand=="cat":
-144self.command_cat(arguments,command)
-145
-146# Closes the current SMB session
-147elifcommand=="close":
-148self.command_close(arguments,command)
-149
-150# Change directory in the current share
-151elifcommand=="cd":
-152self.command_cd(arguments,command)
-153
-154# debug
-155elifcommand=="debug":
-156self.command_debug(arguments,command)
-157
-158# Get a file
-159elifcommand=="get":
-160self.command_get(arguments,command)
-161
-162# SMB server info
-163elifcommand=="info":
-164self.command_info(arguments,command)
-165
-166# List directory contents in a share
-167elifcommandin["ls","dir"]:
-168self.command_ls(arguments,command)
-169
-170# Creates a new remote directory
-171elifcommand=="mkdir":
-172self.command_mkdir(arguments,command)
-173
-174# Put a file
-175elifcommand=="put":
-176self.command_put(arguments,command)
-177
-178# Shows the content of a local file
-179elifcommand=="lcat":
-180self.command_lcat(arguments,command)
-181
-182# Changes the current local directory
-183elifcommand=="lcd":
-184self.command_lcd(arguments,command)
-185
-186# Creates a copy of a local file
-187elifcommand=="lcp":
-188self.command_lcp(arguments,command)
-189
-190# Pretty prints the content of a local file
-191elifcommand=="lbat":
-192self.command_lbat(arguments,command)
-193
-194# Lists the contents of the current local directory
-195elifcommand=="lls":
-196self.command_lls(arguments,command)
-197
-198# Creates a new local directory
-199elifcommand=="lmkdir":
-200self.command_lmkdir(arguments,command)
-201
-202# Shows the current local directory
-203elifcommand=="lpwd":
-204self.command_lpwd(arguments,command)
-205
-206# Renames a local file
-207elifcommand=="lrename":
-208self.command_lrename(arguments,command)
-209
-210# Removes a local file
-211elifcommand=="lrm":
-212self.command_lrm(arguments,command)
-213
-214# Removes a local directory
-215elifcommand=="lrmdir":
-216self.command_lrmdir(arguments,command)
-217
-218# Shows the current local directory
-219elifcommand=="ltree":
-220self.command_ltree(arguments,command)
-221
-222# Modules
-223elifcommand=="module":
-224self.command_module(arguments,command)
-225
-226# Creates a mount point of the remote share on the local machine
-227elifcommand=="mount":
-228self.command_mount(arguments,command)
-229
-230# Reconnects the current SMB session
-231elifcommandin["connect","reconnect"]:
-232self.command_reconnect(arguments,command)
-233
-234# Reset the TTY output
-235elifcommand=="reset":
-236self.command_reset(arguments,command)
-237
-238# Removes a remote file
-239elifcommand=="rm":
-240self.command_rm(arguments,command)
-241
-242# Removes a remote directory
-243elifcommand=="rmdir":
-244self.command_rmdir(arguments,command)
-245
-246# List shares
-247elifcommand=="sizeof":
-248self.command_sizeof(arguments,command)
-249
-250# List shares
-251elifcommand=="shares":
-252self.command_shares(arguments,command)
-253
-254# Displays a tree view of the CWD
-255elifcommand=="tree":
-256self.command_tree(arguments,command)
-257
-258# Use a share
-259elifcommand=="use":
-260self.command_use(arguments,command)
+
+
141defprocess_line(self,commandLine):
+142# Split and parse the commandLine
+143tokens=shlex.split(commandLine)
+144iflen(tokens)==0:
+145command=""
+146arguments=[]
+147eliflen(tokens)==1:
+148command=tokens[0].lower()
+149arguments=[]
+150else:
+151command=tokens[0].lower()
+152arguments=tokens[1:]
+153
+154# Skip
+155ifcommand.strip()=="":
+156pass
+157# Execute the command
+158elifcommandinself.commandCompleterObject.commands.keys():
+159
+160# Exit the command line
+161ifcommandin["exit","quit"]:
+162self.running=False
+163
+164# Display help
+165elifcommand=="help":
+166self.command_help(arguments,command)
+167
+168# Cat the contents of a file
+169elifcommand=="bat":
+170self.command_bat(arguments,command)
+171
+172# Cat the contents of a file
+173elifcommand=="cat":
+174self.command_cat(arguments,command)
+175
+176# Closes the current SMB session
+177elifcommand=="close":
+178self.command_close(arguments,command)
+179
+180# Change directory in the current share
+181elifcommand=="cd":
+182self.command_cd(arguments,command)
+183
+184# debug
+185elifcommand=="debug":
+186self.command_debug(arguments,command)
+187
+188# Get a file
+189elifcommand=="get":
+190self.command_get(arguments,command)
+191
+192# SMB server info
+193elifcommand=="info":
+194self.command_info(arguments,command)
+195
+196# List directory contents in a share
+197elifcommandin["ls","dir"]:
+198self.command_ls(arguments,command)
+199
+200# Creates a new remote directory
+201elifcommand=="mkdir":
+202self.command_mkdir(arguments,command)
+203
+204# Put a file
+205elifcommand=="put":
+206self.command_put(arguments,command)
+207
+208# Shows the content of a local file
+209elifcommand=="lcat":
+210self.command_lcat(arguments,command)
+211
+212# Changes the current local directory
+213elifcommand=="lcd":
+214self.command_lcd(arguments,command)
+215
+216# Creates a copy of a local file
+217elifcommand=="lcp":
+218self.command_lcp(arguments,command)
+219
+220# Pretty prints the content of a local file
+221elifcommand=="lbat":
+222self.command_lbat(arguments,command)
+223
+224# Lists the contents of the current local directory
+225elifcommand=="lls":
+226self.command_lls(arguments,command)
+227
+228# Creates a new local directory
+229elifcommand=="lmkdir":
+230self.command_lmkdir(arguments,command)
+231
+232# Shows the current local directory
+233elifcommand=="lpwd":
+234self.command_lpwd(arguments,command)
+235
+236# Renames a local file
+237elifcommand=="lrename":
+238self.command_lrename(arguments,command)
+239
+240# Removes a local file
+241elifcommand=="lrm":
+242self.command_lrm(arguments,command)
+243
+244# Removes a local directory
+245elifcommand=="lrmdir":
+246self.command_lrmdir(arguments,command)
+247
+248# Shows the current local directory
+249elifcommand=="ltree":
+250self.command_ltree(arguments,command)
+251
+252# Modules
+253elifcommand=="module":
+254self.command_module(arguments,command)
+255
+256# Creates a mount point of the remote share on the local machine
+257elifcommand=="mount":
+258self.command_mount(arguments,command)
+259
+260# Reconnects the current SMB session
+261elifcommandin["connect","reconnect"]:
+262self.command_reconnect(arguments,command)
+263
+264# Reset the TTY output
+265elifcommand=="reset":
+266self.command_reset(arguments,command)
+267
+268# Removes a remote file
+269elifcommand=="rm":
+270self.command_rm(arguments,command)
+271
+272# Removes a remote directory
+273elifcommand=="rmdir":
+274self.command_rmdir(arguments,command)
+275
+276# Sessions management
+277elifcommand=="sessions":
+278self.sessionsManager.process_command_line(arguments)
+279
+280# List shares
+281elifcommand=="sizeof":
+282self.command_sizeof(arguments,command)
+283
+284# List shares
+285elifcommand=="shares":
+286self.command_shares(arguments,command)
+287
+288# Displays a tree view of the CWD
+289elifcommand=="tree":
+290self.command_tree(arguments,command)
+291
+292# Use a share
+293elifcommand=="use":
+294self.command_use(arguments,command)
+295
+296# Fallback to unknown command
+297else:
+298self.logger.print("Unknown command. Type \"help\" for help.")
697defcommand_reconnect(self,arguments,command):
-698# Command arguments required : No
-699# Active SMB connection needed : No
-700# SMB share needed : No
-701
-702self.smbSession.ping_smb_session()
-703ifself.smbSession.connected:
-704self.smbSession.close_smb_session()
-705self.smbSession.init_smb_session()
-706else:
-707self.smbSession.init_smb_session()
+
804defcommand_reconnect(self,arguments,command):
+805# Command arguments required : No
+806# Active SMB connection needed : No
+807# SMB share needed : No
+808
+809self.sessionsManager.current_session.ping_smb_session()
+810ifself.sessionsManager.current_session.connected:
+811self.sessionsManager.current_session.close_smb_session()
+812self.sessionsManager.current_session.init_smb_session()
+813else:
+814self.sessionsManager.current_session.init_smb_session()
@@ -3162,14 +3527,14 @@
-
709defcommand_reset(self,arguments,command):
-710# Command arguments required : No
-711# Active SMB connection needed : No
-712# SMB share needed : No
-713sys.stdout.write('\x1b[?25h')# Sets the cursor to on
-714sys.stdout.write('\x1b[v')
-715sys.stdout.write('\x1b[o')# Reset
-716sys.stdout.flush()
+
816defcommand_reset(self,arguments,command):
+817# Command arguments required : No
+818# Active SMB connection needed : No
+819# SMB share needed : No
+820sys.stdout.write('\x1b[?25h')# Sets the cursor to on
+821sys.stdout.write('\x1b[v')
+822sys.stdout.write('\x1b[o')# Reset
+823sys.stdout.flush()
27 read(self, size): Reads data from the file up to the specified size and updates the progress bar if expected size is provided. 28 """ 29
- 30def__init__(self,mode,path=None,expected_size=None,keepRemotePath=False,debug=False):
+ 30def__init__(self,mode,path=None,expected_size=None,keepRemotePath=False,logger=None): 31super(LocalFileIO,self).__init__()
- 32self.mode=mode
- 33# Convert remote path format to local operating system path format
- 34self.path=path.replace(ntpath.sep,os.path.sep)
- 35self.dir=None
- 36self.debug=False
- 37self.expected_size=expected_size
- 38self.keepRemotePath=keepRemotePath
- 39
- 40# Write to local (read remote)
- 41ifself.modein["wb"]:
- 42self.dir='.'+os.path.sep
- 43ifkeepRemotePath:
- 44self.dir+=os.path.dirname(self.path)
- 45
- 46ifnotos.path.exists(self.dir):
- 47ifself.debug:
- 48print("[debug] Creating local directory '%s'"%self.dir)
+ 32self.logger=logger
+ 33self.mode=mode
+ 34# Convert remote path format to local operating system path format
+ 35self.path=path.replace(ntpath.sep,os.path.sep)
+ 36self.dir=None
+ 37self.debug=False
+ 38self.expected_size=expected_size
+ 39self.keepRemotePath=keepRemotePath
+ 40
+ 41# Write to local (read remote)
+ 42ifself.modein["wb"]:
+ 43self.dir='.'+os.path.sep
+ 44ifkeepRemotePath:
+ 45self.dir+=os.path.dirname(self.path)
+ 46
+ 47ifnotos.path.exists(self.dir):
+ 48self.logger.debug("[debug] Creating local directory '%s'"%self.dir) 49os.makedirs(self.dir) 50
- 51ifself.debug:
- 52print("[debug] Openning local '%s' with mode '%s'"%(self.path,self.mode))
- 53
- 54self.fd=open(self.dir+os.path.sep+os.path.basename(self.path),self.mode)
- 55
- 56# Write to remote (read local)
- 57elifself.modein["rb"]:
- 58ifntpath.sepinself.path:
- 59self.dir=os.path.dirname(self.path)
- 60
- 61ifself.debug:
- 62print("[debug] Openning local '%s' with mode '%s'"%(self.path,self.mode))
- 63self.fd=open(self.path,self.mode)
- 64
- 65ifself.expected_sizeisNone:
- 66self.expected_size=os.path.getsize(filename=self.path)
- 67
- 68# Create progress bar
- 69ifself.expected_sizeisnotNone:
- 70self.__progress=Progress(
- 71TextColumn("[bold blue]{task.description}",justify="right"),
- 72BarColumn(bar_width=None),
- 73"[progress.percentage]{task.percentage:>3.1f}%",
- 74"•",
- 75DownloadColumn(),
- 76"•",
- 77TransferSpeedColumn(),
- 78"•",
- 79TimeRemainingColumn(),
- 80)
- 81self.__progress.start()
- 82self.__task=self.__progress.add_task(
- 83description="'%s'"%os.path.basename(self.path),
- 84start=True,
- 85total=self.expected_size,
- 86visible=True
- 87)
- 88
- 89defwrite(self,data):
- 90"""
- 91 Writes data to the file.
- 92
- 93 This method writes the specified data to the file and updates the progress bar with the amount of data written if the expected size is set.
+ 51self.logger.debug("[debug] Openning local '%s' with mode '%s'"%(self.path,self.mode))
+ 52
+ 53try:
+ 54self.fd=open(self.dir+os.path.sep+os.path.basename(self.path),self.mode)
+ 55exceptPermissionErroraserr:
+ 56self.fd=None
+ 57
+ 58# Write to remote (read local)
+ 59elifself.modein["rb"]:
+ 60ifntpath.sepinself.path:
+ 61self.dir=os.path.dirname(self.path)
+ 62
+ 63self.logger.debug("[debug] Openning local '%s' with mode '%s'"%(self.path,self.mode))
+ 64
+ 65try:
+ 66self.fd=open(self.path,self.mode)
+ 67exceptPermissionErroraserr:
+ 68self.fd=None
+ 69
+ 70ifself.fdisnotNone:
+ 71ifself.expected_sizeisNone:
+ 72self.expected_size=os.path.getsize(filename=self.path)
+ 73
+ 74# Create progress bar
+ 75ifself.expected_sizeisnotNone:
+ 76self.__progress=Progress(
+ 77TextColumn("[bold blue]{task.description}",justify="right"),
+ 78BarColumn(bar_width=None),
+ 79"[progress.percentage]{task.percentage:>3.1f}%",
+ 80"•",
+ 81DownloadColumn(),
+ 82"•",
+ 83TransferSpeedColumn(),
+ 84"•",
+ 85TimeRemainingColumn(),
+ 86)
+ 87self.__progress.start()
+ 88self.__task=self.__progress.add_task(
+ 89description="'%s'"%os.path.basename(self.path),
+ 90start=True,
+ 91total=self.expected_size,
+ 92visible=True
+ 93) 94
- 95 Args:
- 96 data (bytes): The data to be written to the file.
- 97
- 98 Returns:
- 99 int: The number of bytes written.
-100 """
-101
-102ifself.expected_sizeisnotNone:
-103self.__progress.update(self.__task,advance=len(data))
-104returnself.fd.write(data)
-105
-106defread(self,size):
-107"""
-108 Reads a specified amount of data from the file.
-109
-110 This method reads data from the file based on the size specified. It also updates the progress bar with the amount of data read if the expected size is set.
-111
-112 Args:
-113 size (int): The number of bytes to read from the file.
-114
-115 Returns:
-116 bytes: The data read from the file.
-117 """
+ 95defwrite(self,data):
+ 96"""
+ 97 Writes data to the file.
+ 98
+ 99 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.
+100
+101 Args:
+102 data (bytes): The data to be written to the file.
+103
+104 Returns:
+105 int: The number of bytes written.
+106 """
+107
+108ifself.fdisnotNone:
+109ifself.expected_sizeisnotNone:
+110self.__progress.update(self.__task,advance=len(data))
+111returnself.fd.write(data)
+112else:
+113return0
+114
+115defread(self,size):
+116"""
+117 Reads a specified amount of data from the file.118
-119read_data=self.fd.read(size)
-120ifself.expected_sizeisnotNone:
-121self.__progress.update(self.__task,advance=len(read_data))
-122returnread_data
+119 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.
+120
+121 Args:
+122 size (int): The number of bytes to read from the file.123
-124defclose(self,remove=False):
-125"""
-126 Closes the file descriptor and optionally removes the file.
+124 Returns:
+125 bytes: The data read from the file.
+126 """127
-128 This method ensures that the file descriptor is properly closed and the file is removed if specified.
-129 It also stops the progress bar if it was initiated and cleans up the object by deleting it.
-130
-131 Args:
-132 remove (bool): If True, the file at the path will be removed after closing the file descriptor.
-133 """
-134
-135self.fd.close()
-136
-137ifremove:
-138os.remove(path=self.path)
+128ifself.fdisnotNone:
+129read_data=self.fd.read(size)
+130ifself.expected_sizeisnotNone:
+131self.__progress.update(self.__task,advance=len(read_data))
+132returnread_data
+133else:
+134returnb""
+135
+136defclose(self,remove=False):
+137"""
+138 Closes the file descriptor and optionally removes the file.139
-140ifself.expected_sizeisnotNone:
-141self.__progress.stop()
-142
-143delself
-144
-145defset_error(self,message):
-146"""
-147 Sets an error message in the progress bar's description and modifies the progress bar to show only essential columns.
-148
-149 This method is used to communicate error states or important messages directly in the progress bar interface.
-150 It updates the task description with the provided message and simplifies the progress bar to show only the text
-151 and download columns, removing other elements like speed and time remaining which may not be relevant in an error state.
-152
-153 Args:
-154 message (str): The error or status message to display in the progress bar.
-155 """
-156
-157self.__progress.tasks[0].description=message
-158self.__progress.columns=[
-159TextColumn("[bold blue]{task.description}",justify="right"),
-160BarColumn(bar_width=None),
-161"•",
-162DownloadColumn(),
-163]
-164self.__progress.update(self.__task,advance=0)
+140 This method ensures that the file descriptor is properly closed and the file is removed if specified.
+141 It also stops the progress bar if it was initiated and cleans up the object by deleting it.
+142
+143 Args:
+144 remove (bool): If True, the file at the path will be removed after closing the file descriptor.
+145 """
+146
+147ifself.fdisnotNone:
+148self.fd.close()
+149
+150ifremove:
+151try:
+152os.remove(path=self.path)
+153except(PermissionError,FileNotFoundError)aserr:
+154pass
+155
+156ifself.expected_sizeisnotNone:
+157self.__progress.stop()
+158
+159delself
+160
+161defset_error(self,message):
+162"""
+163 Sets an error message in the progress bar's description and modifies the progress bar to show only essential columns.
+164
+165 This method is used to communicate error states or important messages directly in the progress bar interface.
+166 It updates the task description with the provided message and simplifies the progress bar to show only the text
+167 and download columns, removing other elements like speed and time remaining which may not be relevant in an error state.
+168
+169 Args:
+170 message (str): The error or status message to display in the progress bar.
+171 """
+172
+173self.__progress.tasks[0].description=message
+174self.__progress.columns=[
+175TextColumn("[bold blue]{task.description}",justify="right"),
+176BarColumn(bar_width=None),
+177"•",
+178DownloadColumn(),
+179]
+180self.__progress.update(self.__task,advance=0)
@@ -286,141 +305,157 @@
28 read(self, size): Reads data from the file up to the specified size and updates the progress bar if expected size is provided. 29 """ 30
- 31def__init__(self,mode,path=None,expected_size=None,keepRemotePath=False,debug=False):
+ 31def__init__(self,mode,path=None,expected_size=None,keepRemotePath=False,logger=None): 32super(LocalFileIO,self).__init__()
- 33self.mode=mode
- 34# Convert remote path format to local operating system path format
- 35self.path=path.replace(ntpath.sep,os.path.sep)
- 36self.dir=None
- 37self.debug=False
- 38self.expected_size=expected_size
- 39self.keepRemotePath=keepRemotePath
- 40
- 41# Write to local (read remote)
- 42ifself.modein["wb"]:
- 43self.dir='.'+os.path.sep
- 44ifkeepRemotePath:
- 45self.dir+=os.path.dirname(self.path)
- 46
- 47ifnotos.path.exists(self.dir):
- 48ifself.debug:
- 49print("[debug] Creating local directory '%s'"%self.dir)
+ 33self.logger=logger
+ 34self.mode=mode
+ 35# Convert remote path format to local operating system path format
+ 36self.path=path.replace(ntpath.sep,os.path.sep)
+ 37self.dir=None
+ 38self.debug=False
+ 39self.expected_size=expected_size
+ 40self.keepRemotePath=keepRemotePath
+ 41
+ 42# Write to local (read remote)
+ 43ifself.modein["wb"]:
+ 44self.dir='.'+os.path.sep
+ 45ifkeepRemotePath:
+ 46self.dir+=os.path.dirname(self.path)
+ 47
+ 48ifnotos.path.exists(self.dir):
+ 49self.logger.debug("[debug] Creating local directory '%s'"%self.dir) 50os.makedirs(self.dir) 51
- 52ifself.debug:
- 53print("[debug] Openning local '%s' with mode '%s'"%(self.path,self.mode))
- 54
- 55self.fd=open(self.dir+os.path.sep+os.path.basename(self.path),self.mode)
- 56
- 57# Write to remote (read local)
- 58elifself.modein["rb"]:
- 59ifntpath.sepinself.path:
- 60self.dir=os.path.dirname(self.path)
- 61
- 62ifself.debug:
- 63print("[debug] Openning local '%s' with mode '%s'"%(self.path,self.mode))
- 64self.fd=open(self.path,self.mode)
- 65
- 66ifself.expected_sizeisNone:
- 67self.expected_size=os.path.getsize(filename=self.path)
- 68
- 69# Create progress bar
- 70ifself.expected_sizeisnotNone:
- 71self.__progress=Progress(
- 72TextColumn("[bold blue]{task.description}",justify="right"),
- 73BarColumn(bar_width=None),
- 74"[progress.percentage]{task.percentage:>3.1f}%",
- 75"•",
- 76DownloadColumn(),
- 77"•",
- 78TransferSpeedColumn(),
- 79"•",
- 80TimeRemainingColumn(),
- 81)
- 82self.__progress.start()
- 83self.__task=self.__progress.add_task(
- 84description="'%s'"%os.path.basename(self.path),
- 85start=True,
- 86total=self.expected_size,
- 87visible=True
- 88)
- 89
- 90defwrite(self,data):
- 91"""
- 92 Writes data to the file.
- 93
- 94 This method writes the specified data to the file and updates the progress bar with the amount of data written if the expected size is set.
+ 52self.logger.debug("[debug] Openning local '%s' with mode '%s'"%(self.path,self.mode))
+ 53
+ 54try:
+ 55self.fd=open(self.dir+os.path.sep+os.path.basename(self.path),self.mode)
+ 56exceptPermissionErroraserr:
+ 57self.fd=None
+ 58
+ 59# Write to remote (read local)
+ 60elifself.modein["rb"]:
+ 61ifntpath.sepinself.path:
+ 62self.dir=os.path.dirname(self.path)
+ 63
+ 64self.logger.debug("[debug] Openning local '%s' with mode '%s'"%(self.path,self.mode))
+ 65
+ 66try:
+ 67self.fd=open(self.path,self.mode)
+ 68exceptPermissionErroraserr:
+ 69self.fd=None
+ 70
+ 71ifself.fdisnotNone:
+ 72ifself.expected_sizeisNone:
+ 73self.expected_size=os.path.getsize(filename=self.path)
+ 74
+ 75# Create progress bar
+ 76ifself.expected_sizeisnotNone:
+ 77self.__progress=Progress(
+ 78TextColumn("[bold blue]{task.description}",justify="right"),
+ 79BarColumn(bar_width=None),
+ 80"[progress.percentage]{task.percentage:>3.1f}%",
+ 81"•",
+ 82DownloadColumn(),
+ 83"•",
+ 84TransferSpeedColumn(),
+ 85"•",
+ 86TimeRemainingColumn(),
+ 87)
+ 88self.__progress.start()
+ 89self.__task=self.__progress.add_task(
+ 90description="'%s'"%os.path.basename(self.path),
+ 91start=True,
+ 92total=self.expected_size,
+ 93visible=True
+ 94) 95
- 96 Args:
- 97 data (bytes): The data to be written to the file.
- 98
- 99 Returns:
-100 int: The number of bytes written.
-101 """
-102
-103ifself.expected_sizeisnotNone:
-104self.__progress.update(self.__task,advance=len(data))
-105returnself.fd.write(data)
-106
-107defread(self,size):
-108"""
-109 Reads a specified amount of data from the file.
-110
-111 This method reads data from the file based on the size specified. It also updates the progress bar with the amount of data read if the expected size is set.
-112
-113 Args:
-114 size (int): The number of bytes to read from the file.
-115
-116 Returns:
-117 bytes: The data read from the file.
-118 """
+ 96defwrite(self,data):
+ 97"""
+ 98 Writes data to the file.
+ 99
+100 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.
+101
+102 Args:
+103 data (bytes): The data to be written to the file.
+104
+105 Returns:
+106 int: The number of bytes written.
+107 """
+108
+109ifself.fdisnotNone:
+110ifself.expected_sizeisnotNone:
+111self.__progress.update(self.__task,advance=len(data))
+112returnself.fd.write(data)
+113else:
+114return0
+115
+116defread(self,size):
+117"""
+118 Reads a specified amount of data from the file.119
-120read_data=self.fd.read(size)
-121ifself.expected_sizeisnotNone:
-122self.__progress.update(self.__task,advance=len(read_data))
-123returnread_data
+120 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.
+121
+122 Args:
+123 size (int): The number of bytes to read from the file.124
-125defclose(self,remove=False):
-126"""
-127 Closes the file descriptor and optionally removes the file.
+125 Returns:
+126 bytes: The data read from the file.
+127 """128
-129 This method ensures that the file descriptor is properly closed and the file is removed if specified.
-130 It also stops the progress bar if it was initiated and cleans up the object by deleting it.
-131
-132 Args:
-133 remove (bool): If True, the file at the path will be removed after closing the file descriptor.
-134 """
-135
-136self.fd.close()
-137
-138ifremove:
-139os.remove(path=self.path)
+129ifself.fdisnotNone:
+130read_data=self.fd.read(size)
+131ifself.expected_sizeisnotNone:
+132self.__progress.update(self.__task,advance=len(read_data))
+133returnread_data
+134else:
+135returnb""
+136
+137defclose(self,remove=False):
+138"""
+139 Closes the file descriptor and optionally removes the file.140
-141ifself.expected_sizeisnotNone:
-142self.__progress.stop()
-143
-144delself
-145
-146defset_error(self,message):
-147"""
-148 Sets an error message in the progress bar's description and modifies the progress bar to show only essential columns.
-149
-150 This method is used to communicate error states or important messages directly in the progress bar interface.
-151 It updates the task description with the provided message and simplifies the progress bar to show only the text
-152 and download columns, removing other elements like speed and time remaining which may not be relevant in an error state.
-153
-154 Args:
-155 message (str): The error or status message to display in the progress bar.
-156 """
-157
-158self.__progress.tasks[0].description=message
-159self.__progress.columns=[
-160TextColumn("[bold blue]{task.description}",justify="right"),
-161BarColumn(bar_width=None),
-162"•",
-163DownloadColumn(),
-164]
-165self.__progress.update(self.__task,advance=0)
+141 This method ensures that the file descriptor is properly closed and the file is removed if specified.
+142 It also stops the progress bar if it was initiated and cleans up the object by deleting it.
+143
+144 Args:
+145 remove (bool): If True, the file at the path will be removed after closing the file descriptor.
+146 """
+147
+148ifself.fdisnotNone:
+149self.fd.close()
+150
+151ifremove:
+152try:
+153os.remove(path=self.path)
+154except(PermissionError,FileNotFoundError)aserr:
+155pass
+156
+157ifself.expected_sizeisnotNone:
+158self.__progress.stop()
+159
+160delself
+161
+162defset_error(self,message):
+163"""
+164 Sets an error message in the progress bar's description and modifies the progress bar to show only essential columns.
+165
+166 This method is used to communicate error states or important messages directly in the progress bar interface.
+167 It updates the task description with the provided message and simplifies the progress bar to show only the text
+168 and download columns, removing other elements like speed and time remaining which may not be relevant in an error state.
+169
+170 Args:
+171 message (str): The error or status message to display in the progress bar.
+172 """
+173
+174self.__progress.tasks[0].description=message
+175self.__progress.columns=[
+176TextColumn("[bold blue]{task.description}",justify="right"),
+177BarColumn(bar_width=None),
+178"•",
+179DownloadColumn(),
+180]
+181self.__progress.update(self.__task,advance=0)
31def__init__(self,mode,path=None,expected_size=None,keepRemotePath=False,logger=None):32super(LocalFileIO,self).__init__()
-33self.mode=mode
-34# Convert remote path format to local operating system path format
-35self.path=path.replace(ntpath.sep,os.path.sep)
-36self.dir=None
-37self.debug=False
-38self.expected_size=expected_size
-39self.keepRemotePath=keepRemotePath
-40
-41# Write to local (read remote)
-42ifself.modein["wb"]:
-43self.dir='.'+os.path.sep
-44ifkeepRemotePath:
-45self.dir+=os.path.dirname(self.path)
-46
-47ifnotos.path.exists(self.dir):
-48ifself.debug:
-49print("[debug] Creating local directory '%s'"%self.dir)
+33self.logger=logger
+34self.mode=mode
+35# Convert remote path format to local operating system path format
+36self.path=path.replace(ntpath.sep,os.path.sep)
+37self.dir=None
+38self.debug=False
+39self.expected_size=expected_size
+40self.keepRemotePath=keepRemotePath
+41
+42# Write to local (read remote)
+43ifself.modein["wb"]:
+44self.dir='.'+os.path.sep
+45ifkeepRemotePath:
+46self.dir+=os.path.dirname(self.path)
+47
+48ifnotos.path.exists(self.dir):
+49self.logger.debug("[debug] Creating local directory '%s'"%self.dir)50os.makedirs(self.dir)51
-52ifself.debug:
-53print("[debug] Openning local '%s' with mode '%s'"%(self.path,self.mode))
-54
-55self.fd=open(self.dir+os.path.sep+os.path.basename(self.path),self.mode)
-56
-57# Write to remote (read local)
-58elifself.modein["rb"]:
-59ifntpath.sepinself.path:
-60self.dir=os.path.dirname(self.path)
-61
-62ifself.debug:
-63print("[debug] Openning local '%s' with mode '%s'"%(self.path,self.mode))
-64self.fd=open(self.path,self.mode)
-65
-66ifself.expected_sizeisNone:
-67self.expected_size=os.path.getsize(filename=self.path)
-68
-69# Create progress bar
-70ifself.expected_sizeisnotNone:
-71self.__progress=Progress(
-72TextColumn("[bold blue]{task.description}",justify="right"),
-73BarColumn(bar_width=None),
-74"[progress.percentage]{task.percentage:>3.1f}%",
-75"•",
-76DownloadColumn(),
-77"•",
-78TransferSpeedColumn(),
-79"•",
-80TimeRemainingColumn(),
-81)
-82self.__progress.start()
-83self.__task=self.__progress.add_task(
-84description="'%s'"%os.path.basename(self.path),
-85start=True,
-86total=self.expected_size,
-87visible=True
-88)
+52self.logger.debug("[debug] Openning local '%s' with mode '%s'"%(self.path,self.mode))
+53
+54try:
+55self.fd=open(self.dir+os.path.sep+os.path.basename(self.path),self.mode)
+56exceptPermissionErroraserr:
+57self.fd=None
+58
+59# Write to remote (read local)
+60elifself.modein["rb"]:
+61ifntpath.sepinself.path:
+62self.dir=os.path.dirname(self.path)
+63
+64self.logger.debug("[debug] Openning local '%s' with mode '%s'"%(self.path,self.mode))
+65
+66try:
+67self.fd=open(self.path,self.mode)
+68exceptPermissionErroraserr:
+69self.fd=None
+70
+71ifself.fdisnotNone:
+72ifself.expected_sizeisNone:
+73self.expected_size=os.path.getsize(filename=self.path)
+74
+75# Create progress bar
+76ifself.expected_sizeisnotNone:
+77self.__progress=Progress(
+78TextColumn("[bold blue]{task.description}",justify="right"),
+79BarColumn(bar_width=None),
+80"[progress.percentage]{task.percentage:>3.1f}%",
+81"•",
+82DownloadColumn(),
+83"•",
+84TransferSpeedColumn(),
+85"•",
+86TimeRemainingColumn(),
+87)
+88self.__progress.start()
+89self.__task=self.__progress.add_task(
+90description="'%s'"%os.path.basename(self.path),
+91start=True,
+92total=self.expected_size,
+93visible=True
+94)
+
+
+
+ logger
+
+
+
+
+
+
+
@@ -591,22 +643,25 @@
-
90defwrite(self,data):
- 91"""
- 92 Writes data to the file.
- 93
- 94 This method writes the specified data to the file and updates the progress bar with the amount of data written if the expected size is set.
- 95
- 96 Args:
- 97 data (bytes): The data to be written to the file.
- 98
- 99 Returns:
-100 int: The number of bytes written.
-101 """
-102
-103ifself.expected_sizeisnotNone:
-104self.__progress.update(self.__task,advance=len(data))
-105returnself.fd.write(data)
+
96defwrite(self,data):
+ 97"""
+ 98 Writes data to the file.
+ 99
+100 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.
+101
+102 Args:
+103 data (bytes): The data to be written to the file.
+104
+105 Returns:
+106 int: The number of bytes written.
+107 """
+108
+109ifself.fdisnotNone:
+110ifself.expected_sizeisnotNone:
+111self.__progress.update(self.__task,advance=len(data))
+112returnself.fd.write(data)
+113else:
+114return0
@@ -634,23 +689,26 @@
-
107defread(self,size):
-108"""
-109 Reads a specified amount of data from the file.
-110
-111 This method reads data from the file based on the size specified. It also updates the progress bar with the amount of data read if the expected size is set.
-112
-113 Args:
-114 size (int): The number of bytes to read from the file.
-115
-116 Returns:
-117 bytes: The data read from the file.
-118 """
+
116defread(self,size):
+117"""
+118 Reads a specified amount of data from the file.119
-120read_data=self.fd.read(size)
-121ifself.expected_sizeisnotNone:
-122self.__progress.update(self.__task,advance=len(read_data))
-123returnread_data
+120 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.
+121
+122 Args:
+123 size (int): The number of bytes to read from the file.
+124
+125 Returns:
+126 bytes: The data read from the file.
+127 """
+128
+129ifself.fdisnotNone:
+130read_data=self.fd.read(size)
+131ifself.expected_sizeisnotNone:
+132self.__progress.update(self.__task,advance=len(read_data))
+133returnread_data
+134else:
+135returnb""
@@ -678,26 +736,30 @@
-
125defclose(self,remove=False):
-126"""
-127 Closes the file descriptor and optionally removes the file.
-128
-129 This method ensures that the file descriptor is properly closed and the file is removed if specified.
-130 It also stops the progress bar if it was initiated and cleans up the object by deleting it.
-131
-132 Args:
-133 remove (bool): If True, the file at the path will be removed after closing the file descriptor.
-134 """
-135
-136self.fd.close()
-137
-138ifremove:
-139os.remove(path=self.path)
+
137defclose(self,remove=False):
+138"""
+139 Closes the file descriptor and optionally removes the file.140
-141ifself.expected_sizeisnotNone:
-142self.__progress.stop()
-143
-144delself
+141 This method ensures that the file descriptor is properly closed and the file is removed if specified.
+142 It also stops the progress bar if it was initiated and cleans up the object by deleting it.
+143
+144 Args:
+145 remove (bool): If True, the file at the path will be removed after closing the file descriptor.
+146 """
+147
+148ifself.fdisnotNone:
+149self.fd.close()
+150
+151ifremove:
+152try:
+153os.remove(path=self.path)
+154except(PermissionError,FileNotFoundError)aserr:
+155pass
+156
+157ifself.expected_sizeisnotNone:
+158self.__progress.stop()
+159
+160delself
@@ -723,26 +785,26 @@
-
146defset_error(self,message):
-147"""
-148 Sets an error message in the progress bar's description and modifies the progress bar to show only essential columns.
-149
-150 This method is used to communicate error states or important messages directly in the progress bar interface.
-151 It updates the task description with the provided message and simplifies the progress bar to show only the text
-152 and download columns, removing other elements like speed and time remaining which may not be relevant in an error state.
-153
-154 Args:
-155 message (str): The error or status message to display in the progress bar.
-156 """
-157
-158self.__progress.tasks[0].description=message
-159self.__progress.columns=[
-160TextColumn("[bold blue]{task.description}",justify="right"),
-161BarColumn(bar_width=None),
-162"•",
-163DownloadColumn(),
-164]
-165self.__progress.update(self.__task,advance=0)
+
162defset_error(self,message):
+163"""
+164 Sets an error message in the progress bar's description and modifies the progress bar to show only essential columns.
+165
+166 This method is used to communicate error states or important messages directly in the progress bar interface.
+167 It updates the task description with the provided message and simplifies the progress bar to show only the text
+168 and download columns, removing other elements like speed and time remaining which may not be relevant in an error state.
+169
+170 Args:
+171 message (str): The error or status message to display in the progress bar.
+172 """
+173
+174self.__progress.tasks[0].description=message
+175self.__progress.columns=[
+176TextColumn("[bold blue]{task.description}",justify="right"),
+177BarColumn(bar_width=None),
+178"•",
+179DownloadColumn(),
+180]
+181self.__progress.update(self.__task,advance=0)
1#!/usr/bin/env python3
+ 2# -*- coding: utf-8 -*-
+ 3# File name : LocalFileIO.py
+ 4# Author : Podalirius (@podalirius_)
+ 5# Date created : 24 June 2024
+ 6
+ 7
+ 8importos
+ 9importre
+ 10fromenumimportEnum
+ 11
+ 12
+ 13classLogLevel(Enum):
+ 14INFO=1
+ 15DEBUG=2
+ 16WARNING=3
+ 17ERROR=4
+ 18CRITICAL=5
+ 19
+ 20
+ 21classLogger(object):
+ 22"""
+ 23 A Logger class that provides logging functionalities with various levels such as INFO, DEBUG, WARNING, ERROR, and CRITICAL.
+ 24 It supports color-coded output, which can be disabled, and can also log messages to a file.
+ 25
+ 26 Attributes:
+ 27 __debug (bool): If True, debug level messages will be printed and logged.
+ 28 __nocolors (bool): If True, disables color-coded output.
+ 29 logfile (str|None): Path to a file where logs will be written. If None, logging to a file is disabled.
+ 30
+ 31 Methods:
+ 32 __init__(debug=False, logfile=None, nocolors=False): Initializes the Logger instance.
+ 33 print(message=""): Prints a message to stdout and logs it to a file if logging is enabled.
+ 34 info(message): Logs a message at the INFO level.
+ 35 debug(message): Logs a message at the DEBUG level if debugging is enabled.
+ 36 error(message): Logs a message at the ERROR level.
+ 37 """
+ 38
+ 39def__init__(self,config,logfile=None):
+ 40super(Logger,self).__init__()
+ 41self.config=config
+ 42self.logfile=logfile
+ 43#
+ 44ifself.logfileisnotNone:
+ 45ifos.path.exists(self.logfile):
+ 46k=1
+ 47whileos.path.exists(self.logfile+(".%d"%k)):
+ 48k+=1
+ 49self.logfile=self.logfile+(".%d"%k)
+ 50open(self.logfile,"w").close()
+ 51
+ 52defprint(self,message="",end='\n'):
+ 53"""
+ 54 Prints a message to stdout and logs it to a file if logging is enabled.
+ 55
+ 56 This method prints the provided message to the standard output and also logs it to a file if a log file path is specified during the Logger instance initialization. The message can include color codes for color-coded output, which can be disabled by setting the `nocolors` attribute to True.
+ 57
+ 58 Args:
+ 59 message (str): The message to be printed and logged.
+ 60 """
+ 61
+ 62nocolor_message=re.sub(r"\x1b[\[]([0-9;]+)m","",message)
+ 63ifself.config.no_colors:
+ 64print(nocolor_message,end=end)
+ 65else:
+ 66print(message,end=end)
+ 67self.__write_to_logfile(nocolor_message,end=end)
+ 68
+ 69definfo(self,message):
+ 70"""
+ 71 Logs a message at the INFO level.
+ 72
+ 73 This method logs the provided message at the INFO level. The message can include color codes for color-coded output, which can be disabled by setting the `nocolors` attribute to True. The message is also logged to a file if a log file path is specified during the Logger instance initialization.
+ 74
+ 75 Args:
+ 76 message (str): The message to be logged at the INFO level.
+ 77 """
+ 78
+ 79nocolor_message=re.sub(r"\x1b[\[]([0-9;]+)m","",message)
+ 80ifself.config.no_colors:
+ 81print("[info] %s"%nocolor_message)
+ 82else:
+ 83print("[\x1b[1;92minfo\x1b[0m] %s"%message)
+ 84self.__write_to_logfile("[info] %s"%nocolor_message)
+ 85
+ 86defdebug(self,message):
+ 87"""
+ 88 Logs a message at the DEBUG level if debugging is enabled.
+ 89
+ 90 This method logs the provided message at the DEBUG level if the `debug` attribute is set to True during the Logger instance initialization. The message can include color codes for color-coded output, which can be disabled by setting the `nocolors` attribute to True.
+ 91
+ 92 Args:
+ 93 message (str): The message to be logged.
+ 94 """
+ 95
+ 96ifself.config.debug==True:
+ 97nocolor_message=re.sub(r"\x1b[\[]([0-9;]+)m","",message)
+ 98ifself.config.no_colors:
+ 99print("[debug] %s"%nocolor_message)
+100else:
+101print("[debug] %s"%message)
+102self.__write_to_logfile("[debug] %s"%nocolor_message)
+103
+104deferror(self,message):
+105"""
+106 Logs an error message to the console and the log file.
+107
+108 This method logs the provided error message to the standard error output and also logs it to a file if a log file path is specified during the Logger instance initialization. The message can include color codes for color-coded output, which can be disabled by setting the `nocolors` attribute to True.
+109
+110 Args:
+111 message (str): The error message to be logged.
+112 """
+113
+114nocolor_message=re.sub(r"\x1b[\[]([0-9;]+)m","",message)
+115ifself.config.no_colors:
+116print("[error] %s"%nocolor_message)
+117else:
+118print("[\x1b[1;91merror\x1b[0m] %s"%message)
+119self.__write_to_logfile("[error] %s"%nocolor_message)
+120
+121def__write_to_logfile(self,message,end='\n'):
+122"""
+123 Writes the provided message to the log file specified during Logger instance initialization.
+124
+125 This method appends the provided message to the log file specified by the `logfile` attribute. If no log file path is specified, this method does nothing.
+126
+127 Args:
+128 message (str): The message to be written to the log file.
+129 """
+130
+131ifself.logfileisnotNone:
+132f=open(self.logfile,"a")
+133f.write(message+end)
+134f.close()
+
22classLogger(object):
+ 23"""
+ 24 A Logger class that provides logging functionalities with various levels such as INFO, DEBUG, WARNING, ERROR, and CRITICAL.
+ 25 It supports color-coded output, which can be disabled, and can also log messages to a file.
+ 26
+ 27 Attributes:
+ 28 __debug (bool): If True, debug level messages will be printed and logged.
+ 29 __nocolors (bool): If True, disables color-coded output.
+ 30 logfile (str|None): Path to a file where logs will be written. If None, logging to a file is disabled.
+ 31
+ 32 Methods:
+ 33 __init__(debug=False, logfile=None, nocolors=False): Initializes the Logger instance.
+ 34 print(message=""): Prints a message to stdout and logs it to a file if logging is enabled.
+ 35 info(message): Logs a message at the INFO level.
+ 36 debug(message): Logs a message at the DEBUG level if debugging is enabled.
+ 37 error(message): Logs a message at the ERROR level.
+ 38 """
+ 39
+ 40def__init__(self,config,logfile=None):
+ 41super(Logger,self).__init__()
+ 42self.config=config
+ 43self.logfile=logfile
+ 44#
+ 45ifself.logfileisnotNone:
+ 46ifos.path.exists(self.logfile):
+ 47k=1
+ 48whileos.path.exists(self.logfile+(".%d"%k)):
+ 49k+=1
+ 50self.logfile=self.logfile+(".%d"%k)
+ 51open(self.logfile,"w").close()
+ 52
+ 53defprint(self,message="",end='\n'):
+ 54"""
+ 55 Prints a message to stdout and logs it to a file if logging is enabled.
+ 56
+ 57 This method prints the provided message to the standard output and also logs it to a file if a log file path is specified during the Logger instance initialization. The message can include color codes for color-coded output, which can be disabled by setting the `nocolors` attribute to True.
+ 58
+ 59 Args:
+ 60 message (str): The message to be printed and logged.
+ 61 """
+ 62
+ 63nocolor_message=re.sub(r"\x1b[\[]([0-9;]+)m","",message)
+ 64ifself.config.no_colors:
+ 65print(nocolor_message,end=end)
+ 66else:
+ 67print(message,end=end)
+ 68self.__write_to_logfile(nocolor_message,end=end)
+ 69
+ 70definfo(self,message):
+ 71"""
+ 72 Logs a message at the INFO level.
+ 73
+ 74 This method logs the provided message at the INFO level. The message can include color codes for color-coded output, which can be disabled by setting the `nocolors` attribute to True. The message is also logged to a file if a log file path is specified during the Logger instance initialization.
+ 75
+ 76 Args:
+ 77 message (str): The message to be logged at the INFO level.
+ 78 """
+ 79
+ 80nocolor_message=re.sub(r"\x1b[\[]([0-9;]+)m","",message)
+ 81ifself.config.no_colors:
+ 82print("[info] %s"%nocolor_message)
+ 83else:
+ 84print("[\x1b[1;92minfo\x1b[0m] %s"%message)
+ 85self.__write_to_logfile("[info] %s"%nocolor_message)
+ 86
+ 87defdebug(self,message):
+ 88"""
+ 89 Logs a message at the DEBUG level if debugging is enabled.
+ 90
+ 91 This method logs the provided message at the DEBUG level if the `debug` attribute is set to True during the Logger instance initialization. The message can include color codes for color-coded output, which can be disabled by setting the `nocolors` attribute to True.
+ 92
+ 93 Args:
+ 94 message (str): The message to be logged.
+ 95 """
+ 96
+ 97ifself.config.debug==True:
+ 98nocolor_message=re.sub(r"\x1b[\[]([0-9;]+)m","",message)
+ 99ifself.config.no_colors:
+100print("[debug] %s"%nocolor_message)
+101else:
+102print("[debug] %s"%message)
+103self.__write_to_logfile("[debug] %s"%nocolor_message)
+104
+105deferror(self,message):
+106"""
+107 Logs an error message to the console and the log file.
+108
+109 This method logs the provided error message to the standard error output and also logs it to a file if a log file path is specified during the Logger instance initialization. The message can include color codes for color-coded output, which can be disabled by setting the `nocolors` attribute to True.
+110
+111 Args:
+112 message (str): The error message to be logged.
+113 """
+114
+115nocolor_message=re.sub(r"\x1b[\[]([0-9;]+)m","",message)
+116ifself.config.no_colors:
+117print("[error] %s"%nocolor_message)
+118else:
+119print("[\x1b[1;91merror\x1b[0m] %s"%message)
+120self.__write_to_logfile("[error] %s"%nocolor_message)
+121
+122def__write_to_logfile(self,message,end='\n'):
+123"""
+124 Writes the provided message to the log file specified during Logger instance initialization.
+125
+126 This method appends the provided message to the log file specified by the `logfile` attribute. If no log file path is specified, this method does nothing.
+127
+128 Args:
+129 message (str): The message to be written to the log file.
+130 """
+131
+132ifself.logfileisnotNone:
+133f=open(self.logfile,"a")
+134f.write(message+end)
+135f.close()
+
+
+
+
A Logger class that provides logging functionalities with various levels such as INFO, DEBUG, WARNING, ERROR, and CRITICAL.
+It supports color-coded output, which can be disabled, and can also log messages to a file.
+
+
Attributes:
+ __debug (bool): If True, debug level messages will be printed and logged.
+ __nocolors (bool): If True, disables color-coded output.
+ logfile (str|None): Path to a file where logs will be written. If None, logging to a file is disabled.
+
+
Methods:
+ __init__(debug=False, logfile=None, nocolors=False): Initializes the Logger instance.
+ print(message=""): Prints a message to stdout and logs it to a file if logging is enabled.
+ info(message): Logs a message at the INFO level.
+ debug(message): Logs a message at the DEBUG level if debugging is enabled.
+ error(message): Logs a message at the ERROR level.
53defprint(self,message="",end='\n'):
+54"""
+55 Prints a message to stdout and logs it to a file if logging is enabled.
+56
+57 This method prints the provided message to the standard output and also logs it to a file if a log file path is specified during the Logger instance initialization. The message can include color codes for color-coded output, which can be disabled by setting the `nocolors` attribute to True.
+58
+59 Args:
+60 message (str): The message to be printed and logged.
+61 """
+62
+63nocolor_message=re.sub(r"\x1b[\[]([0-9;]+)m","",message)
+64ifself.config.no_colors:
+65print(nocolor_message,end=end)
+66else:
+67print(message,end=end)
+68self.__write_to_logfile(nocolor_message,end=end)
+
+
+
+
Prints a message to stdout and logs it to a file if logging is enabled.
+
+
This method prints the provided message to the standard output and also logs it to a file if a log file path is specified during the Logger instance initialization. The message can include color codes for color-coded output, which can be disabled by setting the nocolors attribute to True.
+
+
Args:
+ message (str): The message to be printed and logged.
+
+
+
+
+
+
+
+
+ def
+ info(self, message):
+
+
+
+
+
+
70definfo(self,message):
+71"""
+72 Logs a message at the INFO level.
+73
+74 This method logs the provided message at the INFO level. The message can include color codes for color-coded output, which can be disabled by setting the `nocolors` attribute to True. The message is also logged to a file if a log file path is specified during the Logger instance initialization.
+75
+76 Args:
+77 message (str): The message to be logged at the INFO level.
+78 """
+79
+80nocolor_message=re.sub(r"\x1b[\[]([0-9;]+)m","",message)
+81ifself.config.no_colors:
+82print("[info] %s"%nocolor_message)
+83else:
+84print("[\x1b[1;92minfo\x1b[0m] %s"%message)
+85self.__write_to_logfile("[info] %s"%nocolor_message)
+
+
+
+
Logs a message at the INFO level.
+
+
This method logs the provided message at the INFO level. The message can include color codes for color-coded output, which can be disabled by setting the nocolors attribute to True. The message is also logged to a file if a log file path is specified during the Logger instance initialization.
+
+
Args:
+ message (str): The message to be logged at the INFO level.
+
+
+
+
+
+
+
+
+ def
+ debug(self, message):
+
+
+
+
+
+
87defdebug(self,message):
+ 88"""
+ 89 Logs a message at the DEBUG level if debugging is enabled.
+ 90
+ 91 This method logs the provided message at the DEBUG level if the `debug` attribute is set to True during the Logger instance initialization. The message can include color codes for color-coded output, which can be disabled by setting the `nocolors` attribute to True.
+ 92
+ 93 Args:
+ 94 message (str): The message to be logged.
+ 95 """
+ 96
+ 97ifself.config.debug==True:
+ 98nocolor_message=re.sub(r"\x1b[\[]([0-9;]+)m","",message)
+ 99ifself.config.no_colors:
+100print("[debug] %s"%nocolor_message)
+101else:
+102print("[debug] %s"%message)
+103self.__write_to_logfile("[debug] %s"%nocolor_message)
+
+
+
+
Logs a message at the DEBUG level if debugging is enabled.
+
+
This method logs the provided message at the DEBUG level if the debug attribute is set to True during the Logger instance initialization. The message can include color codes for color-coded output, which can be disabled by setting the nocolors attribute to True.
+
+
Args:
+ message (str): The message to be logged.
+
+
+
+
+
+
+
+
+ def
+ error(self, message):
+
+
+
+
+
+
105deferror(self,message):
+106"""
+107 Logs an error message to the console and the log file.
+108
+109 This method logs the provided error message to the standard error output and also logs it to a file if a log file path is specified during the Logger instance initialization. The message can include color codes for color-coded output, which can be disabled by setting the `nocolors` attribute to True.
+110
+111 Args:
+112 message (str): The error message to be logged.
+113 """
+114
+115nocolor_message=re.sub(r"\x1b[\[]([0-9;]+)m","",message)
+116ifself.config.no_colors:
+117print("[error] %s"%nocolor_message)
+118else:
+119print("[\x1b[1;91merror\x1b[0m] %s"%message)
+120self.__write_to_logfile("[error] %s"%nocolor_message)
+
+
+
+
Logs an error message to the console and the log file.
+
+
This method logs the provided error message to the standard error output and also logs it to a file if a log file path is specified during the Logger instance initialization. The message can include color codes for color-coded output, which can be disabled by setting the nocolors attribute to True.
+
+
Args:
+ message (str): The error message to be logged.
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/documentation/smbclientng/core/Module.html b/documentation/smbclientng/core/Module.html
index f3a042e..9833f3a 100644
--- a/documentation/smbclientng/core/Module.html
+++ b/documentation/smbclientng/core/Module.html
@@ -51,6 +51,9 @@
20smbSession=None21options=None22
-23def__init__(self,smbSession,config):
+23def__init__(self,smbSession,config,logger):24self.smbSession=smbSession25self.config=config
-26
-27defparseArgs(self):
-28raiseNotImplementedError("Subclasses must implement this method")
-29
-30defrun(self):
-31"""
-32 Placeholder method for running the module.
-33
-34 This method should be implemented by subclasses to define the specific behavior of the module.
-35 """
-36raiseNotImplementedError("Subclasses must implement this method")
-37
-38defprocessArguments(self,parser,arguments):
-39iftype(arguments)==list:
-40arguments=' '.join(arguments)
-41
-42__iterableArguments=shlex.split(arguments)
-43
-44self.options=parser.parse_args(__iterableArguments)
-45
-46returnself.options
-47
+26self.logger=logger
+27
+28defparseArgs(self):
+29raiseNotImplementedError("Subclasses must implement this method")
+30
+31defrun(self):
+32"""
+33 Placeholder method for running the module.
+34
+35 This method should be implemented by subclasses to define the specific behavior of the module.
+36 """
+37raiseNotImplementedError("Subclasses must implement this method")
+38
+39defprocessArguments(self,parser,arguments):
+40iftype(arguments)==list:
+41arguments=' '.join(arguments)
+42
+43__iterableArguments=shlex.split(arguments)
+44
+45try:
+46self.options=parser.parse_args(__iterableArguments)
+47exceptSystemExitase:
+48pass
+49
+50returnself.options
+51
@@ -158,30 +165,34 @@
21smbSession=None22options=None23
-24def__init__(self,smbSession,config):
+24def__init__(self,smbSession,config,logger):25self.smbSession=smbSession26self.config=config
-27
-28defparseArgs(self):
-29raiseNotImplementedError("Subclasses must implement this method")
-30
-31defrun(self):
-32"""
-33 Placeholder method for running the module.
-34
-35 This method should be implemented by subclasses to define the specific behavior of the module.
-36 """
-37raiseNotImplementedError("Subclasses must implement this method")
-38
-39defprocessArguments(self,parser,arguments):
-40iftype(arguments)==list:
-41arguments=' '.join(arguments)
-42
-43__iterableArguments=shlex.split(arguments)
-44
-45self.options=parser.parse_args(__iterableArguments)
-46
-47returnself.options
+27self.logger=logger
+28
+29defparseArgs(self):
+30raiseNotImplementedError("Subclasses must implement this method")
+31
+32defrun(self):
+33"""
+34 Placeholder method for running the module.
+35
+36 This method should be implemented by subclasses to define the specific behavior of the module.
+37 """
+38raiseNotImplementedError("Subclasses must implement this method")
+39
+40defprocessArguments(self,parser,arguments):
+41iftype(arguments)==list:
+42arguments=' '.join(arguments)
+43
+44__iterableArguments=shlex.split(arguments)
+45
+46try:
+47self.options=parser.parse_args(__iterableArguments)
+48exceptSystemExitase:
+49pass
+50
+51returnself.options
28defparseArgs(self):
-29raiseNotImplementedError("Subclasses must implement this method")
+
29defparseArgs(self):
+30raiseNotImplementedError("Subclasses must implement this method")
@@ -299,13 +322,13 @@
-
31defrun(self):
-32"""
-33 Placeholder method for running the module.
-34
-35 This method should be implemented by subclasses to define the specific behavior of the module.
-36 """
-37raiseNotImplementedError("Subclasses must implement this method")
+
32defrun(self):
+33"""
+34 Placeholder method for running the module.
+35
+36 This method should be implemented by subclasses to define the specific behavior of the module.
+37 """
+38raiseNotImplementedError("Subclasses must implement this method")
24 Overrides the default error handling to provide a more informative error message and display the help text.25 """26
-27deferror(self,message):
-28"""
-29 Overrides the default error handling of argparse.ArgumentParser to provide a custom error message and help display.
-30
-31 This method is called when ArgumentParser encounters an error. It writes the error message to stderr,
-32 displays the help message, and then exits the program with a status code of 2.
-33
-34 Args:
-35 message (str): The error message to be displayed.
-36 """
-37
-38sys.stderr.write('[!] Error: %s\n'%message)
-39self.print_help()
+27exit_on_error=False
+28
+29deferror(self,message):
+30"""
+31 Overrides the default error handling of argparse.ArgumentParser to provide a custom error message and help display.
+32
+33 This method is called when ArgumentParser encounters an error. It writes the error message to stderr,
+34 displays the help message, and then exits the program with a status code of 2.
+35
+36 Args:
+37 message (str): The error message to be displayed.
+38 """
+39
+40self.print_help()
+41sys.stderr.write('\n[!] Error: %s\n'%message)
@@ -129,19 +134,21 @@
25 Overrides the default error handling to provide a more informative error message and display the help text.26 """27
-28deferror(self,message):
-29"""
-30 Overrides the default error handling of argparse.ArgumentParser to provide a custom error message and help display.
-31
-32 This method is called when ArgumentParser encounters an error. It writes the error message to stderr,
-33 displays the help message, and then exits the program with a status code of 2.
-34
-35 Args:
-36 message (str): The error message to be displayed.
-37 """
-38
-39sys.stderr.write('[!] Error: %s\n'%message)
-40self.print_help()
+28exit_on_error=False
+29
+30deferror(self,message):
+31"""
+32 Overrides the default error handling of argparse.ArgumentParser to provide a custom error message and help display.
+33
+34 This method is called when ArgumentParser encounters an error. It writes the error message to stderr,
+35 displays the help message, and then exits the program with a status code of 2.
+36
+37 Args:
+38 message (str): The error message to be displayed.
+39 """
+40
+41self.print_help()
+42sys.stderr.write('\n[!] Error: %s\n'%message)
@@ -159,6 +166,18 @@
+
+
+ exit_on_error =
+False
+
+
+
+
+
+
+
+
@@ -170,19 +189,19 @@
-
28deferror(self,message):
-29"""
-30 Overrides the default error handling of argparse.ArgumentParser to provide a custom error message and help display.
-31
-32 This method is called when ArgumentParser encounters an error. It writes the error message to stderr,
-33 displays the help message, and then exits the program with a status code of 2.
-34
-35 Args:
-36 message (str): The error message to be displayed.
-37 """
-38
-39sys.stderr.write('[!] Error: %s\n'%message)
-40self.print_help()
+
30deferror(self,message):
+31"""
+32 Overrides the default error handling of argparse.ArgumentParser to provide a custom error message and help display.
+33
+34 This method is called when ArgumentParser encounters an error. It writes the error message to stderr,
+35 displays the help message, and then exits the program with a status code of 2.
+36
+37 Args:
+38 message (str): The error message to be displayed.
+39 """
+40
+41self.print_help()
+42sys.stderr.write('\n[!] Error: %s\n'%message)
14importsys 15importtraceback 16fromsmbclientng.core.LocalFileIOimportLocalFileIO
- 17fromsmbclientng.core.utilsimportb_filesize,STYPE_MASK
+ 17fromsmbclientng.core.utilsimportb_filesize,STYPE_MASK,is_port_open 18 19 20classSMBSession(object): 21"""
- 22 Class SMBSession is designed to handle the session management for SMB (Server Message Block) protocol connections.
- 23 It provides functionalities to connect to an SMB server, authenticate using either NTLM or Kerberos, and manage SMB shares.
- 24
- 25 Attributes:
- 26 address (str): The IP address or hostname of the SMB server.
- 27 domain (str): The domain name for SMB server authentication.
- 28 username (str): The username for SMB server authentication.
- 29 password (str): The password for SMB server authentication.
- 30 lmhash (str): The LM hash of the user's password, if available.
- 31 nthash (str): The NT hash of the user's password, if available.
- 32 use_kerberos (bool): A flag to determine whether to use Kerberos for authentication.
- 33 kdcHost (str): The Key Distribution Center (KDC) host for Kerberos authentication.
- 34 debug (bool): A flag to enable debug output.
- 35 smbClient (object): The SMB client object used for the connection.
- 36 connected (bool): A flag to check the status of the connection.
+ 22 Represents an SMB session for interacting with an SMB server.
+ 23
+ 24 This class provides methods to manage and interact with an SMB server, including
+ 25 connecting to the server, listing shares, uploading and downloading files, and
+ 26 managing directories and files on the server. It handles session initialization,
+ 27 authentication, and cleanup.
+ 28
+ 29 Attributes:
+ 30 host (str): The hostname or IP address of the SMB server.
+ 31 port (int): The port number on which the SMB server is listening.
+ 32 credentials (dict): Authentication credentials for the SMB server.
+ 33 config (dict, optional): Configuration options for the SMB session.
+ 34 smbClient (impacket.smbconnection.SMBConnection): The SMB connection instance.
+ 35 connected (bool): Connection status to the SMB server.
+ 36 available_shares (dict): A dictionary of available SMB shares. 37 smb_share (str): The current SMB share in use.
- 38 smb_path (str): The current path within the SMB share.
- 39
- 40 Methods:
- 41 __init__(address, domain, username, password, lmhash, nthash, use_kerberos=False, kdcHost=None, debug=False):
- 42 Initializes the SMBSession with the specified parameters.
- 43 init_smb_session():
- 44 Initializes the SMB session by connecting to the server and authenticating using the specified method.
- 45 """
- 46
- 47def__init__(self,address,domain,username,password,lmhash,nthash,use_kerberos=False,kdcHost=None,config=None):
- 48super(SMBSession,self).__init__()
- 49# Objects
- 50self.config=config
- 51
- 52# Target server
- 53self.address=address
- 54
- 55# Credentials
- 56self.domain=domain
- 57self.username=username
- 58self.password=password
- 59self.lmhash=lmhash
- 60self.nthash=nthash
- 61self.use_kerberos=use_kerberos
- 62self.kdcHost=kdcHost
- 63
- 64self.smbClient=None
- 65self.connected=False
+ 38 smb_cwd (str): The current working directory on the SMB share.
+ 39 smb_tree_id (int): The tree ID of the connected SMB share.
+ 40
+ 41 Methods:
+ 42 close_smb_session(): Closes the current SMB session.
+ 43 init_smb_session(): Initializes the SMB session with the server.
+ 44 list_shares(): Lists all shares available on the SMB server.
+ 45 set_share(shareName): Sets the current SMB share.
+ 46 set_cwd(path): Sets the current working directory on the SMB share.
+ 47 put_file(localpath): Uploads a file to the current SMB share.
+ 48 get_file(remotepath, localpath): Downloads a file from the SMB share.
+ 49 mkdir(path): Creates a directory on the SMB share.
+ 50 rmdir(path): Removes a directory from the SMB share.
+ 51 rm(path): Removes a file from the SMB share.
+ 52 read_file(path): Reads a file from the SMB share.
+ 53 test_rights(sharename): Tests read and write access rights on a share.
+ 54 """
+ 55
+ 56def__init__(self,host,port,credentials,config=None,logger=None):
+ 57super(SMBSession,self).__init__()
+ 58# Objects
+ 59self.config=config
+ 60self.logger=logger
+ 61
+ 62# Target server
+ 63self.host=host
+ 64# Target port (by default on 445)
+ 65self.port=port 66
- 67self.available_shares={}
- 68self.smb_share=None
- 69self.smb_cwd=""
- 70self.smb_tree_id=None
- 71
- 72self.list_shares()
- 73
- 74# Connect and disconnect SMB session
- 75
- 76definit_smb_session(self):
- 77"""
- 78 Initializes and establishes a session with the SMB server.
+ 67# Credentials
+ 68self.credentials=credentials
+ 69
+ 70self.smbClient=None
+ 71self.connected=False
+ 72
+ 73self.available_shares={}
+ 74self.smb_share=None
+ 75self.smb_cwd=""
+ 76self.smb_tree_id=None
+ 77
+ 78self.list_shares() 79
- 80 This method sets up the SMB connection using either Kerberos or NTLM authentication based on the configuration.
- 81 It attempts to connect to the SMB server specified by the `address` attribute and authenticate using the credentials provided during the object's initialization.
- 82
- 83 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.
- 84
- 85 Returns:
- 86 bool: True if the connection and authentication are successful, False otherwise.
- 87 """
+ 80# Connect and disconnect SMB session
+ 81
+ 82defclose_smb_session(self):
+ 83"""
+ 84 Closes the current SMB session by disconnecting the SMB client.
+ 85
+ 86 This method ensures that the SMB client connection is properly closed. It checks if the client is connected
+ 87 and if so, it closes the connection and resets the connection status. 88
- 89self.connected=False
- 90
- 91ifself.config.debug:
- 92print("[debug] [>] Connecting to remote SMB server '%s' ... "%self.address)
- 93try:
- 94self.smbClient=impacket.smbconnection.SMBConnection(
- 95remoteName=self.address,
- 96remoteHost=self.address,
- 97sess_port=int(445)
- 98)
- 99exceptOSErroraserr:
- 100print("[!] %s"%err)
- 101self.smbClient=None
+ 89 Raises:
+ 90 Exception: If the SMB client is not initialized or if there's an error during the disconnection process.
+ 91 """
+ 92
+ 93ifself.smbClientisnotNone:
+ 94ifself.connected:
+ 95self.smbClient.close()
+ 96self.connected=False
+ 97self.logger.debug("[+] SMB connection closed successfully.")
+ 98else:
+ 99self.logger.debug("[!] No active SMB connection to close.")
+ 100else:
+ 101raiseException("SMB client is not initialized.") 102
- 103ifself.smbClientisnotNone:
- 104ifself.use_kerberos:
- 105ifself.config.debug:
- 106print("[debug] [>] Authenticating as '%s\\%s' with kerberos ... "%(self.domain,self.username))
- 107try:
- 108self.connected=self.smbClient.kerberosLogin(
- 109user=self.username,
- 110password=self.password,
- 111domain=self.domain,
- 112lmhash=self.lmhash,
- 113nthash=self.nthash,
- 114aesKey=self.aesKey,
- 115kdcHost=self.kdcHost
- 116)
- 117exceptimpacket.smbconnection.SessionErroraserr:
- 118ifself.config.debug:
- 119traceback.print_exc()
- 120print("[!] Could not login: %s"%err)
- 121self.connected=False
- 122
- 123else:
- 124ifself.config.debug:
- 125print("[debug] [>] Authenticating as '%s\\%s' with NTLM ... "%(self.domain,self.username))
- 126try:
- 127self.connected=self.smbClient.login(
- 128user=self.username,
- 129password=self.password,
- 130domain=self.domain,
- 131lmhash=self.lmhash,
- 132nthash=self.nthash
- 133)
- 134exceptimpacket.smbconnection.SessionErroraserr:
- 135ifself.config.debug:
- 136traceback.print_exc()
- 137print("[!] Could not login: %s"%err)
- 138self.connected=False
- 139
- 140ifself.connected:
- 141print("[+] Successfully authenticated to '%s' as '%s\\%s'!"%(self.address,self.domain,self.username))
- 142else:
- 143print("[!] Failed to authenticate to '%s' as '%s\\%s'!"%(self.address,self.domain,self.username))
- 144
- 145returnself.connected
- 146
- 147defclose_smb_session(self):
- 148"""
- 149 Closes the current SMB session by disconnecting the SMB client.
- 150
- 151 This method ensures that the SMB client connection is properly closed. It checks if the client is connected
- 152 and if so, it closes the connection and resets the connection status.
+ 103definit_smb_session(self):
+ 104"""
+ 105 Initializes and establishes a session with the SMB server.
+ 106
+ 107 This method sets up the SMB connection using either Kerberos or NTLM authentication based on the configuration.
+ 108 It attempts to connect to the SMB server specified by the `address` attribute and authenticate using the credentials provided during the object's initialization.
+ 109
+ 110 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.
+ 111
+ 112 Returns:
+ 113 bool: True if the connection and authentication are successful, False otherwise.
+ 114 """
+ 115
+ 116self.connected=False
+ 117
+ 118self.logger.debug("[>] Connecting to remote SMB server '%s' ... "%self.host)
+ 119
+ 120try:
+ 121ifis_port_open(self.host,self.port):
+ 122self.smbClient=impacket.smbconnection.SMBConnection(
+ 123remoteName=self.host,
+ 124remoteHost=self.host,
+ 125sess_port=int(self.port)
+ 126)
+ 127else:
+ 128self.connected=False
+ 129exceptOSErroraserr:
+ 130ifself.config.debug:
+ 131traceback.print_exc()
+ 132self.logger.error(err)
+ 133self.smbClient=None
+ 134
+ 135ifself.smbClientisnotNone:
+ 136ifself.credentials.use_kerberos:
+ 137self.logger.debug("[>] Authenticating as '%s\\%s' with kerberos ... "%(self.credentials.domain,self.credentials.username))
+ 138try:
+ 139self.connected=self.smbClient.kerberosLogin(
+ 140user=self.credentials.username,
+ 141password=self.credentials.password,
+ 142domain=self.credentials.domain,
+ 143lmhash=self.credentials.lm_hex,
+ 144nthash=self.credentials.nt_hex,
+ 145aesKey=self.credentials.aesKey,
+ 146kdcHost=self.credentials.kdcHost
+ 147)
+ 148exceptimpacket.smbconnection.SessionErroraserr:
+ 149ifself.config.debug:
+ 150traceback.print_exc()
+ 151self.logger.error("Could not login: %s"%err)
+ 152self.connected=False 153
- 154 Raises:
- 155 Exception: If the SMB client is not initialized or if there's an error during the disconnection process.
- 156 """
- 157
- 158ifself.smbClientisnotNone:
- 159ifself.connected:
- 160self.smbClient.close()
- 161self.connected=False
- 162ifself.config.debug:
- 163print("[+] SMB connection closed successfully.")
- 164else:
- 165ifself.config.debug:
- 166print("[!] No active SMB connection to close.")
- 167else:
- 168raiseException("SMB client is not initialized.")
- 169
- 170# Operations
- 171
- 172defread_file(self,path=None):
- 173ifself.path_isfile(path=path):
- 174tmp_file_path=self.smb_cwd+ntpath.sep+path
- 175matches=self.smbClient.listPath(
- 176shareName=self.smb_share,
- 177path=tmp_file_path
- 178)
- 179
- 180fh=io.BytesIO()
- 181try:
- 182# opening the files in streams instead of mounting shares allows
- 183# for running the script from unprivileged containers
- 184self.smbClient.getFile(self.smb_share,tmp_file_path,fh.write)
- 185exceptimpacket.smbconnection.SessionErrorase:
- 186returnNone
- 187rawdata=fh.getvalue()
- 188fh.close()
- 189returnrawdata
- 190else:
- 191print("[!] Remote path '%s' is not a file."%path)
- 192
- 193deffind(self,paths=[],callback=None):
- 194defrecurse_action(paths=[],depth=0,callback=None):
- 195ifcallbackisNone:
- 196return[]
- 197
- 198next_directories_to_explore=[]
- 199
- 200forpathinpaths:
- 201remote_smb_path=ntpath.normpath(self.smb_cwd+ntpath.sep+path)
- 202entries=[]
- 203
- 204try:
- 205entries=self.smbClient.listPath(
- 206shareName=self.smb_share,
- 207path=(remote_smb_path+ntpath.sep+'*')
- 208)
- 209exceptimpacket.smbconnection.SessionErroraserr:
- 210continue
- 211# Remove dot names
- 212entries=[eforeinentriesife.get_longname()notin[".",".."]]
- 213# Sort the entries ignoring case
- 214entries=sorted(entries,key=lambdax:x.get_longname().lower())
- 215
- 216forentryinentries:
- 217ifentry.is_directory():
- 218callback(entry,path+ntpath.sep+entry.get_longname()+ntpath.sep,depth)
- 219else:
- 220callback(entry,path+ntpath.sep+entry.get_longname(),depth)
- 221
- 222# Next directories to explore
- 223forentryinentries:
- 224ifentry.is_directory():
- 225next_directories_to_explore.append(path+ntpath.sep+entry.get_longname()+ntpath.sep)
- 226
- 227returnnext_directories_to_explore
- 228#
- 229ifcallbackisnotNone:
- 230depth=0
- 231whilelen(paths)!=0:
- 232paths=recurse_action(
- 233paths=paths,
- 234depth=depth,
- 235callback=callback
- 236)
- 237depth=depth+1
- 238else:
- 239print("[!] SMBSession.find(), callback function cannot be None.")
- 240
- 241defget_file(self,path=None,keepRemotePath=False):
- 242"""
- 243 Retrieves a file from the specified path on the SMB share.
- 244
- 245 This method attempts to retrieve a file from the given path within the currently connected SMB share.
- 246 If the path points to a directory, it skips the retrieval. It handles file retrieval by creating a local
- 247 file object and writing the contents of the remote file to it using the SMB client's getFile method.
- 248
- 249 Parameters:
- 250 path (str): The path of the file to retrieve. If None, uses the current smb_path.
- 251
- 252 Returns:
- 253 None
- 254 """
- 255
- 256# Parse path
- 257path=path.replace('/',ntpath.sep)
- 258ifntpath.sepinpath:
- 259tmp_search_path=ntpath.normpath(self.smb_cwd+ntpath.sep+ntpath.dirname(path))
+ 154else:
+ 155self.logger.debug("[>] Authenticating as '%s\\%s' with NTLM ... "%(self.credentials.domain,self.credentials.username))
+ 156
+ 157try:
+ 158self.connected=self.smbClient.login(
+ 159user=self.credentials.username,
+ 160password=self.credentials.password,
+ 161domain=self.credentials.domain,
+ 162lmhash=self.credentials.lm_hex,
+ 163nthash=self.credentials.nt_hex
+ 164)
+ 165exceptimpacket.smbconnection.SessionErroraserr:
+ 166ifself.config.debug:
+ 167traceback.print_exc()
+ 168self.logger.error("Could not login: %s"%err)
+ 169self.connected=False
+ 170
+ 171ifself.connected:
+ 172self.logger.print("[+] Successfully authenticated to '%s' as '%s\\%s'!"%(self.host,self.credentials.domain,self.credentials.username))
+ 173else:
+ 174self.logger.error("Failed to authenticate to '%s' as '%s\\%s'!"%(self.host,self.credentials.domain,self.credentials.username))
+ 175
+ 176returnself.connected
+ 177
+ 178defping_smb_session(self):
+ 179"""
+ 180 Tests the connectivity to the SMB server by sending an echo command.
+ 181
+ 182 This method attempts to send an echo command to the SMB server to check if the session is still active.
+ 183 It updates the `connected` attribute of the class based on the success or failure of the echo command.
+ 184
+ 185 Returns:
+ 186 bool: True if the echo command succeeds (indicating the session is active), False otherwise.
+ 187 """
+ 188
+ 189ifnotis_port_open(self.host,self.port):
+ 190self.connected=False
+ 191else:
+ 192try:
+ 193self.smbClient.getSMBServer().echo()
+ 194exceptExceptionase:
+ 195self.connected=False
+ 196
+ 197returnself.connected
+ 198
+ 199# Operations
+ 200
+ 201deffind(self,paths=[],callback=None):
+ 202"""
+ 203 Finds files and directories on the SMB share based on the provided paths and executes a callback function on each entry.
+ 204
+ 205 This method traverses the specified paths on the SMB share, recursively exploring directories and invoking the callback
+ 206 function on each file or directory found. The callback function is called with three arguments: the entry object, the
+ 207 full path of the entry, and the current depth of recursion.
+ 208
+ 209 Args:
+ 210 paths (list, optional): A list of paths to start the search from. Defaults to an empty list.
+ 211 callback (function, optional): A function to be called on each entry found. The function should accept three arguments:
+ 212 the entry object, the full path of the entry, and the current depth of recursion. Defaults to None.
+ 213
+ 214 Note:
+ 215 If the callback function is None, the method will print an error message and return without performing any action.
+ 216 """
+ 217
+ 218defrecurse_action(paths=[],depth=0,callback=None):
+ 219ifcallbackisNone:
+ 220return[]
+ 221
+ 222next_directories_to_explore=[]
+ 223
+ 224forpathinpaths:
+ 225remote_smb_path=ntpath.normpath(self.smb_cwd+ntpath.sep+path)
+ 226entries=[]
+ 227
+ 228try:
+ 229entries=self.smbClient.listPath(
+ 230shareName=self.smb_share,
+ 231path=(remote_smb_path+ntpath.sep+'*')
+ 232)
+ 233exceptimpacket.smbconnection.SessionErroraserr:
+ 234continue
+ 235# Remove dot names
+ 236entries=[eforeinentriesife.get_longname()notin[".",".."]]
+ 237# Sort the entries ignoring case
+ 238entries=sorted(entries,key=lambdax:x.get_longname().lower())
+ 239
+ 240forentryinentries:
+ 241ifentry.is_directory():
+ 242fullpath=path+ntpath.sep+entry.get_longname()+ntpath.sep
+ 243next_directories_to_explore.append(fullpath)
+ 244else:
+ 245fullpath=path+ntpath.sep+entry.get_longname()
+ 246fullpath=re.sub(r'\\\\+',r'\\',fullpath)
+ 247callback(entry,fullpath,depth)
+ 248
+ 249returnnext_directories_to_explore
+ 250#
+ 251ifcallbackisnotNone:
+ 252depth=0
+ 253whilelen(paths)!=0:
+ 254paths=recurse_action(
+ 255paths=paths,
+ 256depth=depth,
+ 257callback=callback
+ 258)
+ 259depth=depth+1 260else:
- 261tmp_search_path=ntpath.normpath(self.smb_cwd+ntpath.sep)
- 262# Parse filename
- 263filename=ntpath.basename(path)
- 264
- 265# Search for the file
- 266matches=self.smbClient.listPath(
- 267shareName=self.smb_share,
- 268path=tmp_search_path+ntpath.sep+'*'
- 269)
+ 261self.logger.error("SMBSession.find(), callback function cannot be None.")
+ 262
+ 263defget_file(self,path=None,keepRemotePath=False):
+ 264"""
+ 265 Retrieves a file from the specified path on the SMB share.
+ 266
+ 267 This method attempts to retrieve a file from the given path within the currently connected SMB share.
+ 268 If the path points to a directory, it skips the retrieval. It handles file retrieval by creating a local
+ 269 file object and writing the contents of the remote file to it using the SMB client's getFile method. 270
- 271# Filter the entries
- 272matching_entries=[]
- 273forentryinmatches:
- 274ifentry.is_directory():
- 275# Skip directories
- 276continue
- 277ifentry.get_longname()==filename:
- 278matching_entries.append(entry)
- 279elif'*'infilename:
- 280regexp=filename.replace('.','\\.').replace('*','.*')
- 281ifre.match(regexp,entry.get_longname()):
- 282matching_entries.append(entry)
- 283
- 284matching_entries=sorted(list(set(matching_entries)),key=lambdax:x.get_longname())
- 285
- 286forentryinmatching_entries:
- 287ifentry.is_directory():
- 288ifself.config.debug:
- 289print("[debug] [>] Skipping '%s' because it is a directory."%(tmp_search_path+ntpath.sep+entry.get_longname()))
- 290else:
- 291try:
- 292ifntpath.sepinpath:
- 293outputfile=ntpath.dirname(path)+ntpath.sep+entry.get_longname()
- 294else:
- 295outputfile=entry.get_longname()
- 296f=LocalFileIO(
- 297mode="wb",
- 298path=outputfile,
- 299expected_size=entry.get_filesize(),
- 300debug=self.config.debug,
- 301keepRemotePath=keepRemotePath
- 302)
- 303self.smbClient.getFile(
- 304shareName=self.smb_share,
- 305pathName=tmp_search_path+ntpath.sep+entry.get_longname(),
- 306callback=f.write
- 307)
- 308f.close()
- 309except(BrokenPipeError,KeyboardInterrupt)ase:
- 310f.close()
- 311print("\x1b[v\x1b[o\r[!] Interrupted.")
- 312self.close_smb_session()
- 313self.init_smb_session()
- 314
- 315returnNone
- 316
- 317defget_file_recursively(self,path=None):
- 318"""
- 319 Recursively retrieves files from a specified path on the SMB share.
- 320
- 321 This method navigates through all directories starting from the given path,
- 322 and downloads all files found. It handles directories recursively, ensuring
- 323 that all nested files are retrieved. The method skips over directory entries
- 324 and handles errors gracefully, attempting to continue the operation where possible.
- 325
- 326 Parameters:
- 327 path (str): The initial directory path from which to start the recursive file retrieval.
- 328 If None, it starts from the root of the configured SMB share.
- 329 """
- 330
- 331defrecurse_action(base_dir="",path=[]):
- 332iflen(base_dir)==0:
- 333remote_smb_path=ntpath.sep.join(path)
- 334else:
- 335remote_smb_path=base_dir+ntpath.sep+ntpath.sep.join(path)
- 336remote_smb_path=ntpath.normpath(remote_smb_path)
+ 271 Parameters:
+ 272 path (str): The path of the file to retrieve. If None, uses the current smb_path.
+ 273
+ 274 Returns:
+ 275 None
+ 276 """
+ 277
+ 278# Parse path
+ 279path=path.replace('/',ntpath.sep)
+ 280ifntpath.sepinpath:
+ 281tmp_search_path=ntpath.normpath(self.smb_cwd+ntpath.sep+ntpath.dirname(path))
+ 282else:
+ 283tmp_search_path=ntpath.normpath(self.smb_cwd+ntpath.sep)
+ 284# Parse filename
+ 285filename=ntpath.basename(path)
+ 286
+ 287# Search for the file
+ 288matches=self.smbClient.listPath(
+ 289shareName=self.smb_share,
+ 290path=tmp_search_path+ntpath.sep+'*'
+ 291)
+ 292
+ 293# Filter the entries
+ 294matching_entries=[]
+ 295forentryinmatches:
+ 296ifentry.is_directory():
+ 297# Skip directories
+ 298continue
+ 299ifentry.get_longname()==filename:
+ 300matching_entries.append(entry)
+ 301elif'*'infilename:
+ 302regexp=filename.replace('.','\\.').replace('*','.*')
+ 303ifre.match(regexp,entry.get_longname()):
+ 304matching_entries.append(entry)
+ 305
+ 306matching_entries=sorted(list(set(matching_entries)),key=lambdax:x.get_longname())
+ 307
+ 308forentryinmatching_entries:
+ 309ifentry.is_directory():
+ 310self.logger.debug("[>] Skipping '%s' because it is a directory."%(tmp_search_path+ntpath.sep+entry.get_longname()))
+ 311else:
+ 312try:
+ 313ifntpath.sepinpath:
+ 314outputfile=ntpath.dirname(path)+ntpath.sep+entry.get_longname()
+ 315else:
+ 316outputfile=entry.get_longname()
+ 317f=LocalFileIO(
+ 318mode="wb",
+ 319path=outputfile,
+ 320expected_size=entry.get_filesize(),
+ 321debug=self.config.debug,
+ 322keepRemotePath=keepRemotePath
+ 323)
+ 324self.smbClient.getFile(
+ 325shareName=self.smb_share,
+ 326pathName=tmp_search_path+ntpath.sep+entry.get_longname(),
+ 327callback=f.write
+ 328)
+ 329f.close()
+ 330except(BrokenPipeError,KeyboardInterrupt)ase:
+ 331f.close()
+ 332print("\x1b[v\x1b[o\r[!] Interrupted.")
+ 333self.close_smb_session()
+ 334self.init_smb_session()
+ 335
+ 336returnNone 337
- 338entries=self.smbClient.listPath(
- 339shareName=self.smb_share,
- 340path=remote_smb_path+'\\*'
- 341)
- 342iflen(entries)!=0:
- 343files=[entryforentryinentriesifnotentry.is_directory()]
- 344directories=[entryforentryinentriesifentry.is_directory()andentry.get_longname()notin[".",".."]]
- 345
- 346# Files
- 347iflen(files)!=0:
- 348print("[>] Retrieving files of '%s'"%remote_smb_path)
- 349forentry_fileinfiles:
- 350ifnotentry_file.is_directory():
- 351f=LocalFileIO(
- 352mode="wb",
- 353path=remote_smb_path+ntpath.sep+entry_file.get_longname(),
- 354expected_size=entry_file.get_filesize(),
- 355keepRemotePath=True,
- 356debug=self.config.debug
- 357)
- 358try:
- 359self.smbClient.getFile(
- 360shareName=self.smb_share,
- 361pathName=remote_smb_path+ntpath.sep+entry_file.get_longname(),
- 362callback=f.write
- 363)
- 364f.close()
- 365exceptBrokenPipeErroraserr:
- 366f.set_error(message="[bold red]Failed downloading '%s': %s"%(f.path,err))
- 367f.close(remove=True)
- 368break
- 369exceptExceptionaserr:
- 370f.set_error(message="[bold red]Failed downloading '%s': %s"%(f.path,err))
- 371f.close(remove=True)
- 372
- 373# Directories
- 374forentry_directoryindirectories:
- 375ifentry_directory.is_directory():
- 376recurse_action(
- 377base_dir=self.smb_cwd,
- 378path=path+[entry_directory.get_longname()]
- 379)
- 380# Entrypoint
- 381try:
- 382recurse_action(
- 383base_dir=self.smb_cwd,
- 384path=[path]
- 385)
- 386except(BrokenPipeError,KeyboardInterrupt)ase:
- 387print("\x1b[v\x1b[o\r[!] Interrupted.")
- 388self.close_smb_session()
- 389self.init_smb_session()
- 390
- 391defget_entry(self,path=None):
- 392"""
- 393 Retrieves information about a specific entry located at the provided path on the SMB share.
- 394
- 395 This method checks if the specified path exists on the SMB share. If the path exists, it retrieves the details of the entry at that path, including the directory name and file name. If the entry is found, it returns the entry object; otherwise, it returns None.
- 396
- 397 Args:
- 398 path (str): The path of the entry to retrieve information about.
- 399
- 400 Returns:
- 401 Entry: An object representing the entry at the specified path, or None if the entry is not found.
- 402 """
- 403
- 404ifself.path_exists(path=path):
- 405matches=self.smbClient.listPath(shareName=self.smb_share,path=path)
- 406
- 407iflen(matches)==1:
- 408returnmatches[0]
- 409else:
- 410returnNone
- 411
- 412else:
- 413returnNone
- 414
- 415definfo(self,share=True,server=True):
- 416"""
- 417 Displays information about the server and optionally the shares.
- 418
- 419 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.
+ 338defget_file_recursively(self,path=None):
+ 339"""
+ 340 Recursively retrieves files from a specified path on the SMB share.
+ 341
+ 342 This method navigates through all directories starting from the given path,
+ 343 and downloads all files found. It handles directories recursively, ensuring
+ 344 that all nested files are retrieved. The method skips over directory entries
+ 345 and handles errors gracefully, attempting to continue the operation where possible.
+ 346
+ 347 Parameters:
+ 348 path (str): The initial directory path from which to start the recursive file retrieval.
+ 349 If None, it starts from the root of the configured SMB share.
+ 350 """
+ 351
+ 352defrecurse_action(base_dir="",path=[]):
+ 353iflen(base_dir)==0:
+ 354remote_smb_path=ntpath.sep.join(path)
+ 355else:
+ 356remote_smb_path=base_dir+ntpath.sep+ntpath.sep.join(path)
+ 357remote_smb_path=ntpath.normpath(remote_smb_path)
+ 358
+ 359entries=self.smbClient.listPath(
+ 360shareName=self.smb_share,
+ 361path=remote_smb_path+'\\*'
+ 362)
+ 363iflen(entries)!=0:
+ 364files=[entryforentryinentriesifnotentry.is_directory()]
+ 365directories=[entryforentryinentriesifentry.is_directory()andentry.get_longname()notin[".",".."]]
+ 366
+ 367# Files
+ 368iflen(files)!=0:
+ 369self.logger.print("[>] Retrieving files of '%s'"%remote_smb_path)
+ 370forentry_fileinfiles:
+ 371ifnotentry_file.is_directory():
+ 372f=LocalFileIO(
+ 373mode="wb",
+ 374path=remote_smb_path+ntpath.sep+entry_file.get_longname(),
+ 375expected_size=entry_file.get_filesize(),
+ 376keepRemotePath=True,
+ 377debug=self.config.debug
+ 378)
+ 379try:
+ 380self.smbClient.getFile(
+ 381shareName=self.smb_share,
+ 382pathName=remote_smb_path+ntpath.sep+entry_file.get_longname(),
+ 383callback=f.write
+ 384)
+ 385f.close()
+ 386exceptBrokenPipeErroraserr:
+ 387f.set_error(message="[bold red]Failed downloading '%s': %s"%(f.path,err))
+ 388f.close(remove=True)
+ 389break
+ 390exceptExceptionaserr:
+ 391f.set_error(message="[bold red]Failed downloading '%s': %s"%(f.path,err))
+ 392f.close(remove=True)
+ 393
+ 394# Directories
+ 395forentry_directoryindirectories:
+ 396ifentry_directory.is_directory():
+ 397recurse_action(
+ 398base_dir=self.smb_cwd,
+ 399path=path+[entry_directory.get_longname()]
+ 400)
+ 401# Entrypoint
+ 402try:
+ 403recurse_action(
+ 404base_dir=self.smb_cwd,
+ 405path=[path]
+ 406)
+ 407except(BrokenPipeError,KeyboardInterrupt)ase:
+ 408print("\x1b[v\x1b[o\r[!] Interrupted.")
+ 409self.close_smb_session()
+ 410self.init_smb_session()
+ 411
+ 412defget_entry(self,path=None):
+ 413"""
+ 414 Retrieves information about a specific entry located at the provided path on the SMB share.
+ 415
+ 416 This method checks if the specified path exists on the SMB share. If the path exists, it retrieves the details of the entry at that path, including the directory name and file name. If the entry is found, it returns the entry object; otherwise, it returns None.
+ 417
+ 418 Args:
+ 419 path (str): The path of the entry to retrieve information about. 420
- 421 Parameters:
- 422 share (bool): If True, display information about the current share.
- 423 server (bool): If True, display information about the server.
+ 421 Returns:
+ 422 Entry: An object representing the entry at the specified path, or None if the entry is not found.
+ 423 """ 424
- 425 Returns:
- 426 None
- 427 """
- 428
- 429ifserver:
- 430ifself.config.no_colors:
- 431print("[+] Server:")
- 432print(" ├─NetBIOS:")
- 433print(" │ ├─ NetBIOS Hostname ──────── : %s"%(self.smbClient.getServerName()))
- 434print(" │ └─ NetBIOS Domain ────────── : %s"%(self.smbClient.getServerDomain()))
- 435print(" ├─DNS:")
- 436print(" │ ├─ DNS Hostname ──────────── : %s"%(self.smbClient.getServerDNSHostName()))
- 437print(" │ └─ DNS Domain ────────────── : %s"%(self.smbClient.getServerDNSDomainName()))
- 438print(" ├─OS:")
- 439print(" │ ├─ OS Name ───────────────── : %s"%(self.smbClient.getServerOS()))
- 440print(" │ └─ OS Version ────────────── : %s.%s.%s"%(self.smbClient.getServerOSMajor(),self.smbClient.getServerOSMinor(),self.smbClient.getServerOSBuild()))
- 441print(" ├─Server:")
- 442print(" │ ├─ Signing Required ──────── : %s"%(self.smbClient.isSigningRequired()))
- 443print(" │ ├─ Login Required ────────── : %s"%(self.smbClient.isLoginRequired()))
- 444print(" │ ├─ Supports NTLMv2 ───────── : %s"%(self.smbClient.doesSupportNTLMv2()))
- 445MaxReadSize=self.smbClient.getIOCapabilities()["MaxReadSize"]
- 446print(" │ ├─ Max size of read chunk ── : %d bytes (%s)"%(MaxReadSize,b_filesize(MaxReadSize)))
- 447MaxWriteSize=self.smbClient.getIOCapabilities()["MaxWriteSize"]
- 448print(" │ └─ Max size of write chunk ─ : %d bytes (%s)"%(MaxWriteSize,b_filesize(MaxWriteSize)))
- 449print(" └─")
- 450else:
- 451print("[+] Server:")
- 452print(" ├─NetBIOS:")
- 453print(" │ ├─ \x1b[94mNetBIOS Hostname\x1b[0m \x1b[90m────────\x1b[0m : \x1b[93m%s\x1b[0m"%(self.smbClient.getServerName()))
- 454print(" │ └─ \x1b[94mNetBIOS Domain\x1b[0m \x1b[90m──────────\x1b[0m : \x1b[93m%s\x1b[0m"%(self.smbClient.getServerDomain()))
- 455print(" ├─DNS:")
- 456print(" │ ├─ \x1b[94mDNS Hostname\x1b[0m \x1b[90m────────────\x1b[0m : \x1b[93m%s\x1b[0m"%(self.smbClient.getServerDNSHostName()))
- 457print(" │ └─ \x1b[94mDNS Domain\x1b[0m \x1b[90m──────────────\x1b[0m : \x1b[93m%s\x1b[0m"%(self.smbClient.getServerDNSDomainName()))
- 458print(" ├─OS:")
- 459print(" │ ├─ \x1b[94mOS Name\x1b[0m \x1b[90m─────────────────\x1b[0m : \x1b[93m%s\x1b[0m"%(self.smbClient.getServerOS()))
- 460print(" │ └─ \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()))
- 461print(" ├─Server:")
- 462print(" │ ├─ \x1b[94mSigning Required\x1b[0m \x1b[90m────────\x1b[0m : \x1b[93m%s\x1b[0m"%(self.smbClient.isSigningRequired()))
- 463print(" │ ├─ \x1b[94mLogin Required\x1b[0m \x1b[90m──────────\x1b[0m : \x1b[93m%s\x1b[0m"%(self.smbClient.isLoginRequired()))
- 464print(" │ ├─ \x1b[94mSupports NTLMv2\x1b[0m \x1b[90m─────────\x1b[0m : \x1b[93m%s\x1b[0m"%(self.smbClient.doesSupportNTLMv2()))
- 465MaxReadSize=self.smbClient.getIOCapabilities()["MaxReadSize"]
- 466print(" │ ├─ \x1b[94mMax size of read chunk\x1b[0m \x1b[90m──\x1b[0m : \x1b[93m%d bytes (%s)\x1b[0m"%(MaxReadSize,b_filesize(MaxReadSize)))
- 467MaxWriteSize=self.smbClient.getIOCapabilities()["MaxWriteSize"]
- 468print(" │ └─ \x1b[94mMax size of write chunk\x1b[0m \x1b[90m─\x1b[0m : \x1b[93m%d bytes (%s)\x1b[0m"%(MaxWriteSize,b_filesize(MaxWriteSize)))
- 469print(" └─")
- 470
- 471ifshareandself.smb_shareisnotNone:
- 472share_name=self.available_shares.get(self.smb_share.lower(),"")["name"]
- 473share_comment=self.available_shares.get(self.smb_share.lower(),"")["comment"]
- 474share_type=self.available_shares.get(self.smb_share.lower(),"")["type"]
- 475share_type=', '.join([s.replace("STYPE_","")forsinshare_type])
- 476share_rawtype=self.available_shares.get(self.smb_share.lower(),"")["rawtype"]
- 477ifself.config.no_colors:
- 478print("\n[+] Share:")
- 479print(" ├─ Name ──────────── : %s"%(share_name))
- 480print(" ├─ Description ───── : %s"%(share_comment))
- 481print(" ├─ Type ──────────── : %s"%(share_type))
- 482print(" └─ Raw type value ── : %s"%(share_rawtype))
- 483else:
- 484print("\n[+] Share:")
- 485print(" ├─ \x1b[94mName\x1b[0m \x1b[90m────────────\x1b[0m : \x1b[93m%s\x1b[0m"%(share_name))
- 486print(" ├─ \x1b[94mDescription\x1b[0m \x1b[90m─────\x1b[0m : \x1b[93m%s\x1b[0m"%(share_comment))
- 487print(" ├─ \x1b[94mType\x1b[0m \x1b[90m────────────\x1b[0m : \x1b[93m%s\x1b[0m"%(share_type))
- 488print(" └─ \x1b[94mRaw type value\x1b[0m \x1b[90m──\x1b[0m : \x1b[93m%s\x1b[0m"%(share_rawtype))
- 489
- 490deflist_contents(self,path=None):
- 491"""
- 492 Lists the contents of a specified directory on the SMB share.
+ 425ifself.path_exists(path=path):
+ 426matches=self.smbClient.listPath(
+ 427shareName=self.smb_share,
+ 428path=path
+ 429)
+ 430
+ 431iflen(matches)==1:
+ 432returnmatches[0]
+ 433else:
+ 434returnNone
+ 435else:
+ 436returnNone
+ 437
+ 438definfo(self,share=True,server=True):
+ 439"""
+ 440 Displays information about the server and optionally the shares.
+ 441
+ 442 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.
+ 443
+ 444 Parameters:
+ 445 share (bool): If True, display information about the current share.
+ 446 server (bool): If True, display information about the server.
+ 447
+ 448 Returns:
+ 449 None
+ 450 """
+ 451
+ 452ifserver:
+ 453ifself.config.no_colors:
+ 454self.logger.print("[+] Server:")
+ 455self.logger.print(" ├─NetBIOS:")
+ 456self.logger.print(" │ ├─ NetBIOS Hostname ──────── : %s"%(self.smbClient.getServerName()))
+ 457self.logger.print(" │ └─ NetBIOS Domain ────────── : %s"%(self.smbClient.getServerDomain()))
+ 458self.logger.print(" ├─DNS:")
+ 459self.logger.print(" │ ├─ DNS Hostname ──────────── : %s"%(self.smbClient.getServerDNSHostName()))
+ 460self.logger.print(" │ └─ DNS Domain ────────────── : %s"%(self.smbClient.getServerDNSDomainName()))
+ 461self.logger.print(" ├─OS:")
+ 462self.logger.print(" │ ├─ OS Name ───────────────── : %s"%(self.smbClient.getServerOS()))
+ 463self.logger.print(" │ └─ OS Version ────────────── : %s.%s.%s"%(self.smbClient.getServerOSMajor(),self.smbClient.getServerOSMinor(),self.smbClient.getServerOSBuild()))
+ 464self.logger.print(" ├─Server:")
+ 465self.logger.print(" │ ├─ Signing Required ──────── : %s"%(self.smbClient.isSigningRequired()))
+ 466self.logger.print(" │ ├─ Login Required ────────── : %s"%(self.smbClient.isLoginRequired()))
+ 467self.logger.print(" │ ├─ Supports NTLMv2 ───────── : %s"%(self.smbClient.doesSupportNTLMv2()))
+ 468MaxReadSize=self.smbClient.getIOCapabilities()["MaxReadSize"]
+ 469self.logger.print(" │ ├─ Max size of read chunk ── : %d bytes (%s)"%(MaxReadSize,b_filesize(MaxReadSize)))
+ 470MaxWriteSize=self.smbClient.getIOCapabilities()["MaxWriteSize"]
+ 471self.logger.print(" │ └─ Max size of write chunk ─ : %d bytes (%s)"%(MaxWriteSize,b_filesize(MaxWriteSize)))
+ 472self.logger.print(" └─")
+ 473else:
+ 474self.logger.print("[+] Server:")
+ 475self.logger.print(" ├─NetBIOS:")
+ 476self.logger.print(" │ ├─ \x1b[94mNetBIOS Hostname\x1b[0m \x1b[90m────────\x1b[0m : \x1b[93m%s\x1b[0m"%(self.smbClient.getServerName()))
+ 477self.logger.print(" │ └─ \x1b[94mNetBIOS Domain\x1b[0m \x1b[90m──────────\x1b[0m : \x1b[93m%s\x1b[0m"%(self.smbClient.getServerDomain()))
+ 478self.logger.print(" ├─DNS:")
+ 479self.logger.print(" │ ├─ \x1b[94mDNS Hostname\x1b[0m \x1b[90m────────────\x1b[0m : \x1b[93m%s\x1b[0m"%(self.smbClient.getServerDNSHostName()))
+ 480self.logger.print(" │ └─ \x1b[94mDNS Domain\x1b[0m \x1b[90m──────────────\x1b[0m : \x1b[93m%s\x1b[0m"%(self.smbClient.getServerDNSDomainName()))
+ 481self.logger.print(" ├─OS:")
+ 482self.logger.print(" │ ├─ \x1b[94mOS Name\x1b[0m \x1b[90m─────────────────\x1b[0m : \x1b[93m%s\x1b[0m"%(self.smbClient.getServerOS()))
+ 483self.logger.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()))
+ 484self.logger.print(" ├─Server:")
+ 485self.logger.print(" │ ├─ \x1b[94mSigning Required\x1b[0m \x1b[90m────────\x1b[0m : \x1b[93m%s\x1b[0m"%(self.smbClient.isSigningRequired()))
+ 486self.logger.print(" │ ├─ \x1b[94mLogin Required\x1b[0m \x1b[90m──────────\x1b[0m : \x1b[93m%s\x1b[0m"%(self.smbClient.isLoginRequired()))
+ 487self.logger.print(" │ ├─ \x1b[94mSupports NTLMv2\x1b[0m \x1b[90m─────────\x1b[0m : \x1b[93m%s\x1b[0m"%(self.smbClient.doesSupportNTLMv2()))
+ 488MaxReadSize=self.smbClient.getIOCapabilities()["MaxReadSize"]
+ 489self.logger.print(" │ ├─ \x1b[94mMax size of read chunk\x1b[0m \x1b[90m──\x1b[0m : \x1b[93m%d bytes (%s)\x1b[0m"%(MaxReadSize,b_filesize(MaxReadSize)))
+ 490MaxWriteSize=self.smbClient.getIOCapabilities()["MaxWriteSize"]
+ 491self.logger.print(" │ └─ \x1b[94mMax size of write chunk\x1b[0m \x1b[90m─\x1b[0m : \x1b[93m%d bytes (%s)\x1b[0m"%(MaxWriteSize,b_filesize(MaxWriteSize)))
+ 492self.logger.print(" └─") 493
- 494 This method retrieves the contents of a directory specified by `shareName` and `path`. If `shareName` or `path`
- 495 is not provided, it defaults to the instance's current SMB share or path. The method returns a dictionary with
- 496 the long names of the files and directories as keys and their respective SMB entry objects as values.
- 497
- 498 Args:
- 499 shareName (str, optional): The name of the SMB share. Defaults to the current SMB share if None.
- 500 path (str, optional): The directory path to list contents from. Defaults to the current path if None.
- 501
- 502 Returns:
- 503 dict: A dictionary with file and directory names as keys and their SMB entry objects as values.
- 504 """
- 505
- 506dest_path=[self.smb_cwd.rstrip(ntpath.sep),]
- 507ifpathisnotNoneandlen(path)>0:
- 508dest_path.append(path.rstrip(ntpath.sep))
- 509dest_path.append('*')
- 510path=ntpath.sep.join(dest_path)
- 511
- 512contents={}
- 513entries=self.smbClient.listPath(
- 514shareName=self.smb_share,
- 515path=path
- 516)
- 517forentryinentries:
- 518contents[entry.get_longname()]=entry
- 519
- 520returncontents
- 521
- 522deflist_shares(self):
- 523"""
- 524 Lists all the shares available on the connected SMB server.
- 525
- 526 This method queries the SMB server to retrieve a list of all available shares. It populates the `shares` dictionary
- 527 with key-value pairs where the key is the share name and the value is a dictionary containing details about the share
- 528 such as its name, type, raw type, and any comments associated with the share.
- 529
- 530 Returns:
- 531 dict: A dictionary containing information about each share available on the server.
- 532 """
- 533
- 534self.available_shares={}
- 535
- 536ifself.connected:
- 537ifself.smbClientisnotNone:
- 538resp=self.smbClient.listShares()
- 539
- 540forshareinresp:
- 541# SHARE_INFO_1 structure (lmshare.h)
- 542# https://learn.microsoft.com/en-us/windows/win32/api/lmshare/ns-lmshare-share_info_1
- 543sharename=share["shi1_netname"][:-1]
- 544sharecomment=share["shi1_remark"][:-1]
- 545sharetype=share["shi1_type"]
- 546
- 547self.available_shares[sharename.lower()]={
- 548"name":sharename,
- 549"type":STYPE_MASK(sharetype),
- 550"rawtype":sharetype,
- 551"comment":sharecomment
- 552}
- 553else:
- 554print("[!] Error: SMBSession.smbClient is None.")
- 555
- 556returnself.available_shares
- 557
- 558defmkdir(self,path=None):
- 559"""
- 560 Creates a directory at the specified path on the SMB share.
- 561
- 562 This method takes a path and attempts to create the directory structure on the SMB share. If the path includes
- 563 nested directories, it will create each directory in the sequence. If a directory already exists, it will skip
- 564 the creation for that directory without raising an error.
- 565
- 566 Args:
- 567 path (str, optional): The full path of the directory to create on the SMB share. Defaults to None.
- 568
- 569 Note:
- 570 The path should use forward slashes ('/') which will be converted to backslashes (ntpath.sep) for SMB compatibility.
- 571 """
- 572
- 573ifpathisnotNone:
- 574# Prepare path
- 575path=path.replace('/',ntpath.sep)
- 576ifntpath.sepinpath:
- 577path=path.strip(ntpath.sep).split(ntpath.sep)
- 578else:
- 579path=[path]
+ 494ifshareandself.smb_shareisnotNone:
+ 495share_name=self.available_shares.get(self.smb_share.lower(),"")["name"]
+ 496share_comment=self.available_shares.get(self.smb_share.lower(),"")["comment"]
+ 497share_type=self.available_shares.get(self.smb_share.lower(),"")["type"]
+ 498share_type=', '.join([s.replace("STYPE_","")forsinshare_type])
+ 499share_rawtype=self.available_shares.get(self.smb_share.lower(),"")["rawtype"]
+ 500ifself.config.no_colors:
+ 501self.logger.print("\n[+] Share:")
+ 502self.logger.print(" ├─ Name ──────────── : %s"%(share_name))
+ 503self.logger.print(" ├─ Description ───── : %s"%(share_comment))
+ 504self.logger.print(" ├─ Type ──────────── : %s"%(share_type))
+ 505self.logger.print(" └─ Raw type value ── : %s"%(share_rawtype))
+ 506else:
+ 507self.logger.print("\n[+] Share:")
+ 508self.logger.print(" ├─ \x1b[94mName\x1b[0m \x1b[90m────────────\x1b[0m : \x1b[93m%s\x1b[0m"%(share_name))
+ 509self.logger.print(" ├─ \x1b[94mDescription\x1b[0m \x1b[90m─────\x1b[0m : \x1b[93m%s\x1b[0m"%(share_comment))
+ 510self.logger.print(" ├─ \x1b[94mType\x1b[0m \x1b[90m────────────\x1b[0m : \x1b[93m%s\x1b[0m"%(share_type))
+ 511self.logger.print(" └─ \x1b[94mRaw type value\x1b[0m \x1b[90m──\x1b[0m : \x1b[93m%s\x1b[0m"%(share_rawtype))
+ 512
+ 513deflist_contents(self,path=None):
+ 514"""
+ 515 Lists the contents of a specified directory on the SMB share.
+ 516
+ 517 This method retrieves the contents of a directory specified by `shareName` and `path`. If `shareName` or `path`
+ 518 is not provided, it defaults to the instance's current SMB share or path. The method returns a dictionary with
+ 519 the long names of the files and directories as keys and their respective SMB entry objects as values.
+ 520
+ 521 Args:
+ 522 shareName (str, optional): The name of the SMB share. Defaults to the current SMB share if None.
+ 523 path (str, optional): The directory path to list contents from. Defaults to the current path if None.
+ 524
+ 525 Returns:
+ 526 dict: A dictionary with file and directory names as keys and their SMB entry objects as values.
+ 527 """
+ 528
+ 529dest_path=[self.smb_cwd.rstrip(ntpath.sep),]
+ 530ifpathisnotNoneandlen(path)>0:
+ 531dest_path.append(path.rstrip(ntpath.sep))
+ 532dest_path.append('*')
+ 533path=ntpath.sep.join(dest_path)
+ 534
+ 535contents={}
+ 536entries=self.smbClient.listPath(
+ 537shareName=self.smb_share,
+ 538path=path
+ 539)
+ 540forentryinentries:
+ 541contents[entry.get_longname()]=entry
+ 542
+ 543returncontents
+ 544
+ 545deflist_shares(self):
+ 546"""
+ 547 Lists all the shares available on the connected SMB server.
+ 548
+ 549 This method queries the SMB server to retrieve a list of all available shares. It populates the `shares` dictionary
+ 550 with key-value pairs where the key is the share name and the value is a dictionary containing details about the share
+ 551 such as its name, type, raw type, and any comments associated with the share.
+ 552
+ 553 Returns:
+ 554 dict: A dictionary containing information about each share available on the server.
+ 555 """
+ 556
+ 557self.available_shares={}
+ 558
+ 559ifself.connected:
+ 560ifself.smbClientisnotNone:
+ 561resp=self.smbClient.listShares()
+ 562
+ 563forshareinresp:
+ 564# SHARE_INFO_1 structure (lmshare.h)
+ 565# https://learn.microsoft.com/en-us/windows/win32/api/lmshare/ns-lmshare-share_info_1
+ 566sharename=share["shi1_netname"][:-1]
+ 567sharecomment=share["shi1_remark"][:-1]
+ 568sharetype=share["shi1_type"]
+ 569
+ 570self.available_shares[sharename.lower()]={
+ 571"name":sharename,
+ 572"type":STYPE_MASK(sharetype),
+ 573"rawtype":sharetype,
+ 574"comment":sharecomment
+ 575}
+ 576else:
+ 577self.logger.error("Error: SMBSession.smbClient is None.")
+ 578
+ 579returnself.available_shares 580
- 581# Create each dir in the path
- 582fordepthinrange(1,len(path)+1):
- 583tmp_path=ntpath.sep.join(path[:depth])
- 584try:
- 585self.smbClient.createDirectory(
- 586shareName=self.smb_share,
- 587pathName=ntpath.normpath(self.smb_cwd+ntpath.sep+tmp_path+ntpath.sep)
- 588)
- 589exceptimpacket.smbconnection.SessionErroraserr:
- 590iferr.getErrorCode()==0xc0000035:
- 591# STATUS_OBJECT_NAME_COLLISION
- 592# Remote directory already created, this is normal
- 593# Src: https://github.com/fortra/impacket/blob/269ce69872f0e8f2188a80addb0c39fedfa6dcb8/impacket/nt_errors.py#L268C9-L268C19
- 594pass
- 595else:
- 596print("[!] Failed to create directory '%s': %s"%(tmp_path,err))
- 597ifself.config.debug:
- 598traceback.print_exc()
- 599else:
- 600pass
- 601
- 602defmount(self,local_mount_point,remote_path):
- 603"""
- 604 Generates the command to mount an SMB share on different platforms.
- 605
- 606 This method takes the local mount point and the remote path of the SMB share and generates the appropriate mount command based on the platform.
- 607 It constructs the mount command using the provided parameters and executes it using the os.system() function.
- 608
- 609 Args:
- 610 local_mount_point (str): The local directory where the SMB share will be mounted.
- 611 remote_path (str): The remote path on the SMB share to be mounted.
- 612
- 613 Note:
- 614 - For Windows platform, the command uses 'net use' to mount the share.
- 615 - For Linux platform, the command uses 'mount' to mount the share.
- 616 - For macOS platform, the command uses 'mount_smbfs' to mount the share.
- 617 - If the platform is not supported, an error message is displayed.
- 618
- 619 Returns:
- 620 None
- 621 """
- 622
- 623ifnotos.path.exists(local_mount_point):
- 624pass
- 625
- 626ifsys.platform.startswith('win'):
- 627remote_path=remote_path.replace('/',ntpath.sep)
- 628command=f"net use {local_mount_point}\\\\{self.address}\\{self.smb_share}\\{remote_path}"
- 629
- 630elifsys.platform.startswith('linux'):
- 631remote_path=remote_path.replace(ntpath.sep,'/')
- 632command=f"mount -t cifs //{self.address}/{self.smb_share}/{remote_path}{local_mount_point} -o username={self.username},password={self.password}"
- 633
- 634elifsys.platform.startswith('darwin'):
- 635remote_path=remote_path.replace(ntpath.sep,'/')
- 636command=f"mount_smbfs //{self.username}:{self.password}@{self.address}/{self.smb_share}/{remote_path}{local_mount_point}"
- 637
- 638else:
- 639command=None
- 640print("[!] Unsupported platform for mounting SMB share.")
- 641
- 642ifcommandisnotNone:
- 643ifself.config.debug:
- 644print("[debug] Executing: %s"%command)
- 645os.system(command)
- 646
- 647defpath_exists(self,path=None):
- 648"""
- 649 Checks if the specified path exists on the SMB share.
- 650
- 651 This method determines if a given path exists on the SMB share by attempting to list the contents of the path.
- 652 If the path listing is successful and returns one or more entries, the path is considered to exist.
- 653
- 654 Args:
- 655 path (str, optional): The path to check on the SMB share. Defaults to None.
- 656
- 657 Returns:
- 658 bool: True if the path exists, False otherwise or if an error occurs.
- 659 """
- 660
- 661ifpathisnotNone:
- 662path=path.replace('*','')
- 663try:
- 664contents=self.smbClient.listPath(
- 665shareName=self.smb_share,
- 666path=ntpath.normpath(self.smb_cwd+ntpath.sep+path+ntpath.sep)
- 667)
- 668return(len(contents)!=0)
- 669exceptExceptionase:
- 670returnFalse
- 671else:
- 672returnFalse
- 673
- 674defpath_isdir(self,pathFromRoot=None):
- 675"""
- 676 Checks if the specified path is a directory on the SMB share.
- 677
- 678 This method determines if a given path corresponds to a directory on the SMB share. It does this by listing the
- 679 contents of the path and filtering for entries that match the basename of the path and are marked as directories.
- 680
- 681 Args:
- 682 path (str, optional): The path to check on the SMB share. Defaults to None.
+ 581defmkdir(self,path=None):
+ 582"""
+ 583 Creates a directory at the specified path on the SMB share.
+ 584
+ 585 This method takes a path and attempts to create the directory structure on the SMB share. If the path includes
+ 586 nested directories, it will create each directory in the sequence. If a directory already exists, it will skip
+ 587 the creation for that directory without raising an error.
+ 588
+ 589 Args:
+ 590 path (str, optional): The full path of the directory to create on the SMB share. Defaults to None.
+ 591
+ 592 Note:
+ 593 The path should use forward slashes ('/') which will be converted to backslashes (ntpath.sep) for SMB compatibility.
+ 594 """
+ 595
+ 596ifpathisnotNone:
+ 597# Prepare path
+ 598path=path.replace('/',ntpath.sep)
+ 599ifntpath.sepinpath:
+ 600path=path.strip(ntpath.sep).split(ntpath.sep)
+ 601else:
+ 602path=[path]
+ 603
+ 604# Create each dir in the path
+ 605fordepthinrange(1,len(path)+1):
+ 606tmp_path=ntpath.sep.join(path[:depth])
+ 607try:
+ 608self.smbClient.createDirectory(
+ 609shareName=self.smb_share,
+ 610pathName=ntpath.normpath(self.smb_cwd+ntpath.sep+tmp_path+ntpath.sep)
+ 611)
+ 612exceptimpacket.smbconnection.SessionErroraserr:
+ 613iferr.getErrorCode()==0xc0000035:
+ 614# STATUS_OBJECT_NAME_COLLISION
+ 615# Remote directory already created, this is normal
+ 616# Src: https://github.com/fortra/impacket/blob/269ce69872f0e8f2188a80addb0c39fedfa6dcb8/impacket/nt_errors.py#L268C9-L268C19
+ 617pass
+ 618else:
+ 619self.logger.error("Failed to create directory '%s': %s"%(tmp_path,err))
+ 620ifself.config.debug:
+ 621traceback.print_exc()
+ 622else:
+ 623pass
+ 624
+ 625defmount(self,local_mount_point,remote_path):
+ 626"""
+ 627 Generates the command to mount an SMB share on different platforms.
+ 628
+ 629 This method takes the local mount point and the remote path of the SMB share and generates the appropriate mount command based on the platform.
+ 630 It constructs the mount command using the provided parameters and executes it using the os.system() function.
+ 631
+ 632 Args:
+ 633 local_mount_point (str): The local directory where the SMB share will be mounted.
+ 634 remote_path (str): The remote path on the SMB share to be mounted.
+ 635
+ 636 Note:
+ 637 - For Windows platform, the command uses 'net use' to mount the share.
+ 638 - For Linux platform, the command uses 'mount' to mount the share.
+ 639 - For macOS platform, the command uses 'mount_smbfs' to mount the share.
+ 640 - If the platform is not supported, an error message is displayed.
+ 641
+ 642 Returns:
+ 643 None
+ 644 """
+ 645
+ 646ifnotos.path.exists(local_mount_point):
+ 647pass
+ 648
+ 649ifsys.platform.startswith('win'):
+ 650remote_path=remote_path.replace('/',ntpath.sep)
+ 651command=f"net use {local_mount_point}\\\\{self.host}\\{self.smb_share}\\{remote_path}"
+ 652
+ 653elifsys.platform.startswith('linux'):
+ 654remote_path=remote_path.replace(ntpath.sep,'/')
+ 655command=f"mount -t cifs //{self.host}/{self.smb_share}/{remote_path}{local_mount_point} -o username={self.credentials.username},password={self.credentials.password}"
+ 656
+ 657elifsys.platform.startswith('darwin'):
+ 658remote_path=remote_path.replace(ntpath.sep,'/')
+ 659command=f"mount_smbfs //{self.credentials.username}:{self.credentials.password}@{self.host}/{self.smb_share}/{remote_path}{local_mount_point}"
+ 660
+ 661else:
+ 662command=None
+ 663self.logger.error("Unsupported platform for mounting SMB share.")
+ 664
+ 665ifcommandisnotNone:
+ 666ifself.config.debug:
+ 667self.logger.debug("Executing: %s"%command)
+ 668os.system(command)
+ 669
+ 670defpath_exists(self,path=None):
+ 671"""
+ 672 Checks if the specified path exists on the SMB share.
+ 673
+ 674 This method determines if a given path exists on the SMB share by attempting to list the contents of the path.
+ 675 If the path listing is successful and returns one or more entries, the path is considered to exist.
+ 676
+ 677 Args:
+ 678 path (str, optional): The path to check on the SMB share. Defaults to None.
+ 679
+ 680 Returns:
+ 681 bool: True if the path exists, False otherwise or if an error occurs.
+ 682 """ 683
- 684 Returns:
- 685 bool: True if the path is a directory, False otherwise or if an error occurs.
- 686 """
- 687
- 688ifpathFromRootisnotNone:
- 689# Replace slashes if any
- 690path=pathFromRoot.replace('/',ntpath.sep)
- 691
- 692# Strip wildcards to avoid injections
- 693path=path.replace('*','')
- 694
- 695# Normalize path and strip leading backslash
- 696path=ntpath.normpath(path+ntpath.sep).lstrip(ntpath.sep)
- 697
- 698ifpath.strip()in['','.','..']:
- 699# By defininition they exist on the filesystem
- 700returnTrue
- 701else:
- 702try:
- 703contents=self.smbClient.listPath(
- 704shareName=self.smb_share,
- 705path=path+'*'
- 706)
- 707# Filter on directories
- 708contents=[
- 709cforcincontents
- 710ifc.get_longname()==ntpath.basename(path)andc.is_directory()
- 711]
- 712return(len(contents)!=0)
- 713exceptExceptionase:
- 714returnFalse
- 715else:
- 716returnFalse
+ 684ifpathisnotNone:
+ 685path=path.replace('*','')
+ 686path=path.replace('/',ntpath.sep)
+ 687try:
+ 688contents=self.smbClient.listPath(
+ 689shareName=self.smb_share,
+ 690path=ntpath.normpath(self.smb_cwd+ntpath.sep+path+ntpath.sep)
+ 691)
+ 692return(len(contents)!=0)
+ 693exceptExceptionase:
+ 694returnFalse
+ 695else:
+ 696returnFalse
+ 697
+ 698defpath_isdir(self,pathFromRoot=None):
+ 699"""
+ 700 Checks if the specified path is a directory on the SMB share.
+ 701
+ 702 This method determines if a given path corresponds to a directory on the SMB share. It does this by listing the
+ 703 contents of the path and filtering for entries that match the basename of the path and are marked as directories.
+ 704
+ 705 Args:
+ 706 path (str, optional): The path to check on the SMB share. Defaults to None.
+ 707
+ 708 Returns:
+ 709 bool: True if the path is a directory, False otherwise or if an error occurs.
+ 710 """
+ 711
+ 712ifpathFromRootisnotNone:
+ 713# Strip wildcards to avoid injections
+ 714path=pathFromRoot.replace('*','')
+ 715# Replace slashes if any
+ 716path=path.replace('/',ntpath.sep) 717
- 718defpath_isfile(self,path=None):
- 719"""
- 720 Checks if the specified path is a file on the SMB share.
- 721
- 722 This method determines if a given path corresponds to a file on the SMB share. It does this by listing the
- 723 contents of the path and filtering for entries that match the basename of the path and are not marked as directories.
- 724
- 725 Args:
- 726 path (str, optional): The path to check on the SMB share. Defaults to None.
- 727
- 728 Returns:
- 729 bool: True if the path is a file, False otherwise or if an error occurs.
- 730 """
- 731
- 732ifpathisnotNone:
- 733path=path.replace('*','')
- 734search_dir=ntpath.normpath(self.smb_cwd+ntpath.sep+path)
- 735search_dir=ntpath.dirname(search_dir)+ntpath.sep+'*'
- 736try:
- 737contents=self.smbClient.listPath(
- 738shareName=self.smb_share,
- 739path=search_dir
- 740)
- 741# Filter on files
- 742contents=[
- 743cforcincontents
- 744ifc.get_longname()==ntpath.basename(path)andnotc.is_directory()
- 745]
- 746return(len(contents)!=0)
- 747exceptExceptionase:
- 748returnFalse
- 749else:
- 750returnFalse
- 751
- 752defping_smb_session(self):
- 753"""
- 754 Tests the connectivity to the SMB server by sending an echo command.
- 755
- 756 This method attempts to send an echo command to the SMB server to check if the session is still active.
- 757 It updates the `connected` attribute of the class based on the success or failure of the echo command.
- 758
- 759 Returns:
- 760 bool: True if the echo command succeeds (indicating the session is active), False otherwise.
- 761 """
- 762
- 763try:
- 764self.smbClient.getSMBServer().echo()
- 765exceptExceptionase:
- 766self.connected=False
- 767returnself.connected
- 768
- 769defput_file(self,localpath=None):
- 770"""
- 771 Uploads a single file to the SMB share.
- 772
- 773 This method takes a local file path, opens the file, and uploads it to the SMB share at the specified path.
- 774 It handles exceptions such as broken pipe errors or keyboard interrupts by closing and reinitializing the SMB session.
- 775 General exceptions are caught and logged, with a traceback provided if debugging is enabled.
- 776
- 777 Args:
- 778 localpath (str, optional): The local file path of the file to be uploaded. Defaults to None.
- 779 """
- 780
- 781# Parse path
- 782localpath=localpath.replace('/',os.path.sep)
- 783ifos.path.sepinlocalpath:
- 784tmp_search_path=os.path.normpath(os.getcwd()+os.path.sep+os.path.dirname(localpath))
- 785else:
- 786tmp_search_path=os.path.normpath(os.getcwd()+os.path.sep)
- 787# Parse filename
- 788filename=os.path.basename(localpath)
- 789
- 790# Search for the file
- 791matches=os.listdir(tmp_search_path)
- 792# Filter the entries
- 793matching_entries=[]
- 794forentryinmatches:
- 795ifentry==filename:
- 796matching_entries.append(entry)
- 797elif'*'infilename:
- 798regexp=filename.replace('.','\\.').replace('*','.*')
- 799ifre.match(regexp,entry):
- 800matching_entries.append(entry)
- 801
- 802matching_entries=sorted(list(set(matching_entries)))
+ 718# Normalize path and strip leading backslash
+ 719path=ntpath.normpath(path+ntpath.sep).lstrip(ntpath.sep)
+ 720
+ 721ifpath.strip()in['','.','..']:
+ 722# By defininition they exist on the filesystem
+ 723returnTrue
+ 724else:
+ 725try:
+ 726contents=self.smbClient.listPath(
+ 727shareName=self.smb_share,
+ 728path=path+'*'
+ 729)
+ 730# Filter on directories
+ 731contents=[
+ 732cforcincontents
+ 733ifc.get_longname()==ntpath.basename(path)andc.is_directory()
+ 734]
+ 735return(len(contents)!=0)
+ 736exceptExceptionase:
+ 737returnFalse
+ 738else:
+ 739returnFalse
+ 740
+ 741defpath_isfile(self,pathFromRoot=None):
+ 742"""
+ 743 Checks if the specified path is a file on the SMB share.
+ 744
+ 745 This method determines if a given path corresponds to a file on the SMB share. It does this by listing the
+ 746 contents of the path and filtering for entries that match the basename of the path and are not marked as directories.
+ 747
+ 748 Args:
+ 749 path (str, optional): The path to check on the SMB share. Defaults to None.
+ 750
+ 751 Returns:
+ 752 bool: True if the path is a file, False otherwise or if an error occurs.
+ 753 """
+ 754
+ 755ifpathFromRootisnotNone:
+ 756# Strip wildcards to avoid injections
+ 757path=pathFromRoot.replace('*','')
+ 758# Replace slashes if any
+ 759path=path.replace('/',ntpath.sep)
+ 760
+ 761# Normalize path and strip leading backslash
+ 762path=ntpath.normpath(path+ntpath.sep).lstrip(ntpath.sep)
+ 763
+ 764try:
+ 765contents=self.smbClient.listPath(
+ 766shareName=self.smb_share,
+ 767path=ntpath.dirname(path)+ntpath.sep+'*'
+ 768)
+ 769# Filter on files
+ 770contents=[
+ 771cforcincontents
+ 772ifc.get_longname()==ntpath.basename(path)andnotc.is_directory()
+ 773]
+ 774return(len(contents)!=0)
+ 775exceptExceptionase:
+ 776returnFalse
+ 777else:
+ 778returnFalse
+ 779
+ 780defput_file(self,localpath=None):
+ 781"""
+ 782 Uploads a single file to the SMB share.
+ 783
+ 784 This method takes a local file path, opens the file, and uploads it to the SMB share at the specified path.
+ 785 It handles exceptions such as broken pipe errors or keyboard interrupts by closing and reinitializing the SMB session.
+ 786 General exceptions are caught and logged, with a traceback provided if debugging is enabled.
+ 787
+ 788 Args:
+ 789 localpath (str, optional): The local file path of the file to be uploaded. Defaults to None.
+ 790 """
+ 791
+ 792# Parse path
+ 793localpath=localpath.replace('/',os.path.sep)
+ 794ifos.path.sepinlocalpath:
+ 795iflocalpath.startswith(os.path.sep):
+ 796# Absolute path
+ 797tmp_search_path=os.path.normpath(localpath)
+ 798else:
+ 799# Relative path
+ 800tmp_search_path=os.path.normpath(os.getcwd()+os.path.sep+os.path.dirname(localpath))
+ 801else:
+ 802tmp_search_path=os.path.normpath(os.getcwd()+os.path.sep) 803
- 804# Loop and upload
- 805forlocalpathinmatching_entries:
- 806ifos.path.exists(localpath):
- 807ifos.path.isfile(localpath):
- 808try:
- 809localfile=os.path.basename(localpath)
- 810f=LocalFileIO(
- 811mode="rb",
- 812path=localpath,
- 813debug=self.config.debug
- 814)
- 815self.smbClient.putFile(
- 816shareName=self.smb_share,
- 817pathName=ntpath.normpath(self.smb_cwd+ntpath.sep+localfile+ntpath.sep),
- 818callback=f.read
- 819)
- 820f.close()
- 821except(BrokenPipeError,KeyboardInterrupt)aserr:
- 822print("[!] Interrupted.")
- 823self.close_smb_session()
- 824self.init_smb_session()
- 825exceptExceptionaserr:
- 826print("[!] Failed to upload '%s': %s"%(localfile,err))
- 827ifself.config.debug:
- 828traceback.print_exc()
- 829else:
- 830# [!] The specified localpath is a directory. Use 'put -r <directory>' instead.
- 831pass
- 832else:
- 833# [!] The specified localpath does not exist.
- 834pass
- 835
- 836defput_file_recursively(self,localpath=None):
- 837"""
- 838 Recursively uploads files from a specified local directory to the SMB share.
- 839
- 840 This method walks through the given local directory and all its subdirectories, uploading each file to the
- 841 corresponding directory structure on the SMB share. It first checks if the local path is a directory. If it is,
- 842 it iterates over all files and directories within the local path, creating necessary directories on the SMB share
- 843 and uploading files. If the local path is not a directory, it prints an error message.
- 844
- 845 Args:
- 846 localpath (str, optional): The local directory path from which files will be uploaded. Defaults to None.
- 847 """
- 848
- 849ifos.path.exists(localpath):
- 850ifos.path.isfile(localpath):
- 851# Iterate over all files and directories within the local path
- 852local_files={}
- 853forroot,dirs,filesinos.walk(localpath):
- 854iflen(files)!=0:
- 855local_files[root]=files
- 856
- 857# Iterate over the found files
- 858forlocal_dir_pathinsorted(local_files.keys()):
- 859print("[>] Putting files of '%s'"%local_dir_path)
- 860
- 861# Create remote directory
- 862remote_dir_path=local_dir_path.replace(os.path.sep,ntpath.sep)
- 863self.mkdir(
- 864path=ntpath.normpath(self.smb_cwd+ntpath.sep+remote_dir_path+ntpath.sep)
- 865)
- 866
- 867forlocal_file_pathinlocal_files[local_dir_path]:
- 868try:
- 869f=LocalFileIO(
- 870mode="rb",
- 871path=local_dir_path+os.path.sep+local_file_path,
- 872debug=self.config.debug
- 873)
- 874self.smbClient.putFile(
- 875shareName=self.smb_share,
- 876pathName=ntpath.normpath(self.smb_cwd+ntpath.sep+remote_dir_path+ntpath.sep+local_file_path),
- 877callback=f.read
- 878)
- 879f.close()
+ 804# Parse filename
+ 805filename=os.path.basename(localpath)
+ 806
+ 807# Search for the file
+ 808matches=os.listdir(tmp_search_path)
+ 809# Filter the entries
+ 810matching_entries=[]
+ 811forentryinmatches:
+ 812ifentry==filename:
+ 813matching_entries.append(entry)
+ 814elif'*'infilename:
+ 815regexp=filename.replace('.','\\.').replace('*','.*')
+ 816ifre.match(regexp,entry):
+ 817matching_entries.append(entry)
+ 818
+ 819matching_entries=sorted(list(set(matching_entries)))
+ 820
+ 821# Loop and upload
+ 822forlocalpathinmatching_entries:
+ 823ifos.path.exists(localpath):
+ 824ifos.path.isfile(localpath):
+ 825try:
+ 826localfile=os.path.basename(localpath)
+ 827f=LocalFileIO(
+ 828mode="rb",
+ 829path=localpath,
+ 830debug=self.config.debug
+ 831)
+ 832self.smbClient.putFile(
+ 833shareName=self.smb_share,
+ 834pathName=ntpath.normpath(self.smb_cwd+ntpath.sep+localfile+ntpath.sep),
+ 835callback=f.read
+ 836)
+ 837f.close()
+ 838
+ 839except(BrokenPipeError,KeyboardInterrupt)aserr:
+ 840self.logger.error("Interrupted.")
+ 841self.close_smb_session()
+ 842self.init_smb_session()
+ 843
+ 844except(Exception,PermissionError)aserr:
+ 845f.set_error(message="[bold red]Failed uploading '%s': %s"%(f.path,err))
+ 846f.close(remove=False)
+ 847ifself.config.debug:
+ 848traceback.print_exc()
+ 849else:
+ 850# [!] The specified localpath is a directory. Use 'put -r <directory>' instead.
+ 851pass
+ 852else:
+ 853# [!] The specified localpath does not exist.
+ 854pass
+ 855
+ 856defput_file_recursively(self,localpath=None):
+ 857"""
+ 858 Recursively uploads files from a specified local directory to the SMB share.
+ 859
+ 860 This method walks through the given local directory and all its subdirectories, uploading each file to the
+ 861 corresponding directory structure on the SMB share. It first checks if the local path is a directory. If it is,
+ 862 it iterates over all files and directories within the local path, creating necessary directories on the SMB share
+ 863 and uploading files. If the local path is not a directory, it prints an error message.
+ 864
+ 865 Args:
+ 866 localpath (str, optional): The local directory path from which files will be uploaded. Defaults to None.
+ 867 """
+ 868
+ 869ifos.path.exists(localpath):
+ 870ifos.path.isdir(localpath):
+ 871# Iterate over all files and directories within the local path
+ 872local_files={}
+ 873forroot,dirs,filesinos.walk(localpath):
+ 874iflen(files)!=0:
+ 875local_files[root]=files
+ 876
+ 877# Iterate over the found files
+ 878forlocal_dir_pathinsorted(local_files.keys()):
+ 879self.logger.print("[>] Putting files of '%s'"%local_dir_path) 880
- 881exceptBrokenPipeErroraserr:
- 882f.set_error(message="[bold red]Failed uploading '%s': %s"%(f.path,err))
- 883f.close(remove=True)
- 884break
- 885exceptExceptionaserr:
- 886f.set_error(message="[bold red]Failed uploading '%s': %s"%(f.path,err))
- 887f.close(remove=True)
- 888else:
- 889print("[!] The specified localpath is a file. Use 'put <file>' instead.")
- 890else:
- 891print("[!] The specified localpath does not exist.")
- 892
- 893defrmdir(self,path=None):
- 894"""
- 895 Removes a directory from the SMB share at the specified path.
- 896
- 897 This method attempts to delete a directory located at the given path on the SMB share. If the operation fails,
- 898 it prints an error message indicating the failure and the reason. If debugging is enabled, it also prints
- 899 the stack trace of the exception.
+ 881# Create remote directory
+ 882remote_dir_path=local_dir_path.replace(os.path.sep,ntpath.sep)
+ 883self.mkdir(
+ 884path=ntpath.normpath(remote_dir_path+ntpath.sep)
+ 885)
+ 886
+ 887forlocal_file_pathinlocal_files[local_dir_path]:
+ 888try:
+ 889f=LocalFileIO(
+ 890mode="rb",
+ 891path=local_dir_path+os.path.sep+local_file_path,
+ 892debug=self.config.debug
+ 893)
+ 894self.smbClient.putFile(
+ 895shareName=self.smb_share,
+ 896pathName=ntpath.normpath(self.smb_cwd+ntpath.sep+remote_dir_path+ntpath.sep+local_file_path),
+ 897callback=f.read
+ 898)
+ 899f.close() 900
- 901 Args:
- 902 path (str, optional): The path of the directory to be removed on the SMB share. Defaults to None.
- 903 """
- 904try:
- 905self.smbClient.deleteDirectory(
- 906shareName=self.smb_share,
- 907pathName=ntpath.normpath(self.smb_cwd+ntpath.sep+path),
- 908)
- 909exceptExceptionaserr:
- 910print("[!] Failed to remove directory '%s': %s"%(path,err))
- 911ifself.config.debug:
- 912traceback.print_exc()
- 913
- 914defrm(self,path=None):
- 915"""
- 916 Removes a file from the SMB share at the specified path.
- 917
- 918 This method attempts to delete a file located at the given path on the SMB share. If the operation fails,
- 919 it prints an error message indicating the failure and the reason. If debugging is enabled, it also prints
- 920 the stack trace of the exception.
- 921
- 922 Args:
- 923 path (str, optional): The path of the file to be removed on the SMB share. Defaults to None.
- 924 """
- 925
- 926# Parse path
- 927path=path.replace('/',ntpath.sep)
- 928ifntpath.sepinpath:
- 929tmp_search_path=ntpath.normpath(self.smb_cwd+ntpath.sep+ntpath.dirname(path))
- 930else:
- 931tmp_search_path=ntpath.normpath(self.smb_cwd+ntpath.sep)
- 932# Parse filename
- 933filename=ntpath.basename(path)
- 934
- 935# Search for the file
- 936matches=self.smbClient.listPath(
- 937shareName=self.smb_share,
- 938path=tmp_search_path+ntpath.sep+'*'
- 939)
+ 901except(BrokenPipeError,KeyboardInterrupt)aserr:
+ 902self.logger.error("Interrupted.")
+ 903self.close_smb_session()
+ 904self.init_smb_session()
+ 905
+ 906except(Exception,PermissionError)aserr:
+ 907f.set_error(message="[bold red]Failed uploading '%s': %s"%(f.path,err))
+ 908f.close(remove=False)
+ 909ifself.config.debug:
+ 910traceback.print_exc()
+ 911else:
+ 912self.logger.error("The specified localpath is a file. Use 'put <file>' instead.")
+ 913else:
+ 914self.logger.error("The specified localpath does not exist.")
+ 915
+ 916defread_file(self,path=None):
+ 917"""
+ 918 Reads a file from the SMB share.
+ 919
+ 920 This method attempts to read the contents of a file specified by the `path` parameter from the SMB share.
+ 921 It constructs the full path to the file, checks if the path is a valid file, and then reads the file content
+ 922 into a byte stream which is returned to the caller.
+ 923
+ 924 Args:
+ 925 path (str, optional): The path of the file to be read from the SMB share. Defaults to None.
+ 926
+ 927 Returns:
+ 928 bytes: The content of the file as a byte stream, or None if the file does not exist or an error occurs.
+ 929 """
+ 930
+ 931ifself.path_isfile(pathFromRoot=path):
+ 932path=path.replace('/',ntpath.sep)
+ 933ifpath.startswith(ntpath.sep):
+ 934# Absolute path
+ 935tmp_file_path=ntpath.normpath(path)
+ 936else:
+ 937# Relative path
+ 938tmp_file_path=ntpath.normpath(self.smb_cwd+ntpath.sep+path)
+ 939tmp_file_path=tmp_file_path.lstrip(ntpath.sep) 940
- 941# Filter the entries
- 942matching_entries=[]
- 943forentryinmatches:
- 944ifentry.is_directory():
- 945# Skip directories
- 946continue
- 947ifentry.get_longname()==filename:
- 948matching_entries.append(entry)
- 949elif'*'infilename:
- 950regexp=filename.replace('.','\\.').replace('*','.*')
- 951ifre.match(regexp,entry.get_longname()):
- 952matching_entries.append(entry)
- 953
- 954matching_entries=sorted(list(set(matching_entries)),key=lambdax:x.get_longname())
- 955
- 956forentryinmatching_entries:
- 957try:
- 958self.smbClient.deleteFile(
- 959shareName=self.smb_share,
- 960pathName=ntpath.normpath(tmp_search_path+ntpath.sep+entry.get_longname()),
- 961)
- 962exceptExceptionaserr:
- 963print("[!] Failed to remove file '%s': %s"%(path,err))
- 964ifself.config.debug:
- 965traceback.print_exc()
- 966
- 967deftree(self,path=None):
- 968"""
- 969 Recursively lists the directory structure of the SMB share starting from the specified path.
- 970
- 971 This function prints a visual representation of the directory tree of the remote SMB share. It uses
- 972 recursion to navigate through directories and lists all files and subdirectories in each directory.
- 973 The output is color-coded and formatted to enhance readability, with directories highlighted in cyan.
+ 941fh=io.BytesIO()
+ 942try:
+ 943# opening the files in streams instead of mounting shares allows
+ 944# for running the script from unprivileged containers
+ 945self.smbClient.getFile(self.smb_share,tmp_file_path,fh.write)
+ 946exceptimpacket.smbconnection.SessionErrorase:
+ 947returnNone
+ 948rawdata=fh.getvalue()
+ 949fh.close()
+ 950returnrawdata
+ 951else:
+ 952returnNone
+ 953
+ 954defrmdir(self,path=None):
+ 955"""
+ 956 Removes a directory from the SMB share at the specified path.
+ 957
+ 958 This method attempts to delete a directory located at the given path on the SMB share. If the operation fails,
+ 959 it prints an error message indicating the failure and the reason. If debugging is enabled, it also prints
+ 960 the stack trace of the exception.
+ 961
+ 962 Args:
+ 963 path (str, optional): The path of the directory to be removed on the SMB share. Defaults to None.
+ 964 """
+ 965try:
+ 966self.smbClient.deleteDirectory(
+ 967shareName=self.smb_share,
+ 968pathName=ntpath.normpath(self.smb_cwd+ntpath.sep+path),
+ 969)
+ 970exceptExceptionaserr:
+ 971self.logger.error("Failed to remove directory '%s': %s"%(path,err))
+ 972ifself.config.debug:
+ 973traceback.print_exc() 974
- 975 Args:
- 976 path (str, optional): The starting path on the SMB share from which to begin listing the tree.
- 977 Defaults to the root of the current share.
- 978 """
- 979
- 980defrecurse_action(base_dir="",path=[],prompt=[]):
- 981bars=["│ ","├── ","└── "]
+ 975defrm(self,path=None):
+ 976"""
+ 977 Removes a file from the SMB share at the specified path.
+ 978
+ 979 This method attempts to delete a file located at the given path on the SMB share. If the operation fails,
+ 980 it prints an error message indicating the failure and the reason. If debugging is enabled, it also prints
+ 981 the stack trace of the exception. 982
- 983remote_smb_path=ntpath.normpath(base_dir+ntpath.sep+ntpath.sep.join(path))
- 984
- 985entries=[]
- 986try:
- 987entries=self.smbClient.listPath(
- 988shareName=self.smb_share,
- 989path=remote_smb_path+'\\*'
- 990)
- 991exceptimpacket.smbconnection.SessionErroraserr:
- 992code,const,text=err.getErrorCode(),err.getErrorString()[0],err.getErrorString()[1]
- 993errmsg="Error 0x%08x (%s): %s"%(code,const,text)
- 994ifself.config.no_colors:
- 995print("%s%s"%(''.join(prompt+[bars[2]]),errmsg))
- 996else:
- 997print("%s\x1b[1;91m%s\x1b[0m"%(''.join(prompt+[bars[2]]),errmsg))
- 998return
- 999
-1000entries=[eforeinentriesife.get_longname()notin[".",".."]]
-1001entries=sorted(entries,key=lambdax:x.get_longname())
-1002
-1003#
-1004iflen(entries)>1:
-1005index=0
-1006forentryinentries:
-1007index+=1
-1008# This is the first entry
-1009ifindex==0:
-1010ifentry.is_directory():
-1011ifself.config.no_colors:
-1012print("%s%s\\"%(''.join(prompt+[bars[1]]),entry.get_longname()))
-1013else:
-1014print("%s\x1b[1;96m%s\x1b[0m\\"%(''.join(prompt+[bars[1]]),entry.get_longname()))
-1015recurse_action(
-1016base_dir=base_dir,
-1017path=path+[entry.get_longname()],
-1018prompt=prompt+["│ "]
-1019)
-1020else:
-1021ifself.config.no_colors:
-1022print("%s%s"%(''.join(prompt+[bars[1]]),entry.get_longname()))
-1023else:
-1024print("%s\x1b[1m%s\x1b[0m"%(''.join(prompt+[bars[1]]),entry.get_longname()))
-1025
-1026# This is the last entry
-1027elifindex==len(entries):
-1028ifentry.is_directory():
-1029ifself.config.no_colors:
-1030print("%s%s\\"%(''.join(prompt+[bars[2]]),entry.get_longname()))
-1031else:
-1032print("%s\x1b[1;96m%s\x1b[0m\\"%(''.join(prompt+[bars[2]]),entry.get_longname()))
-1033recurse_action(
-1034base_dir=base_dir,
-1035path=path+[entry.get_longname()],
-1036prompt=prompt+[" "]
-1037)
-1038else:
-1039ifself.config.no_colors:
-1040print("%s%s"%(''.join(prompt+[bars[2]]),entry.get_longname()))
-1041else:
-1042print("%s\x1b[1m%s\x1b[0m"%(''.join(prompt+[bars[2]]),entry.get_longname()))
-1043
-1044# These are entries in the middle
-1045else:
-1046ifentry.is_directory():
-1047ifself.config.no_colors:
-1048print("%s%s\\"%(''.join(prompt+[bars[1]]),entry.get_longname()))
-1049else:
-1050print("%s\x1b[1;96m%s\x1b[0m\\"%(''.join(prompt+[bars[1]]),entry.get_longname()))
-1051recurse_action(
-1052base_dir=base_dir,
-1053path=path+[entry.get_longname()],
-1054prompt=prompt+["│ "]
-1055)
-1056else:
-1057ifself.config.no_colors:
-1058print("%s%s"%(''.join(prompt+[bars[1]]),entry.get_longname()))
-1059else:
-1060print("%s\x1b[1m%s\x1b[0m"%(''.join(prompt+[bars[1]]),entry.get_longname()))
-1061
-1062#
-1063eliflen(entries)==1:
-1064entry=entries[0]
-1065ifentry.is_directory():
-1066ifself.config.no_colors:
-1067print("%s%s\\"%(''.join(prompt+[bars[2]]),entry.get_longname()))
-1068else:
-1069print("%s\x1b[1;96m%s\x1b[0m\\"%(''.join(prompt+[bars[2]]),entry.get_longname()))
-1070recurse_action(
-1071base_dir=base_dir,
-1072path=path+[entry.get_longname()],
-1073prompt=prompt+[" "]
-1074)
-1075else:
-1076ifself.config.no_colors:
-1077print("%s%s"%(''.join(prompt+[bars[2]]),entry.get_longname()))
-1078else:
-1079print("%s\x1b[1m%s\x1b[0m"%(''.join(prompt+[bars[2]]),entry.get_longname()))
-1080
-1081# Entrypoint
-1082try:
-1083ifself.config.no_colors:
-1084print("%s\\"%path)
-1085else:
-1086print("\x1b[1;96m%s\x1b[0m\\"%path)
-1087recurse_action(
-1088base_dir=self.smb_cwd,
-1089path=[path],
-1090prompt=[""]
-1091)
-1092except(BrokenPipeError,KeyboardInterrupt)ase:
-1093print("[!] Interrupted.")
-1094self.close_smb_session()
-1095self.init_smb_session()
-1096
-1097defumount(self,local_mount_point):
-1098"""
-1099 Unmounts the specified local mount point of the remote share.
-1100
-1101 This method unmounts the specified local mount point of the remote share based on the platform.
-1102 It supports Windows, Linux, and macOS platforms for unmounting.
-1103
-1104 Parameters:
-1105 local_mount_point (str): The local mount point to unmount.
-1106
-1107 Raises:
-1108 None
-1109 """
-1110
-1111ifos.path.exists(local_mount_point):
-1112ifsys.platform.startswith('win'):
-1113command=f"net use {local_mount_point} /delete"
-1114
-1115elifsys.platform.startswith('linux')orsys.platform.startswith('darwin'):
-1116command=f"umount {local_mount_point}"
-1117
-1118else:
-1119command=None
-1120print("[!] Unsupported platform for unmounting SMB share.")
-1121
-1122ifcommandisnotNone:
-1123ifself.config.debug:
-1124print("[debug] Executing: %s"%command)
-1125os.system(command)
-1126else:
-1127print("[!] Cannot unmount a non existing path.")
-1128
-1129# Other functions
-1130
-1131deftest_rights(self,sharename):
-1132"""
-1133 Tests the read and write access rights of the current SMB session.
-1134
-1135 This method checks the read and write access rights of the current SMB session by attempting to list paths and create/delete temporary directories.
-1136
-1137 Returns:
-1138 dict: A dictionary containing the read and write access rights status.
-1139 - "readable" (bool): Indicates if the session has read access rights.
-1140 - "writable" (bool): Indicates if the session has write access rights.
-1141 """
-1142
-1143# Restore the current share
-1144current_share=self.smb_share
-1145self.set_share(shareName=sharename)
-1146
-1147access_rights={"readable":False,"writable":False}
-1148try:
-1149self.smbClient.listPath(self.smb_share,'*',password=None)
-1150access_rights["readable"]=True
-1151exceptimpacket.smbconnection.SessionErrorase:
-1152access_rights["readable"]=False
-1153
-1154try:
-1155temp_dir=ntpath.normpath("\\"+''.join([random.choice("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPRSTUVWXYZ0123456759")forkinrange(16)]))
-1156self.smbClient.createDirectory(self.smb_share,temp_dir)
-1157self.smbClient.deleteDirectory(self.smb_share,temp_dir)
-1158access_rights["writable"]=True
-1159exceptimpacket.smbconnection.SessionErrorase:
-1160access_rights["writable"]=False
+ 983 Args:
+ 984 path (str, optional): The path of the file to be removed on the SMB share. Defaults to None.
+ 985 """
+ 986
+ 987# Parse path
+ 988path=path.replace('/',ntpath.sep)
+ 989ifntpath.sepinpath:
+ 990tmp_search_path=ntpath.normpath(self.smb_cwd+ntpath.sep+ntpath.dirname(path))
+ 991else:
+ 992tmp_search_path=ntpath.normpath(self.smb_cwd+ntpath.sep)
+ 993# Parse filename
+ 994filename=ntpath.basename(path)
+ 995
+ 996# Search for the file
+ 997matches=self.smbClient.listPath(
+ 998shareName=self.smb_share,
+ 999path=tmp_search_path+ntpath.sep+'*'
+1000)
+1001
+1002# Filter the entries
+1003matching_entries=[]
+1004forentryinmatches:
+1005ifentry.is_directory():
+1006# Skip directories
+1007continue
+1008ifentry.get_longname()==filename:
+1009matching_entries.append(entry)
+1010elif'*'infilename:
+1011regexp=filename.replace('.','\\.').replace('*','.*')
+1012ifre.match(regexp,entry.get_longname()):
+1013matching_entries.append(entry)
+1014
+1015matching_entries=sorted(list(set(matching_entries)),key=lambdax:x.get_longname())
+1016
+1017forentryinmatching_entries:
+1018try:
+1019self.smbClient.deleteFile(
+1020shareName=self.smb_share,
+1021pathName=ntpath.normpath(tmp_search_path+ntpath.sep+entry.get_longname()),
+1022)
+1023exceptExceptionaserr:
+1024self.logger.error("Failed to remove file '%s': %s"%(path,err))
+1025ifself.config.debug:
+1026traceback.print_exc()
+1027
+1028deftree(self,path=None):
+1029"""
+1030 Recursively lists the directory structure of the SMB share starting from the specified path.
+1031
+1032 This function prints a visual representation of the directory tree of the remote SMB share. It uses
+1033 recursion to navigate through directories and lists all files and subdirectories in each directory.
+1034 The output is color-coded and formatted to enhance readability, with directories highlighted in cyan.
+1035
+1036 Args:
+1037 path (str, optional): The starting path on the SMB share from which to begin listing the tree.
+1038 Defaults to the root of the current share.
+1039 """
+1040
+1041defrecurse_action(base_dir="",path=[],prompt=[]):
+1042bars=["│ ","├── ","└── "]
+1043
+1044remote_smb_path=ntpath.normpath(base_dir+ntpath.sep+ntpath.sep.join(path))
+1045
+1046entries=[]
+1047try:
+1048entries=self.smbClient.listPath(
+1049shareName=self.smb_share,
+1050path=remote_smb_path+'\\*'
+1051)
+1052exceptimpacket.smbconnection.SessionErroraserr:
+1053code,const,text=err.getErrorCode(),err.getErrorString()[0],err.getErrorString()[1]
+1054errmsg="Error 0x%08x (%s): %s"%(code,const,text)
+1055ifself.config.no_colors:
+1056self.logger.print("%s%s"%(''.join(prompt+[bars[2]]),errmsg))
+1057else:
+1058self.logger.print("%s\x1b[1;91m%s\x1b[0m"%(''.join(prompt+[bars[2]]),errmsg))
+1059return
+1060
+1061entries=[eforeinentriesife.get_longname()notin[".",".."]]
+1062entries=sorted(entries,key=lambdax:x.get_longname())
+1063
+1064#
+1065iflen(entries)>1:
+1066index=0
+1067forentryinentries:
+1068index+=1
+1069# This is the first entry
+1070ifindex==0:
+1071ifentry.is_directory():
+1072ifself.config.no_colors:
+1073self.logger.print("%s%s\\"%(''.join(prompt+[bars[1]]),entry.get_longname()))
+1074else:
+1075self.logger.print("%s\x1b[1;96m%s\x1b[0m\\"%(''.join(prompt+[bars[1]]),entry.get_longname()))
+1076recurse_action(
+1077base_dir=base_dir,
+1078path=path+[entry.get_longname()],
+1079prompt=prompt+["│ "]
+1080)
+1081else:
+1082ifself.config.no_colors:
+1083self.logger.print("%s%s"%(''.join(prompt+[bars[1]]),entry.get_longname()))
+1084else:
+1085self.logger.print("%s\x1b[1m%s\x1b[0m"%(''.join(prompt+[bars[1]]),entry.get_longname()))
+1086
+1087# This is the last entry
+1088elifindex==len(entries):
+1089ifentry.is_directory():
+1090ifself.config.no_colors:
+1091self.logger.print("%s%s\\"%(''.join(prompt+[bars[2]]),entry.get_longname()))
+1092else:
+1093self.logger.print("%s\x1b[1;96m%s\x1b[0m\\"%(''.join(prompt+[bars[2]]),entry.get_longname()))
+1094recurse_action(
+1095base_dir=base_dir,
+1096path=path+[entry.get_longname()],
+1097prompt=prompt+[" "]
+1098)
+1099else:
+1100ifself.config.no_colors:
+1101self.logger.print("%s%s"%(''.join(prompt+[bars[2]]),entry.get_longname()))
+1102else:
+1103self.logger.print("%s\x1b[1m%s\x1b[0m"%(''.join(prompt+[bars[2]]),entry.get_longname()))
+1104
+1105# These are entries in the middle
+1106else:
+1107ifentry.is_directory():
+1108ifself.config.no_colors:
+1109self.logger.print("%s%s\\"%(''.join(prompt+[bars[1]]),entry.get_longname()))
+1110else:
+1111self.logger.print("%s\x1b[1;96m%s\x1b[0m\\"%(''.join(prompt+[bars[1]]),entry.get_longname()))
+1112recurse_action(
+1113base_dir=base_dir,
+1114path=path+[entry.get_longname()],
+1115prompt=prompt+["│ "]
+1116)
+1117else:
+1118ifself.config.no_colors:
+1119self.logger.print("%s%s"%(''.join(prompt+[bars[1]]),entry.get_longname()))
+1120else:
+1121self.logger.print("%s\x1b[1m%s\x1b[0m"%(''.join(prompt+[bars[1]]),entry.get_longname()))
+1122
+1123#
+1124eliflen(entries)==1:
+1125entry=entries[0]
+1126ifentry.is_directory():
+1127ifself.config.no_colors:
+1128self.logger.print("%s%s\\"%(''.join(prompt+[bars[2]]),entry.get_longname()))
+1129else:
+1130self.logger.print("%s\x1b[1;96m%s\x1b[0m\\"%(''.join(prompt+[bars[2]]),entry.get_longname()))
+1131recurse_action(
+1132base_dir=base_dir,
+1133path=path+[entry.get_longname()],
+1134prompt=prompt+[" "]
+1135)
+1136else:
+1137ifself.config.no_colors:
+1138self.logger.print("%s%s"%(''.join(prompt+[bars[2]]),entry.get_longname()))
+1139else:
+1140self.logger.print("%s\x1b[1m%s\x1b[0m"%(''.join(prompt+[bars[2]]),entry.get_longname()))
+1141
+1142# Entrypoint
+1143try:
+1144ifself.config.no_colors:
+1145self.logger.print("%s\\"%path)
+1146else:
+1147self.logger.print("\x1b[1;96m%s\x1b[0m\\"%path)
+1148recurse_action(
+1149base_dir=self.smb_cwd,
+1150path=[path],
+1151prompt=[""]
+1152)
+1153except(BrokenPipeError,KeyboardInterrupt)ase:
+1154self.logger.error("Interrupted.")
+1155self.close_smb_session()
+1156self.init_smb_session()
+1157
+1158defumount(self,local_mount_point):
+1159"""
+1160 Unmounts the specified local mount point of the remote share.1161
-1162# Restore the current share
-1163self.set_share(shareName=current_share)
+1162 This method unmounts the specified local mount point of the remote share based on the platform.
+1163 It supports Windows, Linux, and macOS platforms for unmounting.1164
-1165returnaccess_rights
-1166
-1167# Setter / Getter
-1168
-1169defset_share(self,shareName):
-1170"""
-1171 Sets the current SMB share to the specified share name.
-1172
-1173 This method updates the SMB session to use the specified share name. It checks if the share name is valid
-1174 and updates the smb_share attribute of the SMBSession instance.
+1165 Parameters:
+1166 local_mount_point (str): The local mount point to unmount.
+1167
+1168 Raises:
+1169 None
+1170 """
+1171
+1172ifos.path.exists(local_mount_point):
+1173ifsys.platform.startswith('win'):
+1174command=f"net use {local_mount_point} /delete"1175
-1176 Parameters:
-1177 shareName (str): The name of the share to set as the current SMB share.
+1176elifsys.platform.startswith('linux')orsys.platform.startswith('darwin'):
+1177command=f"umount {local_mount_point}"1178
-1179 Raises:
-1180 ValueError: If the shareName is None or an empty string.
-1181 """
-1182
-1183ifshareNameisnotNone:
-1184self.list_shares()
-1185ifshareName.lower()inself.available_shares.keys():
-1186# Doing this in order to keep the case of the share adevertised by the remote machine
-1187self.smb_share=self.available_shares[shareName.lower()]["name"]
-1188self.smb_cwd=""
-1189# Connects the tree
-1190self.smb_tree_id=self.smbClient.connectTree(self.smb_share)
-1191else:
-1192print("[!] Could not set share '%s', it does not exist remotely."%shareName)
-1193else:
-1194self.smb_share=None
-1195
-1196defset_cwd(self,path=None):
-1197"""
-1198 Sets the current working directory on the SMB share to the specified path.
-1199
-1200 This method updates the current working directory (cwd) of the SMB session to the given path if it is a valid directory.
-1201 If the specified path is not a directory, the cwd remains unchanged.
+1179else:
+1180command=None
+1181self.logger.error("Unsupported platform for unmounting SMB share.")
+1182
+1183ifcommandisnotNone:
+1184self.logger.debug("Executing: %s"%command)
+1185os.system(command)
+1186else:
+1187self.logger.error("Cannot unmount a non existing path.")
+1188
+1189# Other functions
+1190
+1191deftest_rights(self,sharename):
+1192"""
+1193 Tests the read and write access rights of the current SMB session.
+1194
+1195 This method checks the read and write access rights of the current SMB session by attempting to list paths and create/delete temporary directories.
+1196
+1197 Returns:
+1198 dict: A dictionary containing the read and write access rights status.
+1199 - "readable" (bool): Indicates if the session has read access rights.
+1200 - "writable" (bool): Indicates if the session has write access rights.
+1201 """1202
-1203 Parameters:
-1204 path (str): The path to set as the current working directory.
-1205
-1206 Raises:
-1207 ValueError: If the specified path is not a directory.
-1208 """
-1209
-1210ifpathisnotNone:
-1211# Set path separators to ntpath sep
-1212if'/'inpath:
-1213path=path.replace('/',ntpath.sep)
-1214
-1215ifpath.startswith(ntpath.sep):
-1216# Absolute path
-1217path=path+ntpath.sep
-1218else:
-1219# Relative path to the CWD
-1220iflen(self.smb_cwd)==0:
-1221path=path+ntpath.sep
-1222else:
-1223path=self.smb_cwd+ntpath.sep+path
-1224
-1225# Path normalization
-1226path=ntpath.normpath(path)
-1227path=re.sub(r'\\+',r'\\',path)
+1203# Restore the current share
+1204current_share=self.smb_share
+1205self.set_share(shareName=sharename)
+1206
+1207access_rights={"readable":False,"writable":False}
+1208try:
+1209self.smbClient.listPath(self.smb_share,'*',password=None)
+1210access_rights["readable"]=True
+1211exceptimpacket.smbconnection.SessionErrorase:
+1212access_rights["readable"]=False
+1213
+1214try:
+1215temp_dir=ntpath.normpath("\\"+''.join([random.choice("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPRSTUVWXYZ0123456759")forkinrange(16)]))
+1216self.smbClient.createDirectory(self.smb_share,temp_dir)
+1217self.smbClient.deleteDirectory(self.smb_share,temp_dir)
+1218access_rights["writable"]=True
+1219exceptimpacket.smbconnection.SessionErrorase:
+1220access_rights["writable"]=False
+1221
+1222# Restore the current share
+1223self.set_share(shareName=current_share)
+1224
+1225returnaccess_rights
+1226
+1227# Setter / Getter1228
-1229ifpathin["",".",".."]:
-1230self.smb_cwd=""
-1231else:
-1232ifself.path_isdir(pathFromRoot=path.strip(ntpath.sep)):
-1233# Path exists on the remote
-1234self.smb_cwd=ntpath.normpath(path)
-1235else:
-1236# Path does not exists or is not a directory on the remote
-1237print("[!] Remote directory '%s' does not exist."%path)
+1229defset_share(self,shareName):
+1230"""
+1231 Sets the current SMB share to the specified share name.
+1232
+1233 This method updates the SMB session to use the specified share name. It checks if the share name is valid
+1234 and updates the smb_share attribute of the SMBSession instance.
+1235
+1236 Parameters:
+1237 shareName (str): The name of the share to set as the current SMB share.
+1238
+1239 Raises:
+1240 ValueError: If the shareName is None or an empty string.
+1241 """
+1242
+1243ifshareNameisnotNone:
+1244self.list_shares()
+1245ifshareName.lower()inself.available_shares.keys():
+1246# Doing this in order to keep the case of the share adevertised by the remote machine
+1247self.smb_share=self.available_shares[shareName.lower()]["name"]
+1248self.smb_cwd=""
+1249# Connects the tree
+1250self.smb_tree_id=self.smbClient.connectTree(self.smb_share)
+1251else:
+1252self.logger.error("Could not set share '%s', it does not exist remotely."%shareName)
+1253else:
+1254self.smb_share=None
+1255
+1256defset_cwd(self,path=None):
+1257"""
+1258 Sets the current working directory on the SMB share to the specified path.
+1259
+1260 This method updates the current working directory (cwd) of the SMB session to the given path if it is a valid directory.
+1261 If the specified path is not a directory, the cwd remains unchanged.
+1262
+1263 Parameters:
+1264 path (str): The path to set as the current working directory.
+1265
+1266 Raises:
+1267 ValueError: If the specified path is not a directory.
+1268 """
+1269
+1270ifpathisnotNone:
+1271# Set path separators to ntpath sep
+1272if'/'inpath:
+1273path=path.replace('/',ntpath.sep)
+1274
+1275ifpath.startswith(ntpath.sep):
+1276# Absolute path
+1277path=path+ntpath.sep
+1278else:
+1279# Relative path to the CWD
+1280iflen(self.smb_cwd)==0:
+1281path=path+ntpath.sep
+1282else:
+1283path=self.smb_cwd+ntpath.sep+path
+1284
+1285# Path normalization
+1286path=ntpath.normpath(path)
+1287path=re.sub(r'\\+',r'\\',path)
+1288
+1289ifpathin["",".",".."]:
+1290self.smb_cwd=""
+1291else:
+1292ifself.path_isdir(pathFromRoot=path.strip(ntpath.sep)):
+1293# Path exists on the remote
+1294self.smb_cwd=ntpath.normpath(path)
+1295else:
+1296# Path does not exists or is not a directory on the remote
+1297self.logger.error("Remote directory '%s' does not exist."%path)
@@ -1434,1248 +1482,1317 @@
21classSMBSession(object): 22"""
- 23 Class SMBSession is designed to handle the session management for SMB (Server Message Block) protocol connections.
- 24 It provides functionalities to connect to an SMB server, authenticate using either NTLM or Kerberos, and manage SMB shares.
- 25
- 26 Attributes:
- 27 address (str): The IP address or hostname of the SMB server.
- 28 domain (str): The domain name for SMB server authentication.
- 29 username (str): The username for SMB server authentication.
- 30 password (str): The password for SMB server authentication.
- 31 lmhash (str): The LM hash of the user's password, if available.
- 32 nthash (str): The NT hash of the user's password, if available.
- 33 use_kerberos (bool): A flag to determine whether to use Kerberos for authentication.
- 34 kdcHost (str): The Key Distribution Center (KDC) host for Kerberos authentication.
- 35 debug (bool): A flag to enable debug output.
- 36 smbClient (object): The SMB client object used for the connection.
- 37 connected (bool): A flag to check the status of the connection.
+ 23 Represents an SMB session for interacting with an SMB server.
+ 24
+ 25 This class provides methods to manage and interact with an SMB server, including
+ 26 connecting to the server, listing shares, uploading and downloading files, and
+ 27 managing directories and files on the server. It handles session initialization,
+ 28 authentication, and cleanup.
+ 29
+ 30 Attributes:
+ 31 host (str): The hostname or IP address of the SMB server.
+ 32 port (int): The port number on which the SMB server is listening.
+ 33 credentials (dict): Authentication credentials for the SMB server.
+ 34 config (dict, optional): Configuration options for the SMB session.
+ 35 smbClient (impacket.smbconnection.SMBConnection): The SMB connection instance.
+ 36 connected (bool): Connection status to the SMB server.
+ 37 available_shares (dict): A dictionary of available SMB shares. 38 smb_share (str): The current SMB share in use.
- 39 smb_path (str): The current path within the SMB share.
- 40
- 41 Methods:
- 42 __init__(address, domain, username, password, lmhash, nthash, use_kerberos=False, kdcHost=None, debug=False):
- 43 Initializes the SMBSession with the specified parameters.
- 44 init_smb_session():
- 45 Initializes the SMB session by connecting to the server and authenticating using the specified method.
- 46 """
- 47
- 48def__init__(self,address,domain,username,password,lmhash,nthash,use_kerberos=False,kdcHost=None,config=None):
- 49super(SMBSession,self).__init__()
- 50# Objects
- 51self.config=config
- 52
- 53# Target server
- 54self.address=address
- 55
- 56# Credentials
- 57self.domain=domain
- 58self.username=username
- 59self.password=password
- 60self.lmhash=lmhash
- 61self.nthash=nthash
- 62self.use_kerberos=use_kerberos
- 63self.kdcHost=kdcHost
- 64
- 65self.smbClient=None
- 66self.connected=False
+ 39 smb_cwd (str): The current working directory on the SMB share.
+ 40 smb_tree_id (int): The tree ID of the connected SMB share.
+ 41
+ 42 Methods:
+ 43 close_smb_session(): Closes the current SMB session.
+ 44 init_smb_session(): Initializes the SMB session with the server.
+ 45 list_shares(): Lists all shares available on the SMB server.
+ 46 set_share(shareName): Sets the current SMB share.
+ 47 set_cwd(path): Sets the current working directory on the SMB share.
+ 48 put_file(localpath): Uploads a file to the current SMB share.
+ 49 get_file(remotepath, localpath): Downloads a file from the SMB share.
+ 50 mkdir(path): Creates a directory on the SMB share.
+ 51 rmdir(path): Removes a directory from the SMB share.
+ 52 rm(path): Removes a file from the SMB share.
+ 53 read_file(path): Reads a file from the SMB share.
+ 54 test_rights(sharename): Tests read and write access rights on a share.
+ 55 """
+ 56
+ 57def__init__(self,host,port,credentials,config=None,logger=None):
+ 58super(SMBSession,self).__init__()
+ 59# Objects
+ 60self.config=config
+ 61self.logger=logger
+ 62
+ 63# Target server
+ 64self.host=host
+ 65# Target port (by default on 445)
+ 66self.port=port 67
- 68self.available_shares={}
- 69self.smb_share=None
- 70self.smb_cwd=""
- 71self.smb_tree_id=None
- 72
- 73self.list_shares()
- 74
- 75# Connect and disconnect SMB session
- 76
- 77definit_smb_session(self):
- 78"""
- 79 Initializes and establishes a session with the SMB server.
+ 68# Credentials
+ 69self.credentials=credentials
+ 70
+ 71self.smbClient=None
+ 72self.connected=False
+ 73
+ 74self.available_shares={}
+ 75self.smb_share=None
+ 76self.smb_cwd=""
+ 77self.smb_tree_id=None
+ 78
+ 79self.list_shares() 80
- 81 This method sets up the SMB connection using either Kerberos or NTLM authentication based on the configuration.
- 82 It attempts to connect to the SMB server specified by the `address` attribute and authenticate using the credentials provided during the object's initialization.
- 83
- 84 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.
- 85
- 86 Returns:
- 87 bool: True if the connection and authentication are successful, False otherwise.
- 88 """
+ 81# Connect and disconnect SMB session
+ 82
+ 83defclose_smb_session(self):
+ 84"""
+ 85 Closes the current SMB session by disconnecting the SMB client.
+ 86
+ 87 This method ensures that the SMB client connection is properly closed. It checks if the client is connected
+ 88 and if so, it closes the connection and resets the connection status. 89
- 90self.connected=False
- 91
- 92ifself.config.debug:
- 93print("[debug] [>] Connecting to remote SMB server '%s' ... "%self.address)
- 94try:
- 95self.smbClient=impacket.smbconnection.SMBConnection(
- 96remoteName=self.address,
- 97remoteHost=self.address,
- 98sess_port=int(445)
- 99)
- 100exceptOSErroraserr:
- 101print("[!] %s"%err)
- 102self.smbClient=None
+ 90 Raises:
+ 91 Exception: If the SMB client is not initialized or if there's an error during the disconnection process.
+ 92 """
+ 93
+ 94ifself.smbClientisnotNone:
+ 95ifself.connected:
+ 96self.smbClient.close()
+ 97self.connected=False
+ 98self.logger.debug("[+] SMB connection closed successfully.")
+ 99else:
+ 100self.logger.debug("[!] No active SMB connection to close.")
+ 101else:
+ 102raiseException("SMB client is not initialized.") 103
- 104ifself.smbClientisnotNone:
- 105ifself.use_kerberos:
- 106ifself.config.debug:
- 107print("[debug] [>] Authenticating as '%s\\%s' with kerberos ... "%(self.domain,self.username))
- 108try:
- 109self.connected=self.smbClient.kerberosLogin(
- 110user=self.username,
- 111password=self.password,
- 112domain=self.domain,
- 113lmhash=self.lmhash,
- 114nthash=self.nthash,
- 115aesKey=self.aesKey,
- 116kdcHost=self.kdcHost
- 117)
- 118exceptimpacket.smbconnection.SessionErroraserr:
- 119ifself.config.debug:
- 120traceback.print_exc()
- 121print("[!] Could not login: %s"%err)
- 122self.connected=False
- 123
- 124else:
- 125ifself.config.debug:
- 126print("[debug] [>] Authenticating as '%s\\%s' with NTLM ... "%(self.domain,self.username))
- 127try:
- 128self.connected=self.smbClient.login(
- 129user=self.username,
- 130password=self.password,
- 131domain=self.domain,
- 132lmhash=self.lmhash,
- 133nthash=self.nthash
- 134)
- 135exceptimpacket.smbconnection.SessionErroraserr:
- 136ifself.config.debug:
- 137traceback.print_exc()
- 138print("[!] Could not login: %s"%err)
- 139self.connected=False
- 140
- 141ifself.connected:
- 142print("[+] Successfully authenticated to '%s' as '%s\\%s'!"%(self.address,self.domain,self.username))
- 143else:
- 144print("[!] Failed to authenticate to '%s' as '%s\\%s'!"%(self.address,self.domain,self.username))
- 145
- 146returnself.connected
- 147
- 148defclose_smb_session(self):
- 149"""
- 150 Closes the current SMB session by disconnecting the SMB client.
- 151
- 152 This method ensures that the SMB client connection is properly closed. It checks if the client is connected
- 153 and if so, it closes the connection and resets the connection status.
+ 104definit_smb_session(self):
+ 105"""
+ 106 Initializes and establishes a session with the SMB server.
+ 107
+ 108 This method sets up the SMB connection using either Kerberos or NTLM authentication based on the configuration.
+ 109 It attempts to connect to the SMB server specified by the `address` attribute and authenticate using the credentials provided during the object's initialization.
+ 110
+ 111 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.
+ 112
+ 113 Returns:
+ 114 bool: True if the connection and authentication are successful, False otherwise.
+ 115 """
+ 116
+ 117self.connected=False
+ 118
+ 119self.logger.debug("[>] Connecting to remote SMB server '%s' ... "%self.host)
+ 120
+ 121try:
+ 122ifis_port_open(self.host,self.port):
+ 123self.smbClient=impacket.smbconnection.SMBConnection(
+ 124remoteName=self.host,
+ 125remoteHost=self.host,
+ 126sess_port=int(self.port)
+ 127)
+ 128else:
+ 129self.connected=False
+ 130exceptOSErroraserr:
+ 131ifself.config.debug:
+ 132traceback.print_exc()
+ 133self.logger.error(err)
+ 134self.smbClient=None
+ 135
+ 136ifself.smbClientisnotNone:
+ 137ifself.credentials.use_kerberos:
+ 138self.logger.debug("[>] Authenticating as '%s\\%s' with kerberos ... "%(self.credentials.domain,self.credentials.username))
+ 139try:
+ 140self.connected=self.smbClient.kerberosLogin(
+ 141user=self.credentials.username,
+ 142password=self.credentials.password,
+ 143domain=self.credentials.domain,
+ 144lmhash=self.credentials.lm_hex,
+ 145nthash=self.credentials.nt_hex,
+ 146aesKey=self.credentials.aesKey,
+ 147kdcHost=self.credentials.kdcHost
+ 148)
+ 149exceptimpacket.smbconnection.SessionErroraserr:
+ 150ifself.config.debug:
+ 151traceback.print_exc()
+ 152self.logger.error("Could not login: %s"%err)
+ 153self.connected=False 154
- 155 Raises:
- 156 Exception: If the SMB client is not initialized or if there's an error during the disconnection process.
- 157 """
- 158
- 159ifself.smbClientisnotNone:
- 160ifself.connected:
- 161self.smbClient.close()
- 162self.connected=False
- 163ifself.config.debug:
- 164print("[+] SMB connection closed successfully.")
- 165else:
- 166ifself.config.debug:
- 167print("[!] No active SMB connection to close.")
- 168else:
- 169raiseException("SMB client is not initialized.")
- 170
- 171# Operations
- 172
- 173defread_file(self,path=None):
- 174ifself.path_isfile(path=path):
- 175tmp_file_path=self.smb_cwd+ntpath.sep+path
- 176matches=self.smbClient.listPath(
- 177shareName=self.smb_share,
- 178path=tmp_file_path
- 179)
- 180
- 181fh=io.BytesIO()
- 182try:
- 183# opening the files in streams instead of mounting shares allows
- 184# for running the script from unprivileged containers
- 185self.smbClient.getFile(self.smb_share,tmp_file_path,fh.write)
- 186exceptimpacket.smbconnection.SessionErrorase:
- 187returnNone
- 188rawdata=fh.getvalue()
- 189fh.close()
- 190returnrawdata
- 191else:
- 192print("[!] Remote path '%s' is not a file."%path)
- 193
- 194deffind(self,paths=[],callback=None):
- 195defrecurse_action(paths=[],depth=0,callback=None):
- 196ifcallbackisNone:
- 197return[]
- 198
- 199next_directories_to_explore=[]
- 200
- 201forpathinpaths:
- 202remote_smb_path=ntpath.normpath(self.smb_cwd+ntpath.sep+path)
- 203entries=[]
- 204
- 205try:
- 206entries=self.smbClient.listPath(
- 207shareName=self.smb_share,
- 208path=(remote_smb_path+ntpath.sep+'*')
- 209)
- 210exceptimpacket.smbconnection.SessionErroraserr:
- 211continue
- 212# Remove dot names
- 213entries=[eforeinentriesife.get_longname()notin[".",".."]]
- 214# Sort the entries ignoring case
- 215entries=sorted(entries,key=lambdax:x.get_longname().lower())
- 216
- 217forentryinentries:
- 218ifentry.is_directory():
- 219callback(entry,path+ntpath.sep+entry.get_longname()+ntpath.sep,depth)
- 220else:
- 221callback(entry,path+ntpath.sep+entry.get_longname(),depth)
- 222
- 223# Next directories to explore
- 224forentryinentries:
- 225ifentry.is_directory():
- 226next_directories_to_explore.append(path+ntpath.sep+entry.get_longname()+ntpath.sep)
- 227
- 228returnnext_directories_to_explore
- 229#
- 230ifcallbackisnotNone:
- 231depth=0
- 232whilelen(paths)!=0:
- 233paths=recurse_action(
- 234paths=paths,
- 235depth=depth,
- 236callback=callback
- 237)
- 238depth=depth+1
- 239else:
- 240print("[!] SMBSession.find(), callback function cannot be None.")
- 241
- 242defget_file(self,path=None,keepRemotePath=False):
- 243"""
- 244 Retrieves a file from the specified path on the SMB share.
- 245
- 246 This method attempts to retrieve a file from the given path within the currently connected SMB share.
- 247 If the path points to a directory, it skips the retrieval. It handles file retrieval by creating a local
- 248 file object and writing the contents of the remote file to it using the SMB client's getFile method.
- 249
- 250 Parameters:
- 251 path (str): The path of the file to retrieve. If None, uses the current smb_path.
- 252
- 253 Returns:
- 254 None
- 255 """
- 256
- 257# Parse path
- 258path=path.replace('/',ntpath.sep)
- 259ifntpath.sepinpath:
- 260tmp_search_path=ntpath.normpath(self.smb_cwd+ntpath.sep+ntpath.dirname(path))
+ 155else:
+ 156self.logger.debug("[>] Authenticating as '%s\\%s' with NTLM ... "%(self.credentials.domain,self.credentials.username))
+ 157
+ 158try:
+ 159self.connected=self.smbClient.login(
+ 160user=self.credentials.username,
+ 161password=self.credentials.password,
+ 162domain=self.credentials.domain,
+ 163lmhash=self.credentials.lm_hex,
+ 164nthash=self.credentials.nt_hex
+ 165)
+ 166exceptimpacket.smbconnection.SessionErroraserr:
+ 167ifself.config.debug:
+ 168traceback.print_exc()
+ 169self.logger.error("Could not login: %s"%err)
+ 170self.connected=False
+ 171
+ 172ifself.connected:
+ 173self.logger.print("[+] Successfully authenticated to '%s' as '%s\\%s'!"%(self.host,self.credentials.domain,self.credentials.username))
+ 174else:
+ 175self.logger.error("Failed to authenticate to '%s' as '%s\\%s'!"%(self.host,self.credentials.domain,self.credentials.username))
+ 176
+ 177returnself.connected
+ 178
+ 179defping_smb_session(self):
+ 180"""
+ 181 Tests the connectivity to the SMB server by sending an echo command.
+ 182
+ 183 This method attempts to send an echo command to the SMB server to check if the session is still active.
+ 184 It updates the `connected` attribute of the class based on the success or failure of the echo command.
+ 185
+ 186 Returns:
+ 187 bool: True if the echo command succeeds (indicating the session is active), False otherwise.
+ 188 """
+ 189
+ 190ifnotis_port_open(self.host,self.port):
+ 191self.connected=False
+ 192else:
+ 193try:
+ 194self.smbClient.getSMBServer().echo()
+ 195exceptExceptionase:
+ 196self.connected=False
+ 197
+ 198returnself.connected
+ 199
+ 200# Operations
+ 201
+ 202deffind(self,paths=[],callback=None):
+ 203"""
+ 204 Finds files and directories on the SMB share based on the provided paths and executes a callback function on each entry.
+ 205
+ 206 This method traverses the specified paths on the SMB share, recursively exploring directories and invoking the callback
+ 207 function on each file or directory found. The callback function is called with three arguments: the entry object, the
+ 208 full path of the entry, and the current depth of recursion.
+ 209
+ 210 Args:
+ 211 paths (list, optional): A list of paths to start the search from. Defaults to an empty list.
+ 212 callback (function, optional): A function to be called on each entry found. The function should accept three arguments:
+ 213 the entry object, the full path of the entry, and the current depth of recursion. Defaults to None.
+ 214
+ 215 Note:
+ 216 If the callback function is None, the method will print an error message and return without performing any action.
+ 217 """
+ 218
+ 219defrecurse_action(paths=[],depth=0,callback=None):
+ 220ifcallbackisNone:
+ 221return[]
+ 222
+ 223next_directories_to_explore=[]
+ 224
+ 225forpathinpaths:
+ 226remote_smb_path=ntpath.normpath(self.smb_cwd+ntpath.sep+path)
+ 227entries=[]
+ 228
+ 229try:
+ 230entries=self.smbClient.listPath(
+ 231shareName=self.smb_share,
+ 232path=(remote_smb_path+ntpath.sep+'*')
+ 233)
+ 234exceptimpacket.smbconnection.SessionErroraserr:
+ 235continue
+ 236# Remove dot names
+ 237entries=[eforeinentriesife.get_longname()notin[".",".."]]
+ 238# Sort the entries ignoring case
+ 239entries=sorted(entries,key=lambdax:x.get_longname().lower())
+ 240
+ 241forentryinentries:
+ 242ifentry.is_directory():
+ 243fullpath=path+ntpath.sep+entry.get_longname()+ntpath.sep
+ 244next_directories_to_explore.append(fullpath)
+ 245else:
+ 246fullpath=path+ntpath.sep+entry.get_longname()
+ 247fullpath=re.sub(r'\\\\+',r'\\',fullpath)
+ 248callback(entry,fullpath,depth)
+ 249
+ 250returnnext_directories_to_explore
+ 251#
+ 252ifcallbackisnotNone:
+ 253depth=0
+ 254whilelen(paths)!=0:
+ 255paths=recurse_action(
+ 256paths=paths,
+ 257depth=depth,
+ 258callback=callback
+ 259)
+ 260depth=depth+1 261else:
- 262tmp_search_path=ntpath.normpath(self.smb_cwd+ntpath.sep)
- 263# Parse filename
- 264filename=ntpath.basename(path)
- 265
- 266# Search for the file
- 267matches=self.smbClient.listPath(
- 268shareName=self.smb_share,
- 269path=tmp_search_path+ntpath.sep+'*'
- 270)
+ 262self.logger.error("SMBSession.find(), callback function cannot be None.")
+ 263
+ 264defget_file(self,path=None,keepRemotePath=False):
+ 265"""
+ 266 Retrieves a file from the specified path on the SMB share.
+ 267
+ 268 This method attempts to retrieve a file from the given path within the currently connected SMB share.
+ 269 If the path points to a directory, it skips the retrieval. It handles file retrieval by creating a local
+ 270 file object and writing the contents of the remote file to it using the SMB client's getFile method. 271
- 272# Filter the entries
- 273matching_entries=[]
- 274forentryinmatches:
- 275ifentry.is_directory():
- 276# Skip directories
- 277continue
- 278ifentry.get_longname()==filename:
- 279matching_entries.append(entry)
- 280elif'*'infilename:
- 281regexp=filename.replace('.','\\.').replace('*','.*')
- 282ifre.match(regexp,entry.get_longname()):
- 283matching_entries.append(entry)
- 284
- 285matching_entries=sorted(list(set(matching_entries)),key=lambdax:x.get_longname())
- 286
- 287forentryinmatching_entries:
- 288ifentry.is_directory():
- 289ifself.config.debug:
- 290print("[debug] [>] Skipping '%s' because it is a directory."%(tmp_search_path+ntpath.sep+entry.get_longname()))
- 291else:
- 292try:
- 293ifntpath.sepinpath:
- 294outputfile=ntpath.dirname(path)+ntpath.sep+entry.get_longname()
- 295else:
- 296outputfile=entry.get_longname()
- 297f=LocalFileIO(
- 298mode="wb",
- 299path=outputfile,
- 300expected_size=entry.get_filesize(),
- 301debug=self.config.debug,
- 302keepRemotePath=keepRemotePath
- 303)
- 304self.smbClient.getFile(
- 305shareName=self.smb_share,
- 306pathName=tmp_search_path+ntpath.sep+entry.get_longname(),
- 307callback=f.write
- 308)
- 309f.close()
- 310except(BrokenPipeError,KeyboardInterrupt)ase:
- 311f.close()
- 312print("\x1b[v\x1b[o\r[!] Interrupted.")
- 313self.close_smb_session()
- 314self.init_smb_session()
- 315
- 316returnNone
- 317
- 318defget_file_recursively(self,path=None):
- 319"""
- 320 Recursively retrieves files from a specified path on the SMB share.
- 321
- 322 This method navigates through all directories starting from the given path,
- 323 and downloads all files found. It handles directories recursively, ensuring
- 324 that all nested files are retrieved. The method skips over directory entries
- 325 and handles errors gracefully, attempting to continue the operation where possible.
- 326
- 327 Parameters:
- 328 path (str): The initial directory path from which to start the recursive file retrieval.
- 329 If None, it starts from the root of the configured SMB share.
- 330 """
- 331
- 332defrecurse_action(base_dir="",path=[]):
- 333iflen(base_dir)==0:
- 334remote_smb_path=ntpath.sep.join(path)
- 335else:
- 336remote_smb_path=base_dir+ntpath.sep+ntpath.sep.join(path)
- 337remote_smb_path=ntpath.normpath(remote_smb_path)
+ 272 Parameters:
+ 273 path (str): The path of the file to retrieve. If None, uses the current smb_path.
+ 274
+ 275 Returns:
+ 276 None
+ 277 """
+ 278
+ 279# Parse path
+ 280path=path.replace('/',ntpath.sep)
+ 281ifntpath.sepinpath:
+ 282tmp_search_path=ntpath.normpath(self.smb_cwd+ntpath.sep+ntpath.dirname(path))
+ 283else:
+ 284tmp_search_path=ntpath.normpath(self.smb_cwd+ntpath.sep)
+ 285# Parse filename
+ 286filename=ntpath.basename(path)
+ 287
+ 288# Search for the file
+ 289matches=self.smbClient.listPath(
+ 290shareName=self.smb_share,
+ 291path=tmp_search_path+ntpath.sep+'*'
+ 292)
+ 293
+ 294# Filter the entries
+ 295matching_entries=[]
+ 296forentryinmatches:
+ 297ifentry.is_directory():
+ 298# Skip directories
+ 299continue
+ 300ifentry.get_longname()==filename:
+ 301matching_entries.append(entry)
+ 302elif'*'infilename:
+ 303regexp=filename.replace('.','\\.').replace('*','.*')
+ 304ifre.match(regexp,entry.get_longname()):
+ 305matching_entries.append(entry)
+ 306
+ 307matching_entries=sorted(list(set(matching_entries)),key=lambdax:x.get_longname())
+ 308
+ 309forentryinmatching_entries:
+ 310ifentry.is_directory():
+ 311self.logger.debug("[>] Skipping '%s' because it is a directory."%(tmp_search_path+ntpath.sep+entry.get_longname()))
+ 312else:
+ 313try:
+ 314ifntpath.sepinpath:
+ 315outputfile=ntpath.dirname(path)+ntpath.sep+entry.get_longname()
+ 316else:
+ 317outputfile=entry.get_longname()
+ 318f=LocalFileIO(
+ 319mode="wb",
+ 320path=outputfile,
+ 321expected_size=entry.get_filesize(),
+ 322debug=self.config.debug,
+ 323keepRemotePath=keepRemotePath
+ 324)
+ 325self.smbClient.getFile(
+ 326shareName=self.smb_share,
+ 327pathName=tmp_search_path+ntpath.sep+entry.get_longname(),
+ 328callback=f.write
+ 329)
+ 330f.close()
+ 331except(BrokenPipeError,KeyboardInterrupt)ase:
+ 332f.close()
+ 333print("\x1b[v\x1b[o\r[!] Interrupted.")
+ 334self.close_smb_session()
+ 335self.init_smb_session()
+ 336
+ 337returnNone 338
- 339entries=self.smbClient.listPath(
- 340shareName=self.smb_share,
- 341path=remote_smb_path+'\\*'
- 342)
- 343iflen(entries)!=0:
- 344files=[entryforentryinentriesifnotentry.is_directory()]
- 345directories=[entryforentryinentriesifentry.is_directory()andentry.get_longname()notin[".",".."]]
- 346
- 347# Files
- 348iflen(files)!=0:
- 349print("[>] Retrieving files of '%s'"%remote_smb_path)
- 350forentry_fileinfiles:
- 351ifnotentry_file.is_directory():
- 352f=LocalFileIO(
- 353mode="wb",
- 354path=remote_smb_path+ntpath.sep+entry_file.get_longname(),
- 355expected_size=entry_file.get_filesize(),
- 356keepRemotePath=True,
- 357debug=self.config.debug
- 358)
- 359try:
- 360self.smbClient.getFile(
- 361shareName=self.smb_share,
- 362pathName=remote_smb_path+ntpath.sep+entry_file.get_longname(),
- 363callback=f.write
- 364)
- 365f.close()
- 366exceptBrokenPipeErroraserr:
- 367f.set_error(message="[bold red]Failed downloading '%s': %s"%(f.path,err))
- 368f.close(remove=True)
- 369break
- 370exceptExceptionaserr:
- 371f.set_error(message="[bold red]Failed downloading '%s': %s"%(f.path,err))
- 372f.close(remove=True)
- 373
- 374# Directories
- 375forentry_directoryindirectories:
- 376ifentry_directory.is_directory():
- 377recurse_action(
- 378base_dir=self.smb_cwd,
- 379path=path+[entry_directory.get_longname()]
- 380)
- 381# Entrypoint
- 382try:
- 383recurse_action(
- 384base_dir=self.smb_cwd,
- 385path=[path]
- 386)
- 387except(BrokenPipeError,KeyboardInterrupt)ase:
- 388print("\x1b[v\x1b[o\r[!] Interrupted.")
- 389self.close_smb_session()
- 390self.init_smb_session()
- 391
- 392defget_entry(self,path=None):
- 393"""
- 394 Retrieves information about a specific entry located at the provided path on the SMB share.
- 395
- 396 This method checks if the specified path exists on the SMB share. If the path exists, it retrieves the details of the entry at that path, including the directory name and file name. If the entry is found, it returns the entry object; otherwise, it returns None.
- 397
- 398 Args:
- 399 path (str): The path of the entry to retrieve information about.
- 400
- 401 Returns:
- 402 Entry: An object representing the entry at the specified path, or None if the entry is not found.
- 403 """
- 404
- 405ifself.path_exists(path=path):
- 406matches=self.smbClient.listPath(shareName=self.smb_share,path=path)
- 407
- 408iflen(matches)==1:
- 409returnmatches[0]
- 410else:
- 411returnNone
- 412
- 413else:
- 414returnNone
- 415
- 416definfo(self,share=True,server=True):
- 417"""
- 418 Displays information about the server and optionally the shares.
- 419
- 420 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.
+ 339defget_file_recursively(self,path=None):
+ 340"""
+ 341 Recursively retrieves files from a specified path on the SMB share.
+ 342
+ 343 This method navigates through all directories starting from the given path,
+ 344 and downloads all files found. It handles directories recursively, ensuring
+ 345 that all nested files are retrieved. The method skips over directory entries
+ 346 and handles errors gracefully, attempting to continue the operation where possible.
+ 347
+ 348 Parameters:
+ 349 path (str): The initial directory path from which to start the recursive file retrieval.
+ 350 If None, it starts from the root of the configured SMB share.
+ 351 """
+ 352
+ 353defrecurse_action(base_dir="",path=[]):
+ 354iflen(base_dir)==0:
+ 355remote_smb_path=ntpath.sep.join(path)
+ 356else:
+ 357remote_smb_path=base_dir+ntpath.sep+ntpath.sep.join(path)
+ 358remote_smb_path=ntpath.normpath(remote_smb_path)
+ 359
+ 360entries=self.smbClient.listPath(
+ 361shareName=self.smb_share,
+ 362path=remote_smb_path+'\\*'
+ 363)
+ 364iflen(entries)!=0:
+ 365files=[entryforentryinentriesifnotentry.is_directory()]
+ 366directories=[entryforentryinentriesifentry.is_directory()andentry.get_longname()notin[".",".."]]
+ 367
+ 368# Files
+ 369iflen(files)!=0:
+ 370self.logger.print("[>] Retrieving files of '%s'"%remote_smb_path)
+ 371forentry_fileinfiles:
+ 372ifnotentry_file.is_directory():
+ 373f=LocalFileIO(
+ 374mode="wb",
+ 375path=remote_smb_path+ntpath.sep+entry_file.get_longname(),
+ 376expected_size=entry_file.get_filesize(),
+ 377keepRemotePath=True,
+ 378debug=self.config.debug
+ 379)
+ 380try:
+ 381self.smbClient.getFile(
+ 382shareName=self.smb_share,
+ 383pathName=remote_smb_path+ntpath.sep+entry_file.get_longname(),
+ 384callback=f.write
+ 385)
+ 386f.close()
+ 387exceptBrokenPipeErroraserr:
+ 388f.set_error(message="[bold red]Failed downloading '%s': %s"%(f.path,err))
+ 389f.close(remove=True)
+ 390break
+ 391exceptExceptionaserr:
+ 392f.set_error(message="[bold red]Failed downloading '%s': %s"%(f.path,err))
+ 393f.close(remove=True)
+ 394
+ 395# Directories
+ 396forentry_directoryindirectories:
+ 397ifentry_directory.is_directory():
+ 398recurse_action(
+ 399base_dir=self.smb_cwd,
+ 400path=path+[entry_directory.get_longname()]
+ 401)
+ 402# Entrypoint
+ 403try:
+ 404recurse_action(
+ 405base_dir=self.smb_cwd,
+ 406path=[path]
+ 407)
+ 408except(BrokenPipeError,KeyboardInterrupt)ase:
+ 409print("\x1b[v\x1b[o\r[!] Interrupted.")
+ 410self.close_smb_session()
+ 411self.init_smb_session()
+ 412
+ 413defget_entry(self,path=None):
+ 414"""
+ 415 Retrieves information about a specific entry located at the provided path on the SMB share.
+ 416
+ 417 This method checks if the specified path exists on the SMB share. If the path exists, it retrieves the details of the entry at that path, including the directory name and file name. If the entry is found, it returns the entry object; otherwise, it returns None.
+ 418
+ 419 Args:
+ 420 path (str): The path of the entry to retrieve information about. 421
- 422 Parameters:
- 423 share (bool): If True, display information about the current share.
- 424 server (bool): If True, display information about the server.
+ 422 Returns:
+ 423 Entry: An object representing the entry at the specified path, or None if the entry is not found.
+ 424 """ 425
- 426 Returns:
- 427 None
- 428 """
- 429
- 430ifserver:
- 431ifself.config.no_colors:
- 432print("[+] Server:")
- 433print(" ├─NetBIOS:")
- 434print(" │ ├─ NetBIOS Hostname ──────── : %s"%(self.smbClient.getServerName()))
- 435print(" │ └─ NetBIOS Domain ────────── : %s"%(self.smbClient.getServerDomain()))
- 436print(" ├─DNS:")
- 437print(" │ ├─ DNS Hostname ──────────── : %s"%(self.smbClient.getServerDNSHostName()))
- 438print(" │ └─ DNS Domain ────────────── : %s"%(self.smbClient.getServerDNSDomainName()))
- 439print(" ├─OS:")
- 440print(" │ ├─ OS Name ───────────────── : %s"%(self.smbClient.getServerOS()))
- 441print(" │ └─ OS Version ────────────── : %s.%s.%s"%(self.smbClient.getServerOSMajor(),self.smbClient.getServerOSMinor(),self.smbClient.getServerOSBuild()))
- 442print(" ├─Server:")
- 443print(" │ ├─ Signing Required ──────── : %s"%(self.smbClient.isSigningRequired()))
- 444print(" │ ├─ Login Required ────────── : %s"%(self.smbClient.isLoginRequired()))
- 445print(" │ ├─ Supports NTLMv2 ───────── : %s"%(self.smbClient.doesSupportNTLMv2()))
- 446MaxReadSize=self.smbClient.getIOCapabilities()["MaxReadSize"]
- 447print(" │ ├─ Max size of read chunk ── : %d bytes (%s)"%(MaxReadSize,b_filesize(MaxReadSize)))
- 448MaxWriteSize=self.smbClient.getIOCapabilities()["MaxWriteSize"]
- 449print(" │ └─ Max size of write chunk ─ : %d bytes (%s)"%(MaxWriteSize,b_filesize(MaxWriteSize)))
- 450print(" └─")
- 451else:
- 452print("[+] Server:")
- 453print(" ├─NetBIOS:")
- 454print(" │ ├─ \x1b[94mNetBIOS Hostname\x1b[0m \x1b[90m────────\x1b[0m : \x1b[93m%s\x1b[0m"%(self.smbClient.getServerName()))
- 455print(" │ └─ \x1b[94mNetBIOS Domain\x1b[0m \x1b[90m──────────\x1b[0m : \x1b[93m%s\x1b[0m"%(self.smbClient.getServerDomain()))
- 456print(" ├─DNS:")
- 457print(" │ ├─ \x1b[94mDNS Hostname\x1b[0m \x1b[90m────────────\x1b[0m : \x1b[93m%s\x1b[0m"%(self.smbClient.getServerDNSHostName()))
- 458print(" │ └─ \x1b[94mDNS Domain\x1b[0m \x1b[90m──────────────\x1b[0m : \x1b[93m%s\x1b[0m"%(self.smbClient.getServerDNSDomainName()))
- 459print(" ├─OS:")
- 460print(" │ ├─ \x1b[94mOS Name\x1b[0m \x1b[90m─────────────────\x1b[0m : \x1b[93m%s\x1b[0m"%(self.smbClient.getServerOS()))
- 461print(" │ └─ \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()))
- 462print(" ├─Server:")
- 463print(" │ ├─ \x1b[94mSigning Required\x1b[0m \x1b[90m────────\x1b[0m : \x1b[93m%s\x1b[0m"%(self.smbClient.isSigningRequired()))
- 464print(" │ ├─ \x1b[94mLogin Required\x1b[0m \x1b[90m──────────\x1b[0m : \x1b[93m%s\x1b[0m"%(self.smbClient.isLoginRequired()))
- 465print(" │ ├─ \x1b[94mSupports NTLMv2\x1b[0m \x1b[90m─────────\x1b[0m : \x1b[93m%s\x1b[0m"%(self.smbClient.doesSupportNTLMv2()))
- 466MaxReadSize=self.smbClient.getIOCapabilities()["MaxReadSize"]
- 467print(" │ ├─ \x1b[94mMax size of read chunk\x1b[0m \x1b[90m──\x1b[0m : \x1b[93m%d bytes (%s)\x1b[0m"%(MaxReadSize,b_filesize(MaxReadSize)))
- 468MaxWriteSize=self.smbClient.getIOCapabilities()["MaxWriteSize"]
- 469print(" │ └─ \x1b[94mMax size of write chunk\x1b[0m \x1b[90m─\x1b[0m : \x1b[93m%d bytes (%s)\x1b[0m"%(MaxWriteSize,b_filesize(MaxWriteSize)))
- 470print(" └─")
- 471
- 472ifshareandself.smb_shareisnotNone:
- 473share_name=self.available_shares.get(self.smb_share.lower(),"")["name"]
- 474share_comment=self.available_shares.get(self.smb_share.lower(),"")["comment"]
- 475share_type=self.available_shares.get(self.smb_share.lower(),"")["type"]
- 476share_type=', '.join([s.replace("STYPE_","")forsinshare_type])
- 477share_rawtype=self.available_shares.get(self.smb_share.lower(),"")["rawtype"]
- 478ifself.config.no_colors:
- 479print("\n[+] Share:")
- 480print(" ├─ Name ──────────── : %s"%(share_name))
- 481print(" ├─ Description ───── : %s"%(share_comment))
- 482print(" ├─ Type ──────────── : %s"%(share_type))
- 483print(" └─ Raw type value ── : %s"%(share_rawtype))
- 484else:
- 485print("\n[+] Share:")
- 486print(" ├─ \x1b[94mName\x1b[0m \x1b[90m────────────\x1b[0m : \x1b[93m%s\x1b[0m"%(share_name))
- 487print(" ├─ \x1b[94mDescription\x1b[0m \x1b[90m─────\x1b[0m : \x1b[93m%s\x1b[0m"%(share_comment))
- 488print(" ├─ \x1b[94mType\x1b[0m \x1b[90m────────────\x1b[0m : \x1b[93m%s\x1b[0m"%(share_type))
- 489print(" └─ \x1b[94mRaw type value\x1b[0m \x1b[90m──\x1b[0m : \x1b[93m%s\x1b[0m"%(share_rawtype))
- 490
- 491deflist_contents(self,path=None):
- 492"""
- 493 Lists the contents of a specified directory on the SMB share.
+ 426ifself.path_exists(path=path):
+ 427matches=self.smbClient.listPath(
+ 428shareName=self.smb_share,
+ 429path=path
+ 430)
+ 431
+ 432iflen(matches)==1:
+ 433returnmatches[0]
+ 434else:
+ 435returnNone
+ 436else:
+ 437returnNone
+ 438
+ 439definfo(self,share=True,server=True):
+ 440"""
+ 441 Displays information about the server and optionally the shares.
+ 442
+ 443 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.
+ 444
+ 445 Parameters:
+ 446 share (bool): If True, display information about the current share.
+ 447 server (bool): If True, display information about the server.
+ 448
+ 449 Returns:
+ 450 None
+ 451 """
+ 452
+ 453ifserver:
+ 454ifself.config.no_colors:
+ 455self.logger.print("[+] Server:")
+ 456self.logger.print(" ├─NetBIOS:")
+ 457self.logger.print(" │ ├─ NetBIOS Hostname ──────── : %s"%(self.smbClient.getServerName()))
+ 458self.logger.print(" │ └─ NetBIOS Domain ────────── : %s"%(self.smbClient.getServerDomain()))
+ 459self.logger.print(" ├─DNS:")
+ 460self.logger.print(" │ ├─ DNS Hostname ──────────── : %s"%(self.smbClient.getServerDNSHostName()))
+ 461self.logger.print(" │ └─ DNS Domain ────────────── : %s"%(self.smbClient.getServerDNSDomainName()))
+ 462self.logger.print(" ├─OS:")
+ 463self.logger.print(" │ ├─ OS Name ───────────────── : %s"%(self.smbClient.getServerOS()))
+ 464self.logger.print(" │ └─ OS Version ────────────── : %s.%s.%s"%(self.smbClient.getServerOSMajor(),self.smbClient.getServerOSMinor(),self.smbClient.getServerOSBuild()))
+ 465self.logger.print(" ├─Server:")
+ 466self.logger.print(" │ ├─ Signing Required ──────── : %s"%(self.smbClient.isSigningRequired()))
+ 467self.logger.print(" │ ├─ Login Required ────────── : %s"%(self.smbClient.isLoginRequired()))
+ 468self.logger.print(" │ ├─ Supports NTLMv2 ───────── : %s"%(self.smbClient.doesSupportNTLMv2()))
+ 469MaxReadSize=self.smbClient.getIOCapabilities()["MaxReadSize"]
+ 470self.logger.print(" │ ├─ Max size of read chunk ── : %d bytes (%s)"%(MaxReadSize,b_filesize(MaxReadSize)))
+ 471MaxWriteSize=self.smbClient.getIOCapabilities()["MaxWriteSize"]
+ 472self.logger.print(" │ └─ Max size of write chunk ─ : %d bytes (%s)"%(MaxWriteSize,b_filesize(MaxWriteSize)))
+ 473self.logger.print(" └─")
+ 474else:
+ 475self.logger.print("[+] Server:")
+ 476self.logger.print(" ├─NetBIOS:")
+ 477self.logger.print(" │ ├─ \x1b[94mNetBIOS Hostname\x1b[0m \x1b[90m────────\x1b[0m : \x1b[93m%s\x1b[0m"%(self.smbClient.getServerName()))
+ 478self.logger.print(" │ └─ \x1b[94mNetBIOS Domain\x1b[0m \x1b[90m──────────\x1b[0m : \x1b[93m%s\x1b[0m"%(self.smbClient.getServerDomain()))
+ 479self.logger.print(" ├─DNS:")
+ 480self.logger.print(" │ ├─ \x1b[94mDNS Hostname\x1b[0m \x1b[90m────────────\x1b[0m : \x1b[93m%s\x1b[0m"%(self.smbClient.getServerDNSHostName()))
+ 481self.logger.print(" │ └─ \x1b[94mDNS Domain\x1b[0m \x1b[90m──────────────\x1b[0m : \x1b[93m%s\x1b[0m"%(self.smbClient.getServerDNSDomainName()))
+ 482self.logger.print(" ├─OS:")
+ 483self.logger.print(" │ ├─ \x1b[94mOS Name\x1b[0m \x1b[90m─────────────────\x1b[0m : \x1b[93m%s\x1b[0m"%(self.smbClient.getServerOS()))
+ 484self.logger.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()))
+ 485self.logger.print(" ├─Server:")
+ 486self.logger.print(" │ ├─ \x1b[94mSigning Required\x1b[0m \x1b[90m────────\x1b[0m : \x1b[93m%s\x1b[0m"%(self.smbClient.isSigningRequired()))
+ 487self.logger.print(" │ ├─ \x1b[94mLogin Required\x1b[0m \x1b[90m──────────\x1b[0m : \x1b[93m%s\x1b[0m"%(self.smbClient.isLoginRequired()))
+ 488self.logger.print(" │ ├─ \x1b[94mSupports NTLMv2\x1b[0m \x1b[90m─────────\x1b[0m : \x1b[93m%s\x1b[0m"%(self.smbClient.doesSupportNTLMv2()))
+ 489MaxReadSize=self.smbClient.getIOCapabilities()["MaxReadSize"]
+ 490self.logger.print(" │ ├─ \x1b[94mMax size of read chunk\x1b[0m \x1b[90m──\x1b[0m : \x1b[93m%d bytes (%s)\x1b[0m"%(MaxReadSize,b_filesize(MaxReadSize)))
+ 491MaxWriteSize=self.smbClient.getIOCapabilities()["MaxWriteSize"]
+ 492self.logger.print(" │ └─ \x1b[94mMax size of write chunk\x1b[0m \x1b[90m─\x1b[0m : \x1b[93m%d bytes (%s)\x1b[0m"%(MaxWriteSize,b_filesize(MaxWriteSize)))
+ 493self.logger.print(" └─") 494
- 495 This method retrieves the contents of a directory specified by `shareName` and `path`. If `shareName` or `path`
- 496 is not provided, it defaults to the instance's current SMB share or path. The method returns a dictionary with
- 497 the long names of the files and directories as keys and their respective SMB entry objects as values.
- 498
- 499 Args:
- 500 shareName (str, optional): The name of the SMB share. Defaults to the current SMB share if None.
- 501 path (str, optional): The directory path to list contents from. Defaults to the current path if None.
- 502
- 503 Returns:
- 504 dict: A dictionary with file and directory names as keys and their SMB entry objects as values.
- 505 """
- 506
- 507dest_path=[self.smb_cwd.rstrip(ntpath.sep),]
- 508ifpathisnotNoneandlen(path)>0:
- 509dest_path.append(path.rstrip(ntpath.sep))
- 510dest_path.append('*')
- 511path=ntpath.sep.join(dest_path)
- 512
- 513contents={}
- 514entries=self.smbClient.listPath(
- 515shareName=self.smb_share,
- 516path=path
- 517)
- 518forentryinentries:
- 519contents[entry.get_longname()]=entry
- 520
- 521returncontents
- 522
- 523deflist_shares(self):
- 524"""
- 525 Lists all the shares available on the connected SMB server.
- 526
- 527 This method queries the SMB server to retrieve a list of all available shares. It populates the `shares` dictionary
- 528 with key-value pairs where the key is the share name and the value is a dictionary containing details about the share
- 529 such as its name, type, raw type, and any comments associated with the share.
- 530
- 531 Returns:
- 532 dict: A dictionary containing information about each share available on the server.
- 533 """
- 534
- 535self.available_shares={}
- 536
- 537ifself.connected:
- 538ifself.smbClientisnotNone:
- 539resp=self.smbClient.listShares()
- 540
- 541forshareinresp:
- 542# SHARE_INFO_1 structure (lmshare.h)
- 543# https://learn.microsoft.com/en-us/windows/win32/api/lmshare/ns-lmshare-share_info_1
- 544sharename=share["shi1_netname"][:-1]
- 545sharecomment=share["shi1_remark"][:-1]
- 546sharetype=share["shi1_type"]
- 547
- 548self.available_shares[sharename.lower()]={
- 549"name":sharename,
- 550"type":STYPE_MASK(sharetype),
- 551"rawtype":sharetype,
- 552"comment":sharecomment
- 553}
- 554else:
- 555print("[!] Error: SMBSession.smbClient is None.")
- 556
- 557returnself.available_shares
- 558
- 559defmkdir(self,path=None):
- 560"""
- 561 Creates a directory at the specified path on the SMB share.
- 562
- 563 This method takes a path and attempts to create the directory structure on the SMB share. If the path includes
- 564 nested directories, it will create each directory in the sequence. If a directory already exists, it will skip
- 565 the creation for that directory without raising an error.
- 566
- 567 Args:
- 568 path (str, optional): The full path of the directory to create on the SMB share. Defaults to None.
- 569
- 570 Note:
- 571 The path should use forward slashes ('/') which will be converted to backslashes (ntpath.sep) for SMB compatibility.
- 572 """
- 573
- 574ifpathisnotNone:
- 575# Prepare path
- 576path=path.replace('/',ntpath.sep)
- 577ifntpath.sepinpath:
- 578path=path.strip(ntpath.sep).split(ntpath.sep)
- 579else:
- 580path=[path]
+ 495ifshareandself.smb_shareisnotNone:
+ 496share_name=self.available_shares.get(self.smb_share.lower(),"")["name"]
+ 497share_comment=self.available_shares.get(self.smb_share.lower(),"")["comment"]
+ 498share_type=self.available_shares.get(self.smb_share.lower(),"")["type"]
+ 499share_type=', '.join([s.replace("STYPE_","")forsinshare_type])
+ 500share_rawtype=self.available_shares.get(self.smb_share.lower(),"")["rawtype"]
+ 501ifself.config.no_colors:
+ 502self.logger.print("\n[+] Share:")
+ 503self.logger.print(" ├─ Name ──────────── : %s"%(share_name))
+ 504self.logger.print(" ├─ Description ───── : %s"%(share_comment))
+ 505self.logger.print(" ├─ Type ──────────── : %s"%(share_type))
+ 506self.logger.print(" └─ Raw type value ── : %s"%(share_rawtype))
+ 507else:
+ 508self.logger.print("\n[+] Share:")
+ 509self.logger.print(" ├─ \x1b[94mName\x1b[0m \x1b[90m────────────\x1b[0m : \x1b[93m%s\x1b[0m"%(share_name))
+ 510self.logger.print(" ├─ \x1b[94mDescription\x1b[0m \x1b[90m─────\x1b[0m : \x1b[93m%s\x1b[0m"%(share_comment))
+ 511self.logger.print(" ├─ \x1b[94mType\x1b[0m \x1b[90m────────────\x1b[0m : \x1b[93m%s\x1b[0m"%(share_type))
+ 512self.logger.print(" └─ \x1b[94mRaw type value\x1b[0m \x1b[90m──\x1b[0m : \x1b[93m%s\x1b[0m"%(share_rawtype))
+ 513
+ 514deflist_contents(self,path=None):
+ 515"""
+ 516 Lists the contents of a specified directory on the SMB share.
+ 517
+ 518 This method retrieves the contents of a directory specified by `shareName` and `path`. If `shareName` or `path`
+ 519 is not provided, it defaults to the instance's current SMB share or path. The method returns a dictionary with
+ 520 the long names of the files and directories as keys and their respective SMB entry objects as values.
+ 521
+ 522 Args:
+ 523 shareName (str, optional): The name of the SMB share. Defaults to the current SMB share if None.
+ 524 path (str, optional): The directory path to list contents from. Defaults to the current path if None.
+ 525
+ 526 Returns:
+ 527 dict: A dictionary with file and directory names as keys and their SMB entry objects as values.
+ 528 """
+ 529
+ 530dest_path=[self.smb_cwd.rstrip(ntpath.sep),]
+ 531ifpathisnotNoneandlen(path)>0:
+ 532dest_path.append(path.rstrip(ntpath.sep))
+ 533dest_path.append('*')
+ 534path=ntpath.sep.join(dest_path)
+ 535
+ 536contents={}
+ 537entries=self.smbClient.listPath(
+ 538shareName=self.smb_share,
+ 539path=path
+ 540)
+ 541forentryinentries:
+ 542contents[entry.get_longname()]=entry
+ 543
+ 544returncontents
+ 545
+ 546deflist_shares(self):
+ 547"""
+ 548 Lists all the shares available on the connected SMB server.
+ 549
+ 550 This method queries the SMB server to retrieve a list of all available shares. It populates the `shares` dictionary
+ 551 with key-value pairs where the key is the share name and the value is a dictionary containing details about the share
+ 552 such as its name, type, raw type, and any comments associated with the share.
+ 553
+ 554 Returns:
+ 555 dict: A dictionary containing information about each share available on the server.
+ 556 """
+ 557
+ 558self.available_shares={}
+ 559
+ 560ifself.connected:
+ 561ifself.smbClientisnotNone:
+ 562resp=self.smbClient.listShares()
+ 563
+ 564forshareinresp:
+ 565# SHARE_INFO_1 structure (lmshare.h)
+ 566# https://learn.microsoft.com/en-us/windows/win32/api/lmshare/ns-lmshare-share_info_1
+ 567sharename=share["shi1_netname"][:-1]
+ 568sharecomment=share["shi1_remark"][:-1]
+ 569sharetype=share["shi1_type"]
+ 570
+ 571self.available_shares[sharename.lower()]={
+ 572"name":sharename,
+ 573"type":STYPE_MASK(sharetype),
+ 574"rawtype":sharetype,
+ 575"comment":sharecomment
+ 576}
+ 577else:
+ 578self.logger.error("Error: SMBSession.smbClient is None.")
+ 579
+ 580returnself.available_shares 581
- 582# Create each dir in the path
- 583fordepthinrange(1,len(path)+1):
- 584tmp_path=ntpath.sep.join(path[:depth])
- 585try:
- 586self.smbClient.createDirectory(
- 587shareName=self.smb_share,
- 588pathName=ntpath.normpath(self.smb_cwd+ntpath.sep+tmp_path+ntpath.sep)
- 589)
- 590exceptimpacket.smbconnection.SessionErroraserr:
- 591iferr.getErrorCode()==0xc0000035:
- 592# STATUS_OBJECT_NAME_COLLISION
- 593# Remote directory already created, this is normal
- 594# Src: https://github.com/fortra/impacket/blob/269ce69872f0e8f2188a80addb0c39fedfa6dcb8/impacket/nt_errors.py#L268C9-L268C19
- 595pass
- 596else:
- 597print("[!] Failed to create directory '%s': %s"%(tmp_path,err))
- 598ifself.config.debug:
- 599traceback.print_exc()
- 600else:
- 601pass
- 602
- 603defmount(self,local_mount_point,remote_path):
- 604"""
- 605 Generates the command to mount an SMB share on different platforms.
- 606
- 607 This method takes the local mount point and the remote path of the SMB share and generates the appropriate mount command based on the platform.
- 608 It constructs the mount command using the provided parameters and executes it using the os.system() function.
- 609
- 610 Args:
- 611 local_mount_point (str): The local directory where the SMB share will be mounted.
- 612 remote_path (str): The remote path on the SMB share to be mounted.
- 613
- 614 Note:
- 615 - For Windows platform, the command uses 'net use' to mount the share.
- 616 - For Linux platform, the command uses 'mount' to mount the share.
- 617 - For macOS platform, the command uses 'mount_smbfs' to mount the share.
- 618 - If the platform is not supported, an error message is displayed.
- 619
- 620 Returns:
- 621 None
- 622 """
- 623
- 624ifnotos.path.exists(local_mount_point):
- 625pass
- 626
- 627ifsys.platform.startswith('win'):
- 628remote_path=remote_path.replace('/',ntpath.sep)
- 629command=f"net use {local_mount_point}\\\\{self.address}\\{self.smb_share}\\{remote_path}"
- 630
- 631elifsys.platform.startswith('linux'):
- 632remote_path=remote_path.replace(ntpath.sep,'/')
- 633command=f"mount -t cifs //{self.address}/{self.smb_share}/{remote_path}{local_mount_point} -o username={self.username},password={self.password}"
- 634
- 635elifsys.platform.startswith('darwin'):
- 636remote_path=remote_path.replace(ntpath.sep,'/')
- 637command=f"mount_smbfs //{self.username}:{self.password}@{self.address}/{self.smb_share}/{remote_path}{local_mount_point}"
- 638
- 639else:
- 640command=None
- 641print("[!] Unsupported platform for mounting SMB share.")
- 642
- 643ifcommandisnotNone:
- 644ifself.config.debug:
- 645print("[debug] Executing: %s"%command)
- 646os.system(command)
- 647
- 648defpath_exists(self,path=None):
- 649"""
- 650 Checks if the specified path exists on the SMB share.
- 651
- 652 This method determines if a given path exists on the SMB share by attempting to list the contents of the path.
- 653 If the path listing is successful and returns one or more entries, the path is considered to exist.
- 654
- 655 Args:
- 656 path (str, optional): The path to check on the SMB share. Defaults to None.
- 657
- 658 Returns:
- 659 bool: True if the path exists, False otherwise or if an error occurs.
- 660 """
- 661
- 662ifpathisnotNone:
- 663path=path.replace('*','')
- 664try:
- 665contents=self.smbClient.listPath(
- 666shareName=self.smb_share,
- 667path=ntpath.normpath(self.smb_cwd+ntpath.sep+path+ntpath.sep)
- 668)
- 669return(len(contents)!=0)
- 670exceptExceptionase:
- 671returnFalse
- 672else:
- 673returnFalse
- 674
- 675defpath_isdir(self,pathFromRoot=None):
- 676"""
- 677 Checks if the specified path is a directory on the SMB share.
- 678
- 679 This method determines if a given path corresponds to a directory on the SMB share. It does this by listing the
- 680 contents of the path and filtering for entries that match the basename of the path and are marked as directories.
- 681
- 682 Args:
- 683 path (str, optional): The path to check on the SMB share. Defaults to None.
+ 582defmkdir(self,path=None):
+ 583"""
+ 584 Creates a directory at the specified path on the SMB share.
+ 585
+ 586 This method takes a path and attempts to create the directory structure on the SMB share. If the path includes
+ 587 nested directories, it will create each directory in the sequence. If a directory already exists, it will skip
+ 588 the creation for that directory without raising an error.
+ 589
+ 590 Args:
+ 591 path (str, optional): The full path of the directory to create on the SMB share. Defaults to None.
+ 592
+ 593 Note:
+ 594 The path should use forward slashes ('/') which will be converted to backslashes (ntpath.sep) for SMB compatibility.
+ 595 """
+ 596
+ 597ifpathisnotNone:
+ 598# Prepare path
+ 599path=path.replace('/',ntpath.sep)
+ 600ifntpath.sepinpath:
+ 601path=path.strip(ntpath.sep).split(ntpath.sep)
+ 602else:
+ 603path=[path]
+ 604
+ 605# Create each dir in the path
+ 606fordepthinrange(1,len(path)+1):
+ 607tmp_path=ntpath.sep.join(path[:depth])
+ 608try:
+ 609self.smbClient.createDirectory(
+ 610shareName=self.smb_share,
+ 611pathName=ntpath.normpath(self.smb_cwd+ntpath.sep+tmp_path+ntpath.sep)
+ 612)
+ 613exceptimpacket.smbconnection.SessionErroraserr:
+ 614iferr.getErrorCode()==0xc0000035:
+ 615# STATUS_OBJECT_NAME_COLLISION
+ 616# Remote directory already created, this is normal
+ 617# Src: https://github.com/fortra/impacket/blob/269ce69872f0e8f2188a80addb0c39fedfa6dcb8/impacket/nt_errors.py#L268C9-L268C19
+ 618pass
+ 619else:
+ 620self.logger.error("Failed to create directory '%s': %s"%(tmp_path,err))
+ 621ifself.config.debug:
+ 622traceback.print_exc()
+ 623else:
+ 624pass
+ 625
+ 626defmount(self,local_mount_point,remote_path):
+ 627"""
+ 628 Generates the command to mount an SMB share on different platforms.
+ 629
+ 630 This method takes the local mount point and the remote path of the SMB share and generates the appropriate mount command based on the platform.
+ 631 It constructs the mount command using the provided parameters and executes it using the os.system() function.
+ 632
+ 633 Args:
+ 634 local_mount_point (str): The local directory where the SMB share will be mounted.
+ 635 remote_path (str): The remote path on the SMB share to be mounted.
+ 636
+ 637 Note:
+ 638 - For Windows platform, the command uses 'net use' to mount the share.
+ 639 - For Linux platform, the command uses 'mount' to mount the share.
+ 640 - For macOS platform, the command uses 'mount_smbfs' to mount the share.
+ 641 - If the platform is not supported, an error message is displayed.
+ 642
+ 643 Returns:
+ 644 None
+ 645 """
+ 646
+ 647ifnotos.path.exists(local_mount_point):
+ 648pass
+ 649
+ 650ifsys.platform.startswith('win'):
+ 651remote_path=remote_path.replace('/',ntpath.sep)
+ 652command=f"net use {local_mount_point}\\\\{self.host}\\{self.smb_share}\\{remote_path}"
+ 653
+ 654elifsys.platform.startswith('linux'):
+ 655remote_path=remote_path.replace(ntpath.sep,'/')
+ 656command=f"mount -t cifs //{self.host}/{self.smb_share}/{remote_path}{local_mount_point} -o username={self.credentials.username},password={self.credentials.password}"
+ 657
+ 658elifsys.platform.startswith('darwin'):
+ 659remote_path=remote_path.replace(ntpath.sep,'/')
+ 660command=f"mount_smbfs //{self.credentials.username}:{self.credentials.password}@{self.host}/{self.smb_share}/{remote_path}{local_mount_point}"
+ 661
+ 662else:
+ 663command=None
+ 664self.logger.error("Unsupported platform for mounting SMB share.")
+ 665
+ 666ifcommandisnotNone:
+ 667ifself.config.debug:
+ 668self.logger.debug("Executing: %s"%command)
+ 669os.system(command)
+ 670
+ 671defpath_exists(self,path=None):
+ 672"""
+ 673 Checks if the specified path exists on the SMB share.
+ 674
+ 675 This method determines if a given path exists on the SMB share by attempting to list the contents of the path.
+ 676 If the path listing is successful and returns one or more entries, the path is considered to exist.
+ 677
+ 678 Args:
+ 679 path (str, optional): The path to check on the SMB share. Defaults to None.
+ 680
+ 681 Returns:
+ 682 bool: True if the path exists, False otherwise or if an error occurs.
+ 683 """ 684
- 685 Returns:
- 686 bool: True if the path is a directory, False otherwise or if an error occurs.
- 687 """
- 688
- 689ifpathFromRootisnotNone:
- 690# Replace slashes if any
- 691path=pathFromRoot.replace('/',ntpath.sep)
- 692
- 693# Strip wildcards to avoid injections
- 694path=path.replace('*','')
- 695
- 696# Normalize path and strip leading backslash
- 697path=ntpath.normpath(path+ntpath.sep).lstrip(ntpath.sep)
- 698
- 699ifpath.strip()in['','.','..']:
- 700# By defininition they exist on the filesystem
- 701returnTrue
- 702else:
- 703try:
- 704contents=self.smbClient.listPath(
- 705shareName=self.smb_share,
- 706path=path+'*'
- 707)
- 708# Filter on directories
- 709contents=[
- 710cforcincontents
- 711ifc.get_longname()==ntpath.basename(path)andc.is_directory()
- 712]
- 713return(len(contents)!=0)
- 714exceptExceptionase:
- 715returnFalse
- 716else:
- 717returnFalse
+ 685ifpathisnotNone:
+ 686path=path.replace('*','')
+ 687path=path.replace('/',ntpath.sep)
+ 688try:
+ 689contents=self.smbClient.listPath(
+ 690shareName=self.smb_share,
+ 691path=ntpath.normpath(self.smb_cwd+ntpath.sep+path+ntpath.sep)
+ 692)
+ 693return(len(contents)!=0)
+ 694exceptExceptionase:
+ 695returnFalse
+ 696else:
+ 697returnFalse
+ 698
+ 699defpath_isdir(self,pathFromRoot=None):
+ 700"""
+ 701 Checks if the specified path is a directory on the SMB share.
+ 702
+ 703 This method determines if a given path corresponds to a directory on the SMB share. It does this by listing the
+ 704 contents of the path and filtering for entries that match the basename of the path and are marked as directories.
+ 705
+ 706 Args:
+ 707 path (str, optional): The path to check on the SMB share. Defaults to None.
+ 708
+ 709 Returns:
+ 710 bool: True if the path is a directory, False otherwise or if an error occurs.
+ 711 """
+ 712
+ 713ifpathFromRootisnotNone:
+ 714# Strip wildcards to avoid injections
+ 715path=pathFromRoot.replace('*','')
+ 716# Replace slashes if any
+ 717path=path.replace('/',ntpath.sep) 718
- 719defpath_isfile(self,path=None):
- 720"""
- 721 Checks if the specified path is a file on the SMB share.
- 722
- 723 This method determines if a given path corresponds to a file on the SMB share. It does this by listing the
- 724 contents of the path and filtering for entries that match the basename of the path and are not marked as directories.
- 725
- 726 Args:
- 727 path (str, optional): The path to check on the SMB share. Defaults to None.
- 728
- 729 Returns:
- 730 bool: True if the path is a file, False otherwise or if an error occurs.
- 731 """
- 732
- 733ifpathisnotNone:
- 734path=path.replace('*','')
- 735search_dir=ntpath.normpath(self.smb_cwd+ntpath.sep+path)
- 736search_dir=ntpath.dirname(search_dir)+ntpath.sep+'*'
- 737try:
- 738contents=self.smbClient.listPath(
- 739shareName=self.smb_share,
- 740path=search_dir
- 741)
- 742# Filter on files
- 743contents=[
- 744cforcincontents
- 745ifc.get_longname()==ntpath.basename(path)andnotc.is_directory()
- 746]
- 747return(len(contents)!=0)
- 748exceptExceptionase:
- 749returnFalse
- 750else:
- 751returnFalse
- 752
- 753defping_smb_session(self):
- 754"""
- 755 Tests the connectivity to the SMB server by sending an echo command.
- 756
- 757 This method attempts to send an echo command to the SMB server to check if the session is still active.
- 758 It updates the `connected` attribute of the class based on the success or failure of the echo command.
- 759
- 760 Returns:
- 761 bool: True if the echo command succeeds (indicating the session is active), False otherwise.
- 762 """
- 763
- 764try:
- 765self.smbClient.getSMBServer().echo()
- 766exceptExceptionase:
- 767self.connected=False
- 768returnself.connected
- 769
- 770defput_file(self,localpath=None):
- 771"""
- 772 Uploads a single file to the SMB share.
- 773
- 774 This method takes a local file path, opens the file, and uploads it to the SMB share at the specified path.
- 775 It handles exceptions such as broken pipe errors or keyboard interrupts by closing and reinitializing the SMB session.
- 776 General exceptions are caught and logged, with a traceback provided if debugging is enabled.
- 777
- 778 Args:
- 779 localpath (str, optional): The local file path of the file to be uploaded. Defaults to None.
- 780 """
- 781
- 782# Parse path
- 783localpath=localpath.replace('/',os.path.sep)
- 784ifos.path.sepinlocalpath:
- 785tmp_search_path=os.path.normpath(os.getcwd()+os.path.sep+os.path.dirname(localpath))
- 786else:
- 787tmp_search_path=os.path.normpath(os.getcwd()+os.path.sep)
- 788# Parse filename
- 789filename=os.path.basename(localpath)
- 790
- 791# Search for the file
- 792matches=os.listdir(tmp_search_path)
- 793# Filter the entries
- 794matching_entries=[]
- 795forentryinmatches:
- 796ifentry==filename:
- 797matching_entries.append(entry)
- 798elif'*'infilename:
- 799regexp=filename.replace('.','\\.').replace('*','.*')
- 800ifre.match(regexp,entry):
- 801matching_entries.append(entry)
- 802
- 803matching_entries=sorted(list(set(matching_entries)))
+ 719# Normalize path and strip leading backslash
+ 720path=ntpath.normpath(path+ntpath.sep).lstrip(ntpath.sep)
+ 721
+ 722ifpath.strip()in['','.','..']:
+ 723# By defininition they exist on the filesystem
+ 724returnTrue
+ 725else:
+ 726try:
+ 727contents=self.smbClient.listPath(
+ 728shareName=self.smb_share,
+ 729path=path+'*'
+ 730)
+ 731# Filter on directories
+ 732contents=[
+ 733cforcincontents
+ 734ifc.get_longname()==ntpath.basename(path)andc.is_directory()
+ 735]
+ 736return(len(contents)!=0)
+ 737exceptExceptionase:
+ 738returnFalse
+ 739else:
+ 740returnFalse
+ 741
+ 742defpath_isfile(self,pathFromRoot=None):
+ 743"""
+ 744 Checks if the specified path is a file on the SMB share.
+ 745
+ 746 This method determines if a given path corresponds to a file on the SMB share. It does this by listing the
+ 747 contents of the path and filtering for entries that match the basename of the path and are not marked as directories.
+ 748
+ 749 Args:
+ 750 path (str, optional): The path to check on the SMB share. Defaults to None.
+ 751
+ 752 Returns:
+ 753 bool: True if the path is a file, False otherwise or if an error occurs.
+ 754 """
+ 755
+ 756ifpathFromRootisnotNone:
+ 757# Strip wildcards to avoid injections
+ 758path=pathFromRoot.replace('*','')
+ 759# Replace slashes if any
+ 760path=path.replace('/',ntpath.sep)
+ 761
+ 762# Normalize path and strip leading backslash
+ 763path=ntpath.normpath(path+ntpath.sep).lstrip(ntpath.sep)
+ 764
+ 765try:
+ 766contents=self.smbClient.listPath(
+ 767shareName=self.smb_share,
+ 768path=ntpath.dirname(path)+ntpath.sep+'*'
+ 769)
+ 770# Filter on files
+ 771contents=[
+ 772cforcincontents
+ 773ifc.get_longname()==ntpath.basename(path)andnotc.is_directory()
+ 774]
+ 775return(len(contents)!=0)
+ 776exceptExceptionase:
+ 777returnFalse
+ 778else:
+ 779returnFalse
+ 780
+ 781defput_file(self,localpath=None):
+ 782"""
+ 783 Uploads a single file to the SMB share.
+ 784
+ 785 This method takes a local file path, opens the file, and uploads it to the SMB share at the specified path.
+ 786 It handles exceptions such as broken pipe errors or keyboard interrupts by closing and reinitializing the SMB session.
+ 787 General exceptions are caught and logged, with a traceback provided if debugging is enabled.
+ 788
+ 789 Args:
+ 790 localpath (str, optional): The local file path of the file to be uploaded. Defaults to None.
+ 791 """
+ 792
+ 793# Parse path
+ 794localpath=localpath.replace('/',os.path.sep)
+ 795ifos.path.sepinlocalpath:
+ 796iflocalpath.startswith(os.path.sep):
+ 797# Absolute path
+ 798tmp_search_path=os.path.normpath(localpath)
+ 799else:
+ 800# Relative path
+ 801tmp_search_path=os.path.normpath(os.getcwd()+os.path.sep+os.path.dirname(localpath))
+ 802else:
+ 803tmp_search_path=os.path.normpath(os.getcwd()+os.path.sep) 804
- 805# Loop and upload
- 806forlocalpathinmatching_entries:
- 807ifos.path.exists(localpath):
- 808ifos.path.isfile(localpath):
- 809try:
- 810localfile=os.path.basename(localpath)
- 811f=LocalFileIO(
- 812mode="rb",
- 813path=localpath,
- 814debug=self.config.debug
- 815)
- 816self.smbClient.putFile(
- 817shareName=self.smb_share,
- 818pathName=ntpath.normpath(self.smb_cwd+ntpath.sep+localfile+ntpath.sep),
- 819callback=f.read
- 820)
- 821f.close()
- 822except(BrokenPipeError,KeyboardInterrupt)aserr:
- 823print("[!] Interrupted.")
- 824self.close_smb_session()
- 825self.init_smb_session()
- 826exceptExceptionaserr:
- 827print("[!] Failed to upload '%s': %s"%(localfile,err))
- 828ifself.config.debug:
- 829traceback.print_exc()
- 830else:
- 831# [!] The specified localpath is a directory. Use 'put -r <directory>' instead.
- 832pass
- 833else:
- 834# [!] The specified localpath does not exist.
- 835pass
- 836
- 837defput_file_recursively(self,localpath=None):
- 838"""
- 839 Recursively uploads files from a specified local directory to the SMB share.
- 840
- 841 This method walks through the given local directory and all its subdirectories, uploading each file to the
- 842 corresponding directory structure on the SMB share. It first checks if the local path is a directory. If it is,
- 843 it iterates over all files and directories within the local path, creating necessary directories on the SMB share
- 844 and uploading files. If the local path is not a directory, it prints an error message.
- 845
- 846 Args:
- 847 localpath (str, optional): The local directory path from which files will be uploaded. Defaults to None.
- 848 """
- 849
- 850ifos.path.exists(localpath):
- 851ifos.path.isfile(localpath):
- 852# Iterate over all files and directories within the local path
- 853local_files={}
- 854forroot,dirs,filesinos.walk(localpath):
- 855iflen(files)!=0:
- 856local_files[root]=files
- 857
- 858# Iterate over the found files
- 859forlocal_dir_pathinsorted(local_files.keys()):
- 860print("[>] Putting files of '%s'"%local_dir_path)
- 861
- 862# Create remote directory
- 863remote_dir_path=local_dir_path.replace(os.path.sep,ntpath.sep)
- 864self.mkdir(
- 865path=ntpath.normpath(self.smb_cwd+ntpath.sep+remote_dir_path+ntpath.sep)
- 866)
- 867
- 868forlocal_file_pathinlocal_files[local_dir_path]:
- 869try:
- 870f=LocalFileIO(
- 871mode="rb",
- 872path=local_dir_path+os.path.sep+local_file_path,
- 873debug=self.config.debug
- 874)
- 875self.smbClient.putFile(
- 876shareName=self.smb_share,
- 877pathName=ntpath.normpath(self.smb_cwd+ntpath.sep+remote_dir_path+ntpath.sep+local_file_path),
- 878callback=f.read
- 879)
- 880f.close()
+ 805# Parse filename
+ 806filename=os.path.basename(localpath)
+ 807
+ 808# Search for the file
+ 809matches=os.listdir(tmp_search_path)
+ 810# Filter the entries
+ 811matching_entries=[]
+ 812forentryinmatches:
+ 813ifentry==filename:
+ 814matching_entries.append(entry)
+ 815elif'*'infilename:
+ 816regexp=filename.replace('.','\\.').replace('*','.*')
+ 817ifre.match(regexp,entry):
+ 818matching_entries.append(entry)
+ 819
+ 820matching_entries=sorted(list(set(matching_entries)))
+ 821
+ 822# Loop and upload
+ 823forlocalpathinmatching_entries:
+ 824ifos.path.exists(localpath):
+ 825ifos.path.isfile(localpath):
+ 826try:
+ 827localfile=os.path.basename(localpath)
+ 828f=LocalFileIO(
+ 829mode="rb",
+ 830path=localpath,
+ 831debug=self.config.debug
+ 832)
+ 833self.smbClient.putFile(
+ 834shareName=self.smb_share,
+ 835pathName=ntpath.normpath(self.smb_cwd+ntpath.sep+localfile+ntpath.sep),
+ 836callback=f.read
+ 837)
+ 838f.close()
+ 839
+ 840except(BrokenPipeError,KeyboardInterrupt)aserr:
+ 841self.logger.error("Interrupted.")
+ 842self.close_smb_session()
+ 843self.init_smb_session()
+ 844
+ 845except(Exception,PermissionError)aserr:
+ 846f.set_error(message="[bold red]Failed uploading '%s': %s"%(f.path,err))
+ 847f.close(remove=False)
+ 848ifself.config.debug:
+ 849traceback.print_exc()
+ 850else:
+ 851# [!] The specified localpath is a directory. Use 'put -r <directory>' instead.
+ 852pass
+ 853else:
+ 854# [!] The specified localpath does not exist.
+ 855pass
+ 856
+ 857defput_file_recursively(self,localpath=None):
+ 858"""
+ 859 Recursively uploads files from a specified local directory to the SMB share.
+ 860
+ 861 This method walks through the given local directory and all its subdirectories, uploading each file to the
+ 862 corresponding directory structure on the SMB share. It first checks if the local path is a directory. If it is,
+ 863 it iterates over all files and directories within the local path, creating necessary directories on the SMB share
+ 864 and uploading files. If the local path is not a directory, it prints an error message.
+ 865
+ 866 Args:
+ 867 localpath (str, optional): The local directory path from which files will be uploaded. Defaults to None.
+ 868 """
+ 869
+ 870ifos.path.exists(localpath):
+ 871ifos.path.isdir(localpath):
+ 872# Iterate over all files and directories within the local path
+ 873local_files={}
+ 874forroot,dirs,filesinos.walk(localpath):
+ 875iflen(files)!=0:
+ 876local_files[root]=files
+ 877
+ 878# Iterate over the found files
+ 879forlocal_dir_pathinsorted(local_files.keys()):
+ 880self.logger.print("[>] Putting files of '%s'"%local_dir_path) 881
- 882exceptBrokenPipeErroraserr:
- 883f.set_error(message="[bold red]Failed uploading '%s': %s"%(f.path,err))
- 884f.close(remove=True)
- 885break
- 886exceptExceptionaserr:
- 887f.set_error(message="[bold red]Failed uploading '%s': %s"%(f.path,err))
- 888f.close(remove=True)
- 889else:
- 890print("[!] The specified localpath is a file. Use 'put <file>' instead.")
- 891else:
- 892print("[!] The specified localpath does not exist.")
- 893
- 894defrmdir(self,path=None):
- 895"""
- 896 Removes a directory from the SMB share at the specified path.
- 897
- 898 This method attempts to delete a directory located at the given path on the SMB share. If the operation fails,
- 899 it prints an error message indicating the failure and the reason. If debugging is enabled, it also prints
- 900 the stack trace of the exception.
+ 882# Create remote directory
+ 883remote_dir_path=local_dir_path.replace(os.path.sep,ntpath.sep)
+ 884self.mkdir(
+ 885path=ntpath.normpath(remote_dir_path+ntpath.sep)
+ 886)
+ 887
+ 888forlocal_file_pathinlocal_files[local_dir_path]:
+ 889try:
+ 890f=LocalFileIO(
+ 891mode="rb",
+ 892path=local_dir_path+os.path.sep+local_file_path,
+ 893debug=self.config.debug
+ 894)
+ 895self.smbClient.putFile(
+ 896shareName=self.smb_share,
+ 897pathName=ntpath.normpath(self.smb_cwd+ntpath.sep+remote_dir_path+ntpath.sep+local_file_path),
+ 898callback=f.read
+ 899)
+ 900f.close() 901
- 902 Args:
- 903 path (str, optional): The path of the directory to be removed on the SMB share. Defaults to None.
- 904 """
- 905try:
- 906self.smbClient.deleteDirectory(
- 907shareName=self.smb_share,
- 908pathName=ntpath.normpath(self.smb_cwd+ntpath.sep+path),
- 909)
- 910exceptExceptionaserr:
- 911print("[!] Failed to remove directory '%s': %s"%(path,err))
- 912ifself.config.debug:
- 913traceback.print_exc()
- 914
- 915defrm(self,path=None):
- 916"""
- 917 Removes a file from the SMB share at the specified path.
- 918
- 919 This method attempts to delete a file located at the given path on the SMB share. If the operation fails,
- 920 it prints an error message indicating the failure and the reason. If debugging is enabled, it also prints
- 921 the stack trace of the exception.
- 922
- 923 Args:
- 924 path (str, optional): The path of the file to be removed on the SMB share. Defaults to None.
- 925 """
- 926
- 927# Parse path
- 928path=path.replace('/',ntpath.sep)
- 929ifntpath.sepinpath:
- 930tmp_search_path=ntpath.normpath(self.smb_cwd+ntpath.sep+ntpath.dirname(path))
- 931else:
- 932tmp_search_path=ntpath.normpath(self.smb_cwd+ntpath.sep)
- 933# Parse filename
- 934filename=ntpath.basename(path)
- 935
- 936# Search for the file
- 937matches=self.smbClient.listPath(
- 938shareName=self.smb_share,
- 939path=tmp_search_path+ntpath.sep+'*'
- 940)
+ 902except(BrokenPipeError,KeyboardInterrupt)aserr:
+ 903self.logger.error("Interrupted.")
+ 904self.close_smb_session()
+ 905self.init_smb_session()
+ 906
+ 907except(Exception,PermissionError)aserr:
+ 908f.set_error(message="[bold red]Failed uploading '%s': %s"%(f.path,err))
+ 909f.close(remove=False)
+ 910ifself.config.debug:
+ 911traceback.print_exc()
+ 912else:
+ 913self.logger.error("The specified localpath is a file. Use 'put <file>' instead.")
+ 914else:
+ 915self.logger.error("The specified localpath does not exist.")
+ 916
+ 917defread_file(self,path=None):
+ 918"""
+ 919 Reads a file from the SMB share.
+ 920
+ 921 This method attempts to read the contents of a file specified by the `path` parameter from the SMB share.
+ 922 It constructs the full path to the file, checks if the path is a valid file, and then reads the file content
+ 923 into a byte stream which is returned to the caller.
+ 924
+ 925 Args:
+ 926 path (str, optional): The path of the file to be read from the SMB share. Defaults to None.
+ 927
+ 928 Returns:
+ 929 bytes: The content of the file as a byte stream, or None if the file does not exist or an error occurs.
+ 930 """
+ 931
+ 932ifself.path_isfile(pathFromRoot=path):
+ 933path=path.replace('/',ntpath.sep)
+ 934ifpath.startswith(ntpath.sep):
+ 935# Absolute path
+ 936tmp_file_path=ntpath.normpath(path)
+ 937else:
+ 938# Relative path
+ 939tmp_file_path=ntpath.normpath(self.smb_cwd+ntpath.sep+path)
+ 940tmp_file_path=tmp_file_path.lstrip(ntpath.sep) 941
- 942# Filter the entries
- 943matching_entries=[]
- 944forentryinmatches:
- 945ifentry.is_directory():
- 946# Skip directories
- 947continue
- 948ifentry.get_longname()==filename:
- 949matching_entries.append(entry)
- 950elif'*'infilename:
- 951regexp=filename.replace('.','\\.').replace('*','.*')
- 952ifre.match(regexp,entry.get_longname()):
- 953matching_entries.append(entry)
- 954
- 955matching_entries=sorted(list(set(matching_entries)),key=lambdax:x.get_longname())
- 956
- 957forentryinmatching_entries:
- 958try:
- 959self.smbClient.deleteFile(
- 960shareName=self.smb_share,
- 961pathName=ntpath.normpath(tmp_search_path+ntpath.sep+entry.get_longname()),
- 962)
- 963exceptExceptionaserr:
- 964print("[!] Failed to remove file '%s': %s"%(path,err))
- 965ifself.config.debug:
- 966traceback.print_exc()
- 967
- 968deftree(self,path=None):
- 969"""
- 970 Recursively lists the directory structure of the SMB share starting from the specified path.
- 971
- 972 This function prints a visual representation of the directory tree of the remote SMB share. It uses
- 973 recursion to navigate through directories and lists all files and subdirectories in each directory.
- 974 The output is color-coded and formatted to enhance readability, with directories highlighted in cyan.
+ 942fh=io.BytesIO()
+ 943try:
+ 944# opening the files in streams instead of mounting shares allows
+ 945# for running the script from unprivileged containers
+ 946self.smbClient.getFile(self.smb_share,tmp_file_path,fh.write)
+ 947exceptimpacket.smbconnection.SessionErrorase:
+ 948returnNone
+ 949rawdata=fh.getvalue()
+ 950fh.close()
+ 951returnrawdata
+ 952else:
+ 953returnNone
+ 954
+ 955defrmdir(self,path=None):
+ 956"""
+ 957 Removes a directory from the SMB share at the specified path.
+ 958
+ 959 This method attempts to delete a directory located at the given path on the SMB share. If the operation fails,
+ 960 it prints an error message indicating the failure and the reason. If debugging is enabled, it also prints
+ 961 the stack trace of the exception.
+ 962
+ 963 Args:
+ 964 path (str, optional): The path of the directory to be removed on the SMB share. Defaults to None.
+ 965 """
+ 966try:
+ 967self.smbClient.deleteDirectory(
+ 968shareName=self.smb_share,
+ 969pathName=ntpath.normpath(self.smb_cwd+ntpath.sep+path),
+ 970)
+ 971exceptExceptionaserr:
+ 972self.logger.error("Failed to remove directory '%s': %s"%(path,err))
+ 973ifself.config.debug:
+ 974traceback.print_exc() 975
- 976 Args:
- 977 path (str, optional): The starting path on the SMB share from which to begin listing the tree.
- 978 Defaults to the root of the current share.
- 979 """
- 980
- 981defrecurse_action(base_dir="",path=[],prompt=[]):
- 982bars=["│ ","├── ","└── "]
+ 976defrm(self,path=None):
+ 977"""
+ 978 Removes a file from the SMB share at the specified path.
+ 979
+ 980 This method attempts to delete a file located at the given path on the SMB share. If the operation fails,
+ 981 it prints an error message indicating the failure and the reason. If debugging is enabled, it also prints
+ 982 the stack trace of the exception. 983
- 984remote_smb_path=ntpath.normpath(base_dir+ntpath.sep+ntpath.sep.join(path))
- 985
- 986entries=[]
- 987try:
- 988entries=self.smbClient.listPath(
- 989shareName=self.smb_share,
- 990path=remote_smb_path+'\\*'
- 991)
- 992exceptimpacket.smbconnection.SessionErroraserr:
- 993code,const,text=err.getErrorCode(),err.getErrorString()[0],err.getErrorString()[1]
- 994errmsg="Error 0x%08x (%s): %s"%(code,const,text)
- 995ifself.config.no_colors:
- 996print("%s%s"%(''.join(prompt+[bars[2]]),errmsg))
- 997else:
- 998print("%s\x1b[1;91m%s\x1b[0m"%(''.join(prompt+[bars[2]]),errmsg))
- 999return
-1000
-1001entries=[eforeinentriesife.get_longname()notin[".",".."]]
-1002entries=sorted(entries,key=lambdax:x.get_longname())
-1003
-1004#
-1005iflen(entries)>1:
-1006index=0
-1007forentryinentries:
-1008index+=1
-1009# This is the first entry
-1010ifindex==0:
-1011ifentry.is_directory():
-1012ifself.config.no_colors:
-1013print("%s%s\\"%(''.join(prompt+[bars[1]]),entry.get_longname()))
-1014else:
-1015print("%s\x1b[1;96m%s\x1b[0m\\"%(''.join(prompt+[bars[1]]),entry.get_longname()))
-1016recurse_action(
-1017base_dir=base_dir,
-1018path=path+[entry.get_longname()],
-1019prompt=prompt+["│ "]
-1020)
-1021else:
-1022ifself.config.no_colors:
-1023print("%s%s"%(''.join(prompt+[bars[1]]),entry.get_longname()))
-1024else:
-1025print("%s\x1b[1m%s\x1b[0m"%(''.join(prompt+[bars[1]]),entry.get_longname()))
-1026
-1027# This is the last entry
-1028elifindex==len(entries):
-1029ifentry.is_directory():
-1030ifself.config.no_colors:
-1031print("%s%s\\"%(''.join(prompt+[bars[2]]),entry.get_longname()))
-1032else:
-1033print("%s\x1b[1;96m%s\x1b[0m\\"%(''.join(prompt+[bars[2]]),entry.get_longname()))
-1034recurse_action(
-1035base_dir=base_dir,
-1036path=path+[entry.get_longname()],
-1037prompt=prompt+[" "]
-1038)
-1039else:
-1040ifself.config.no_colors:
-1041print("%s%s"%(''.join(prompt+[bars[2]]),entry.get_longname()))
-1042else:
-1043print("%s\x1b[1m%s\x1b[0m"%(''.join(prompt+[bars[2]]),entry.get_longname()))
-1044
-1045# These are entries in the middle
-1046else:
-1047ifentry.is_directory():
-1048ifself.config.no_colors:
-1049print("%s%s\\"%(''.join(prompt+[bars[1]]),entry.get_longname()))
-1050else:
-1051print("%s\x1b[1;96m%s\x1b[0m\\"%(''.join(prompt+[bars[1]]),entry.get_longname()))
-1052recurse_action(
-1053base_dir=base_dir,
-1054path=path+[entry.get_longname()],
-1055prompt=prompt+["│ "]
-1056)
-1057else:
-1058ifself.config.no_colors:
-1059print("%s%s"%(''.join(prompt+[bars[1]]),entry.get_longname()))
-1060else:
-1061print("%s\x1b[1m%s\x1b[0m"%(''.join(prompt+[bars[1]]),entry.get_longname()))
-1062
-1063#
-1064eliflen(entries)==1:
-1065entry=entries[0]
-1066ifentry.is_directory():
-1067ifself.config.no_colors:
-1068print("%s%s\\"%(''.join(prompt+[bars[2]]),entry.get_longname()))
-1069else:
-1070print("%s\x1b[1;96m%s\x1b[0m\\"%(''.join(prompt+[bars[2]]),entry.get_longname()))
-1071recurse_action(
-1072base_dir=base_dir,
-1073path=path+[entry.get_longname()],
-1074prompt=prompt+[" "]
-1075)
-1076else:
-1077ifself.config.no_colors:
-1078print("%s%s"%(''.join(prompt+[bars[2]]),entry.get_longname()))
-1079else:
-1080print("%s\x1b[1m%s\x1b[0m"%(''.join(prompt+[bars[2]]),entry.get_longname()))
-1081
-1082# Entrypoint
-1083try:
-1084ifself.config.no_colors:
-1085print("%s\\"%path)
-1086else:
-1087print("\x1b[1;96m%s\x1b[0m\\"%path)
-1088recurse_action(
-1089base_dir=self.smb_cwd,
-1090path=[path],
-1091prompt=[""]
-1092)
-1093except(BrokenPipeError,KeyboardInterrupt)ase:
-1094print("[!] Interrupted.")
-1095self.close_smb_session()
-1096self.init_smb_session()
-1097
-1098defumount(self,local_mount_point):
-1099"""
-1100 Unmounts the specified local mount point of the remote share.
-1101
-1102 This method unmounts the specified local mount point of the remote share based on the platform.
-1103 It supports Windows, Linux, and macOS platforms for unmounting.
-1104
-1105 Parameters:
-1106 local_mount_point (str): The local mount point to unmount.
-1107
-1108 Raises:
-1109 None
-1110 """
-1111
-1112ifos.path.exists(local_mount_point):
-1113ifsys.platform.startswith('win'):
-1114command=f"net use {local_mount_point} /delete"
-1115
-1116elifsys.platform.startswith('linux')orsys.platform.startswith('darwin'):
-1117command=f"umount {local_mount_point}"
-1118
-1119else:
-1120command=None
-1121print("[!] Unsupported platform for unmounting SMB share.")
-1122
-1123ifcommandisnotNone:
-1124ifself.config.debug:
-1125print("[debug] Executing: %s"%command)
-1126os.system(command)
-1127else:
-1128print("[!] Cannot unmount a non existing path.")
-1129
-1130# Other functions
-1131
-1132deftest_rights(self,sharename):
-1133"""
-1134 Tests the read and write access rights of the current SMB session.
-1135
-1136 This method checks the read and write access rights of the current SMB session by attempting to list paths and create/delete temporary directories.
-1137
-1138 Returns:
-1139 dict: A dictionary containing the read and write access rights status.
-1140 - "readable" (bool): Indicates if the session has read access rights.
-1141 - "writable" (bool): Indicates if the session has write access rights.
-1142 """
-1143
-1144# Restore the current share
-1145current_share=self.smb_share
-1146self.set_share(shareName=sharename)
-1147
-1148access_rights={"readable":False,"writable":False}
-1149try:
-1150self.smbClient.listPath(self.smb_share,'*',password=None)
-1151access_rights["readable"]=True
-1152exceptimpacket.smbconnection.SessionErrorase:
-1153access_rights["readable"]=False
-1154
-1155try:
-1156temp_dir=ntpath.normpath("\\"+''.join([random.choice("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPRSTUVWXYZ0123456759")forkinrange(16)]))
-1157self.smbClient.createDirectory(self.smb_share,temp_dir)
-1158self.smbClient.deleteDirectory(self.smb_share,temp_dir)
-1159access_rights["writable"]=True
-1160exceptimpacket.smbconnection.SessionErrorase:
-1161access_rights["writable"]=False
+ 984 Args:
+ 985 path (str, optional): The path of the file to be removed on the SMB share. Defaults to None.
+ 986 """
+ 987
+ 988# Parse path
+ 989path=path.replace('/',ntpath.sep)
+ 990ifntpath.sepinpath:
+ 991tmp_search_path=ntpath.normpath(self.smb_cwd+ntpath.sep+ntpath.dirname(path))
+ 992else:
+ 993tmp_search_path=ntpath.normpath(self.smb_cwd+ntpath.sep)
+ 994# Parse filename
+ 995filename=ntpath.basename(path)
+ 996
+ 997# Search for the file
+ 998matches=self.smbClient.listPath(
+ 999shareName=self.smb_share,
+1000path=tmp_search_path+ntpath.sep+'*'
+1001)
+1002
+1003# Filter the entries
+1004matching_entries=[]
+1005forentryinmatches:
+1006ifentry.is_directory():
+1007# Skip directories
+1008continue
+1009ifentry.get_longname()==filename:
+1010matching_entries.append(entry)
+1011elif'*'infilename:
+1012regexp=filename.replace('.','\\.').replace('*','.*')
+1013ifre.match(regexp,entry.get_longname()):
+1014matching_entries.append(entry)
+1015
+1016matching_entries=sorted(list(set(matching_entries)),key=lambdax:x.get_longname())
+1017
+1018forentryinmatching_entries:
+1019try:
+1020self.smbClient.deleteFile(
+1021shareName=self.smb_share,
+1022pathName=ntpath.normpath(tmp_search_path+ntpath.sep+entry.get_longname()),
+1023)
+1024exceptExceptionaserr:
+1025self.logger.error("Failed to remove file '%s': %s"%(path,err))
+1026ifself.config.debug:
+1027traceback.print_exc()
+1028
+1029deftree(self,path=None):
+1030"""
+1031 Recursively lists the directory structure of the SMB share starting from the specified path.
+1032
+1033 This function prints a visual representation of the directory tree of the remote SMB share. It uses
+1034 recursion to navigate through directories and lists all files and subdirectories in each directory.
+1035 The output is color-coded and formatted to enhance readability, with directories highlighted in cyan.
+1036
+1037 Args:
+1038 path (str, optional): The starting path on the SMB share from which to begin listing the tree.
+1039 Defaults to the root of the current share.
+1040 """
+1041
+1042defrecurse_action(base_dir="",path=[],prompt=[]):
+1043bars=["│ ","├── ","└── "]
+1044
+1045remote_smb_path=ntpath.normpath(base_dir+ntpath.sep+ntpath.sep.join(path))
+1046
+1047entries=[]
+1048try:
+1049entries=self.smbClient.listPath(
+1050shareName=self.smb_share,
+1051path=remote_smb_path+'\\*'
+1052)
+1053exceptimpacket.smbconnection.SessionErroraserr:
+1054code,const,text=err.getErrorCode(),err.getErrorString()[0],err.getErrorString()[1]
+1055errmsg="Error 0x%08x (%s): %s"%(code,const,text)
+1056ifself.config.no_colors:
+1057self.logger.print("%s%s"%(''.join(prompt+[bars[2]]),errmsg))
+1058else:
+1059self.logger.print("%s\x1b[1;91m%s\x1b[0m"%(''.join(prompt+[bars[2]]),errmsg))
+1060return
+1061
+1062entries=[eforeinentriesife.get_longname()notin[".",".."]]
+1063entries=sorted(entries,key=lambdax:x.get_longname())
+1064
+1065#
+1066iflen(entries)>1:
+1067index=0
+1068forentryinentries:
+1069index+=1
+1070# This is the first entry
+1071ifindex==0:
+1072ifentry.is_directory():
+1073ifself.config.no_colors:
+1074self.logger.print("%s%s\\"%(''.join(prompt+[bars[1]]),entry.get_longname()))
+1075else:
+1076self.logger.print("%s\x1b[1;96m%s\x1b[0m\\"%(''.join(prompt+[bars[1]]),entry.get_longname()))
+1077recurse_action(
+1078base_dir=base_dir,
+1079path=path+[entry.get_longname()],
+1080prompt=prompt+["│ "]
+1081)
+1082else:
+1083ifself.config.no_colors:
+1084self.logger.print("%s%s"%(''.join(prompt+[bars[1]]),entry.get_longname()))
+1085else:
+1086self.logger.print("%s\x1b[1m%s\x1b[0m"%(''.join(prompt+[bars[1]]),entry.get_longname()))
+1087
+1088# This is the last entry
+1089elifindex==len(entries):
+1090ifentry.is_directory():
+1091ifself.config.no_colors:
+1092self.logger.print("%s%s\\"%(''.join(prompt+[bars[2]]),entry.get_longname()))
+1093else:
+1094self.logger.print("%s\x1b[1;96m%s\x1b[0m\\"%(''.join(prompt+[bars[2]]),entry.get_longname()))
+1095recurse_action(
+1096base_dir=base_dir,
+1097path=path+[entry.get_longname()],
+1098prompt=prompt+[" "]
+1099)
+1100else:
+1101ifself.config.no_colors:
+1102self.logger.print("%s%s"%(''.join(prompt+[bars[2]]),entry.get_longname()))
+1103else:
+1104self.logger.print("%s\x1b[1m%s\x1b[0m"%(''.join(prompt+[bars[2]]),entry.get_longname()))
+1105
+1106# These are entries in the middle
+1107else:
+1108ifentry.is_directory():
+1109ifself.config.no_colors:
+1110self.logger.print("%s%s\\"%(''.join(prompt+[bars[1]]),entry.get_longname()))
+1111else:
+1112self.logger.print("%s\x1b[1;96m%s\x1b[0m\\"%(''.join(prompt+[bars[1]]),entry.get_longname()))
+1113recurse_action(
+1114base_dir=base_dir,
+1115path=path+[entry.get_longname()],
+1116prompt=prompt+["│ "]
+1117)
+1118else:
+1119ifself.config.no_colors:
+1120self.logger.print("%s%s"%(''.join(prompt+[bars[1]]),entry.get_longname()))
+1121else:
+1122self.logger.print("%s\x1b[1m%s\x1b[0m"%(''.join(prompt+[bars[1]]),entry.get_longname()))
+1123
+1124#
+1125eliflen(entries)==1:
+1126entry=entries[0]
+1127ifentry.is_directory():
+1128ifself.config.no_colors:
+1129self.logger.print("%s%s\\"%(''.join(prompt+[bars[2]]),entry.get_longname()))
+1130else:
+1131self.logger.print("%s\x1b[1;96m%s\x1b[0m\\"%(''.join(prompt+[bars[2]]),entry.get_longname()))
+1132recurse_action(
+1133base_dir=base_dir,
+1134path=path+[entry.get_longname()],
+1135prompt=prompt+[" "]
+1136)
+1137else:
+1138ifself.config.no_colors:
+1139self.logger.print("%s%s"%(''.join(prompt+[bars[2]]),entry.get_longname()))
+1140else:
+1141self.logger.print("%s\x1b[1m%s\x1b[0m"%(''.join(prompt+[bars[2]]),entry.get_longname()))
+1142
+1143# Entrypoint
+1144try:
+1145ifself.config.no_colors:
+1146self.logger.print("%s\\"%path)
+1147else:
+1148self.logger.print("\x1b[1;96m%s\x1b[0m\\"%path)
+1149recurse_action(
+1150base_dir=self.smb_cwd,
+1151path=[path],
+1152prompt=[""]
+1153)
+1154except(BrokenPipeError,KeyboardInterrupt)ase:
+1155self.logger.error("Interrupted.")
+1156self.close_smb_session()
+1157self.init_smb_session()
+1158
+1159defumount(self,local_mount_point):
+1160"""
+1161 Unmounts the specified local mount point of the remote share.1162
-1163# Restore the current share
-1164self.set_share(shareName=current_share)
+1163 This method unmounts the specified local mount point of the remote share based on the platform.
+1164 It supports Windows, Linux, and macOS platforms for unmounting.1165
-1166returnaccess_rights
-1167
-1168# Setter / Getter
-1169
-1170defset_share(self,shareName):
-1171"""
-1172 Sets the current SMB share to the specified share name.
-1173
-1174 This method updates the SMB session to use the specified share name. It checks if the share name is valid
-1175 and updates the smb_share attribute of the SMBSession instance.
+1166 Parameters:
+1167 local_mount_point (str): The local mount point to unmount.
+1168
+1169 Raises:
+1170 None
+1171 """
+1172
+1173ifos.path.exists(local_mount_point):
+1174ifsys.platform.startswith('win'):
+1175command=f"net use {local_mount_point} /delete"1176
-1177 Parameters:
-1178 shareName (str): The name of the share to set as the current SMB share.
+1177elifsys.platform.startswith('linux')orsys.platform.startswith('darwin'):
+1178command=f"umount {local_mount_point}"1179
-1180 Raises:
-1181 ValueError: If the shareName is None or an empty string.
-1182 """
-1183
-1184ifshareNameisnotNone:
-1185self.list_shares()
-1186ifshareName.lower()inself.available_shares.keys():
-1187# Doing this in order to keep the case of the share adevertised by the remote machine
-1188self.smb_share=self.available_shares[shareName.lower()]["name"]
-1189self.smb_cwd=""
-1190# Connects the tree
-1191self.smb_tree_id=self.smbClient.connectTree(self.smb_share)
-1192else:
-1193print("[!] Could not set share '%s', it does not exist remotely."%shareName)
-1194else:
-1195self.smb_share=None
-1196
-1197defset_cwd(self,path=None):
-1198"""
-1199 Sets the current working directory on the SMB share to the specified path.
-1200
-1201 This method updates the current working directory (cwd) of the SMB session to the given path if it is a valid directory.
-1202 If the specified path is not a directory, the cwd remains unchanged.
+1180else:
+1181command=None
+1182self.logger.error("Unsupported platform for unmounting SMB share.")
+1183
+1184ifcommandisnotNone:
+1185self.logger.debug("Executing: %s"%command)
+1186os.system(command)
+1187else:
+1188self.logger.error("Cannot unmount a non existing path.")
+1189
+1190# Other functions
+1191
+1192deftest_rights(self,sharename):
+1193"""
+1194 Tests the read and write access rights of the current SMB session.
+1195
+1196 This method checks the read and write access rights of the current SMB session by attempting to list paths and create/delete temporary directories.
+1197
+1198 Returns:
+1199 dict: A dictionary containing the read and write access rights status.
+1200 - "readable" (bool): Indicates if the session has read access rights.
+1201 - "writable" (bool): Indicates if the session has write access rights.
+1202 """1203
-1204 Parameters:
-1205 path (str): The path to set as the current working directory.
-1206
-1207 Raises:
-1208 ValueError: If the specified path is not a directory.
-1209 """
-1210
-1211ifpathisnotNone:
-1212# Set path separators to ntpath sep
-1213if'/'inpath:
-1214path=path.replace('/',ntpath.sep)
-1215
-1216ifpath.startswith(ntpath.sep):
-1217# Absolute path
-1218path=path+ntpath.sep
-1219else:
-1220# Relative path to the CWD
-1221iflen(self.smb_cwd)==0:
-1222path=path+ntpath.sep
-1223else:
-1224path=self.smb_cwd+ntpath.sep+path
-1225
-1226# Path normalization
-1227path=ntpath.normpath(path)
-1228path=re.sub(r'\\+',r'\\',path)
+1204# Restore the current share
+1205current_share=self.smb_share
+1206self.set_share(shareName=sharename)
+1207
+1208access_rights={"readable":False,"writable":False}
+1209try:
+1210self.smbClient.listPath(self.smb_share,'*',password=None)
+1211access_rights["readable"]=True
+1212exceptimpacket.smbconnection.SessionErrorase:
+1213access_rights["readable"]=False
+1214
+1215try:
+1216temp_dir=ntpath.normpath("\\"+''.join([random.choice("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPRSTUVWXYZ0123456759")forkinrange(16)]))
+1217self.smbClient.createDirectory(self.smb_share,temp_dir)
+1218self.smbClient.deleteDirectory(self.smb_share,temp_dir)
+1219access_rights["writable"]=True
+1220exceptimpacket.smbconnection.SessionErrorase:
+1221access_rights["writable"]=False
+1222
+1223# Restore the current share
+1224self.set_share(shareName=current_share)
+1225
+1226returnaccess_rights
+1227
+1228# Setter / Getter1229
-1230ifpathin["",".",".."]:
-1231self.smb_cwd=""
-1232else:
-1233ifself.path_isdir(pathFromRoot=path.strip(ntpath.sep)):
-1234# Path exists on the remote
-1235self.smb_cwd=ntpath.normpath(path)
-1236else:
-1237# Path does not exists or is not a directory on the remote
-1238print("[!] Remote directory '%s' does not exist."%path)
+1230defset_share(self,shareName):
+1231"""
+1232 Sets the current SMB share to the specified share name.
+1233
+1234 This method updates the SMB session to use the specified share name. It checks if the share name is valid
+1235 and updates the smb_share attribute of the SMBSession instance.
+1236
+1237 Parameters:
+1238 shareName (str): The name of the share to set as the current SMB share.
+1239
+1240 Raises:
+1241 ValueError: If the shareName is None or an empty string.
+1242 """
+1243
+1244ifshareNameisnotNone:
+1245self.list_shares()
+1246ifshareName.lower()inself.available_shares.keys():
+1247# Doing this in order to keep the case of the share adevertised by the remote machine
+1248self.smb_share=self.available_shares[shareName.lower()]["name"]
+1249self.smb_cwd=""
+1250# Connects the tree
+1251self.smb_tree_id=self.smbClient.connectTree(self.smb_share)
+1252else:
+1253self.logger.error("Could not set share '%s', it does not exist remotely."%shareName)
+1254else:
+1255self.smb_share=None
+1256
+1257defset_cwd(self,path=None):
+1258"""
+1259 Sets the current working directory on the SMB share to the specified path.
+1260
+1261 This method updates the current working directory (cwd) of the SMB session to the given path if it is a valid directory.
+1262 If the specified path is not a directory, the cwd remains unchanged.
+1263
+1264 Parameters:
+1265 path (str): The path to set as the current working directory.
+1266
+1267 Raises:
+1268 ValueError: If the specified path is not a directory.
+1269 """
+1270
+1271ifpathisnotNone:
+1272# Set path separators to ntpath sep
+1273if'/'inpath:
+1274path=path.replace('/',ntpath.sep)
+1275
+1276ifpath.startswith(ntpath.sep):
+1277# Absolute path
+1278path=path+ntpath.sep
+1279else:
+1280# Relative path to the CWD
+1281iflen(self.smb_cwd)==0:
+1282path=path+ntpath.sep
+1283else:
+1284path=self.smb_cwd+ntpath.sep+path
+1285
+1286# Path normalization
+1287path=ntpath.normpath(path)
+1288path=re.sub(r'\\+',r'\\',path)
+1289
+1290ifpathin["",".",".."]:
+1291self.smb_cwd=""
+1292else:
+1293ifself.path_isdir(pathFromRoot=path.strip(ntpath.sep)):
+1294# Path exists on the remote
+1295self.smb_cwd=ntpath.normpath(path)
+1296else:
+1297# Path does not exists or is not a directory on the remote
+1298self.logger.error("Remote directory '%s' does not exist."%path)
-
Class SMBSession is designed to handle the session management for SMB (Server Message Block) protocol connections.
-It provides functionalities to connect to an SMB server, authenticate using either NTLM or Kerberos, and manage SMB shares.
+
Represents an SMB session for interacting with an SMB server.
+
+
This class provides methods to manage and interact with an SMB server, including
+connecting to the server, listing shares, uploading and downloading files, and
+managing directories and files on the server. It handles session initialization,
+authentication, and cleanup.
Attributes:
- address (str): The IP address or hostname of the SMB server.
- domain (str): The domain name for SMB server authentication.
- username (str): The username for SMB server authentication.
- password (str): The password for SMB server authentication.
- lmhash (str): The LM hash of the user's password, if available.
- nthash (str): The NT hash of the user's password, if available.
- use_kerberos (bool): A flag to determine whether to use Kerberos for authentication.
- kdcHost (str): The Key Distribution Center (KDC) host for Kerberos authentication.
- debug (bool): A flag to enable debug output.
- smbClient (object): The SMB client object used for the connection.
- connected (bool): A flag to check the status of the connection.
+ host (str): The hostname or IP address of the SMB server.
+ port (int): The port number on which the SMB server is listening.
+ credentials (dict): Authentication credentials for the SMB server.
+ config (dict, optional): Configuration options for the SMB session.
+ smbClient (impacket.smbconnection.SMBConnection): The SMB connection instance.
+ connected (bool): Connection status to the SMB server.
+ available_shares (dict): A dictionary of available SMB shares.
smb_share (str): The current SMB share in use.
- smb_path (str): The current path within the SMB share.
+ smb_cwd (str): The current working directory on the SMB share.
+ smb_tree_id (int): The tree ID of the connected SMB share.
Methods:
- __init__(address, domain, username, password, lmhash, nthash, use_kerberos=False, kdcHost=None, debug=False):
- Initializes the SMBSession with the specified parameters.
- init_smb_session():
- Initializes the SMB session by connecting to the server and authenticating using the specified method.
+ close_smb_session(): Closes the current SMB session.
+ init_smb_session(): Initializes the SMB session with the server.
+ list_shares(): Lists all shares available on the SMB server.
+ set_share(shareName): Sets the current SMB share.
+ set_cwd(path): Sets the current working directory on the SMB share.
+ put_file(localpath): Uploads a file to the current SMB share.
+ get_file(remotepath, localpath): Downloads a file from the SMB share.
+ mkdir(path): Creates a directory on the SMB share.
+ rmdir(path): Removes a directory from the SMB share.
+ rm(path): Removes a file from the SMB share.
+ read_file(path): Reads a file from the SMB share.
+ test_rights(sharename): Tests read and write access rights on a share.
77definit_smb_session(self):
- 78"""
- 79 Initializes and establishes a session with the SMB server.
- 80
- 81 This method sets up the SMB connection using either Kerberos or NTLM authentication based on the configuration.
- 82 It attempts to connect to the SMB server specified by the `address` attribute and authenticate using the credentials provided during the object's initialization.
- 83
- 84 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.
- 85
- 86 Returns:
- 87 bool: True if the connection and authentication are successful, False otherwise.
- 88 """
- 89
- 90self.connected=False
- 91
- 92ifself.config.debug:
- 93print("[debug] [>] Connecting to remote SMB server '%s' ... "%self.address)
- 94try:
- 95self.smbClient=impacket.smbconnection.SMBConnection(
- 96remoteName=self.address,
- 97remoteHost=self.address,
- 98sess_port=int(445)
- 99)
-100exceptOSErroraserr:
-101print("[!] %s"%err)
-102self.smbClient=None
-103
-104ifself.smbClientisnotNone:
-105ifself.use_kerberos:
-106ifself.config.debug:
-107print("[debug] [>] Authenticating as '%s\\%s' with kerberos ... "%(self.domain,self.username))
-108try:
-109self.connected=self.smbClient.kerberosLogin(
-110user=self.username,
-111password=self.password,
-112domain=self.domain,
-113lmhash=self.lmhash,
-114nthash=self.nthash,
-115aesKey=self.aesKey,
-116kdcHost=self.kdcHost
-117)
-118exceptimpacket.smbconnection.SessionErroraserr:
-119ifself.config.debug:
-120traceback.print_exc()
-121print("[!] Could not login: %s"%err)
-122self.connected=False
-123
-124else:
-125ifself.config.debug:
-126print("[debug] [>] Authenticating as '%s\\%s' with NTLM ... "%(self.domain,self.username))
-127try:
-128self.connected=self.smbClient.login(
-129user=self.username,
-130password=self.password,
-131domain=self.domain,
-132lmhash=self.lmhash,
-133nthash=self.nthash
-134)
-135exceptimpacket.smbconnection.SessionErroraserr:
-136ifself.config.debug:
-137traceback.print_exc()
-138print("[!] Could not login: %s"%err)
-139self.connected=False
-140
-141ifself.connected:
-142print("[+] Successfully authenticated to '%s' as '%s\\%s'!"%(self.address,self.domain,self.username))
-143else:
-144print("[!] Failed to authenticate to '%s' as '%s\\%s'!"%(self.address,self.domain,self.username))
-145
-146returnself.connected
+
+
83defclose_smb_session(self):
+ 84"""
+ 85 Closes the current SMB session by disconnecting the SMB client.
+ 86
+ 87 This method ensures that the SMB client connection is properly closed. It checks if the client is connected
+ 88 and if so, it closes the connection and resets the connection status.
+ 89
+ 90 Raises:
+ 91 Exception: If the SMB client is not initialized or if there's an error during the disconnection process.
+ 92 """
+ 93
+ 94ifself.smbClientisnotNone:
+ 95ifself.connected:
+ 96self.smbClient.close()
+ 97self.connected=False
+ 98self.logger.debug("[+] SMB connection closed successfully.")
+ 99else:
+100self.logger.debug("[!] No active SMB connection to close.")
+101else:
+102raiseException("SMB client is not initialized.")
-
Initializes and establishes a session with the SMB server.
-
-
This method sets up the SMB connection using either Kerberos or NTLM authentication based on the configuration.
-It attempts to connect to the SMB server specified by the address attribute and authenticate using the credentials provided during the object's initialization.
+
Closes the current SMB session by disconnecting the SMB client.
-
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.
+
This method ensures that the SMB client connection is properly closed. It checks if the client is connected
+and if so, it closes the connection and resets the connection status.
-
Returns:
- bool: True if the connection and authentication are successful, False otherwise.
+
Raises:
+ Exception: If the SMB client is not initialized or if there's an error during the disconnection process.
148defclose_smb_session(self):
-149"""
-150 Closes the current SMB session by disconnecting the SMB client.
-151
-152 This method ensures that the SMB client connection is properly closed. It checks if the client is connected
-153 and if so, it closes the connection and resets the connection status.
-154
-155 Raises:
-156 Exception: If the SMB client is not initialized or if there's an error during the disconnection process.
-157 """
-158
-159ifself.smbClientisnotNone:
-160ifself.connected:
-161self.smbClient.close()
-162self.connected=False
-163ifself.config.debug:
-164print("[+] SMB connection closed successfully.")
-165else:
-166ifself.config.debug:
-167print("[!] No active SMB connection to close.")
-168else:
-169raiseException("SMB client is not initialized.")
+
+
104definit_smb_session(self):
+105"""
+106 Initializes and establishes a session with the SMB server.
+107
+108 This method sets up the SMB connection using either Kerberos or NTLM authentication based on the configuration.
+109 It attempts to connect to the SMB server specified by the `address` attribute and authenticate using the credentials provided during the object's initialization.
+110
+111 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.
+112
+113 Returns:
+114 bool: True if the connection and authentication are successful, False otherwise.
+115 """
+116
+117self.connected=False
+118
+119self.logger.debug("[>] Connecting to remote SMB server '%s' ... "%self.host)
+120
+121try:
+122ifis_port_open(self.host,self.port):
+123self.smbClient=impacket.smbconnection.SMBConnection(
+124remoteName=self.host,
+125remoteHost=self.host,
+126sess_port=int(self.port)
+127)
+128else:
+129self.connected=False
+130exceptOSErroraserr:
+131ifself.config.debug:
+132traceback.print_exc()
+133self.logger.error(err)
+134self.smbClient=None
+135
+136ifself.smbClientisnotNone:
+137ifself.credentials.use_kerberos:
+138self.logger.debug("[>] Authenticating as '%s\\%s' with kerberos ... "%(self.credentials.domain,self.credentials.username))
+139try:
+140self.connected=self.smbClient.kerberosLogin(
+141user=self.credentials.username,
+142password=self.credentials.password,
+143domain=self.credentials.domain,
+144lmhash=self.credentials.lm_hex,
+145nthash=self.credentials.nt_hex,
+146aesKey=self.credentials.aesKey,
+147kdcHost=self.credentials.kdcHost
+148)
+149exceptimpacket.smbconnection.SessionErroraserr:
+150ifself.config.debug:
+151traceback.print_exc()
+152self.logger.error("Could not login: %s"%err)
+153self.connected=False
+154
+155else:
+156self.logger.debug("[>] Authenticating as '%s\\%s' with NTLM ... "%(self.credentials.domain,self.credentials.username))
+157
+158try:
+159self.connected=self.smbClient.login(
+160user=self.credentials.username,
+161password=self.credentials.password,
+162domain=self.credentials.domain,
+163lmhash=self.credentials.lm_hex,
+164nthash=self.credentials.nt_hex
+165)
+166exceptimpacket.smbconnection.SessionErroraserr:
+167ifself.config.debug:
+168traceback.print_exc()
+169self.logger.error("Could not login: %s"%err)
+170self.connected=False
+171
+172ifself.connected:
+173self.logger.print("[+] Successfully authenticated to '%s' as '%s\\%s'!"%(self.host,self.credentials.domain,self.credentials.username))
+174else:
+175self.logger.error("Failed to authenticate to '%s' as '%s\\%s'!"%(self.host,self.credentials.domain,self.credentials.username))
+176
+177returnself.connected
-
Closes the current SMB session by disconnecting the SMB client.
+
Initializes and establishes a session with the SMB server.
-
This method ensures that the SMB client connection is properly closed. It checks if the client is connected
-and if so, it closes the connection and resets the connection status.
+
This method sets up the SMB connection using either Kerberos or NTLM authentication based on the configuration.
+It attempts to connect to the SMB server specified by the address attribute and authenticate using the credentials provided during the object's initialization.
-
Raises:
- Exception: If the SMB client is not initialized or if there's an error during the disconnection process.
+
The method will print debug information if the debug attribute is set to True. Upon successful connection and authentication, it sets the connected attribute to True.
+
+
Returns:
+ bool: True if the connection and authentication are successful, False otherwise.
173defread_file(self,path=None):
-174ifself.path_isfile(path=path):
-175tmp_file_path=self.smb_cwd+ntpath.sep+path
-176matches=self.smbClient.listPath(
-177shareName=self.smb_share,
-178path=tmp_file_path
-179)
-180
-181fh=io.BytesIO()
-182try:
-183# opening the files in streams instead of mounting shares allows
-184# for running the script from unprivileged containers
-185self.smbClient.getFile(self.smb_share,tmp_file_path,fh.write)
-186exceptimpacket.smbconnection.SessionErrorase:
-187returnNone
-188rawdata=fh.getvalue()
-189fh.close()
-190returnrawdata
-191else:
-192print("[!] Remote path '%s' is not a file."%path)
+
+
179defping_smb_session(self):
+180"""
+181 Tests the connectivity to the SMB server by sending an echo command.
+182
+183 This method attempts to send an echo command to the SMB server to check if the session is still active.
+184 It updates the `connected` attribute of the class based on the success or failure of the echo command.
+185
+186 Returns:
+187 bool: True if the echo command succeeds (indicating the session is active), False otherwise.
+188 """
+189
+190ifnotis_port_open(self.host,self.port):
+191self.connected=False
+192else:
+193try:
+194self.smbClient.getSMBServer().echo()
+195exceptExceptionase:
+196self.connected=False
+197
+198returnself.connected
-
+
Tests the connectivity to the SMB server by sending an echo command.
+
+
This method attempts to send an echo command to the SMB server to check if the session is still active.
+It updates the connected attribute of the class based on the success or failure of the echo command.
+
+
Returns:
+ bool: True if the echo command succeeds (indicating the session is active), False otherwise.
202deffind(self,paths=[],callback=None):
+203"""
+204 Finds files and directories on the SMB share based on the provided paths and executes a callback function on each entry.
+205
+206 This method traverses the specified paths on the SMB share, recursively exploring directories and invoking the callback
+207 function on each file or directory found. The callback function is called with three arguments: the entry object, the
+208 full path of the entry, and the current depth of recursion.
+209
+210 Args:
+211 paths (list, optional): A list of paths to start the search from. Defaults to an empty list.
+212 callback (function, optional): A function to be called on each entry found. The function should accept three arguments:
+213 the entry object, the full path of the entry, and the current depth of recursion. Defaults to None.
+214
+215 Note:
+216 If the callback function is None, the method will print an error message and return without performing any action.
+217 """
+218
+219defrecurse_action(paths=[],depth=0,callback=None):
+220ifcallbackisNone:
+221return[]
+222
+223next_directories_to_explore=[]
+224
+225forpathinpaths:
+226remote_smb_path=ntpath.normpath(self.smb_cwd+ntpath.sep+path)
+227entries=[]
+228
+229try:
+230entries=self.smbClient.listPath(
+231shareName=self.smb_share,
+232path=(remote_smb_path+ntpath.sep+'*')
+233)
+234exceptimpacket.smbconnection.SessionErroraserr:
+235continue
+236# Remove dot names
+237entries=[eforeinentriesife.get_longname()notin[".",".."]]
+238# Sort the entries ignoring case
+239entries=sorted(entries,key=lambdax:x.get_longname().lower())
+240
+241forentryinentries:
+242ifentry.is_directory():
+243fullpath=path+ntpath.sep+entry.get_longname()+ntpath.sep
+244next_directories_to_explore.append(fullpath)
+245else:
+246fullpath=path+ntpath.sep+entry.get_longname()
+247fullpath=re.sub(r'\\\\+',r'\\',fullpath)
+248callback(entry,fullpath,depth)
+249
+250returnnext_directories_to_explore
+251#
+252ifcallbackisnotNone:
+253depth=0
+254whilelen(paths)!=0:
+255paths=recurse_action(
+256paths=paths,
+257depth=depth,
+258callback=callback
+259)
+260depth=depth+1
+261else:
+262self.logger.error("SMBSession.find(), callback function cannot be None.")
-
+
Finds files and directories on the SMB share based on the provided paths and executes a callback function on each entry.
+
+
This method traverses the specified paths on the SMB share, recursively exploring directories and invoking the callback
+function on each file or directory found. The callback function is called with three arguments: the entry object, the
+full path of the entry, and the current depth of recursion.
+
+
Args:
+ paths (list, optional): A list of paths to start the search from. Defaults to an empty list.
+ callback (function, optional): A function to be called on each entry found. The function should accept three arguments:
+ the entry object, the full path of the entry, and the current depth of recursion. Defaults to None.
+
+
Note:
+ If the callback function is None, the method will print an error message and return without performing any action.
+
+
@@ -3142,81 +3250,80 @@
-
242defget_file(self,path=None,keepRemotePath=False):
-243"""
-244 Retrieves a file from the specified path on the SMB share.
-245
-246 This method attempts to retrieve a file from the given path within the currently connected SMB share.
-247 If the path points to a directory, it skips the retrieval. It handles file retrieval by creating a local
-248 file object and writing the contents of the remote file to it using the SMB client's getFile method.
-249
-250 Parameters:
-251 path (str): The path of the file to retrieve. If None, uses the current smb_path.
-252
-253 Returns:
-254 None
-255 """
-256
-257# Parse path
-258path=path.replace('/',ntpath.sep)
-259ifntpath.sepinpath:
-260tmp_search_path=ntpath.normpath(self.smb_cwd+ntpath.sep+ntpath.dirname(path))
-261else:
-262tmp_search_path=ntpath.normpath(self.smb_cwd+ntpath.sep)
-263# Parse filename
-264filename=ntpath.basename(path)
-265
-266# Search for the file
-267matches=self.smbClient.listPath(
-268shareName=self.smb_share,
-269path=tmp_search_path+ntpath.sep+'*'
-270)
+
264defget_file(self,path=None,keepRemotePath=False):
+265"""
+266 Retrieves a file from the specified path on the SMB share.
+267
+268 This method attempts to retrieve a file from the given path within the currently connected SMB share.
+269 If the path points to a directory, it skips the retrieval. It handles file retrieval by creating a local
+270 file object and writing the contents of the remote file to it using the SMB client's getFile method.271
-272# Filter the entries
-273matching_entries=[]
-274forentryinmatches:
-275ifentry.is_directory():
-276# Skip directories
-277continue
-278ifentry.get_longname()==filename:
-279matching_entries.append(entry)
-280elif'*'infilename:
-281regexp=filename.replace('.','\\.').replace('*','.*')
-282ifre.match(regexp,entry.get_longname()):
-283matching_entries.append(entry)
-284
-285matching_entries=sorted(list(set(matching_entries)),key=lambdax:x.get_longname())
-286
-287forentryinmatching_entries:
-288ifentry.is_directory():
-289ifself.config.debug:
-290print("[debug] [>] Skipping '%s' because it is a directory."%(tmp_search_path+ntpath.sep+entry.get_longname()))
-291else:
-292try:
-293ifntpath.sepinpath:
-294outputfile=ntpath.dirname(path)+ntpath.sep+entry.get_longname()
-295else:
-296outputfile=entry.get_longname()
-297f=LocalFileIO(
-298mode="wb",
-299path=outputfile,
-300expected_size=entry.get_filesize(),
-301debug=self.config.debug,
-302keepRemotePath=keepRemotePath
-303)
-304self.smbClient.getFile(
-305shareName=self.smb_share,
-306pathName=tmp_search_path+ntpath.sep+entry.get_longname(),
-307callback=f.write
-308)
-309f.close()
-310except(BrokenPipeError,KeyboardInterrupt)ase:
-311f.close()
-312print("\x1b[v\x1b[o\r[!] Interrupted.")
-313self.close_smb_session()
-314self.init_smb_session()
-315
-316returnNone
+272 Parameters:
+273 path (str): The path of the file to retrieve. If None, uses the current smb_path.
+274
+275 Returns:
+276 None
+277 """
+278
+279# Parse path
+280path=path.replace('/',ntpath.sep)
+281ifntpath.sepinpath:
+282tmp_search_path=ntpath.normpath(self.smb_cwd+ntpath.sep+ntpath.dirname(path))
+283else:
+284tmp_search_path=ntpath.normpath(self.smb_cwd+ntpath.sep)
+285# Parse filename
+286filename=ntpath.basename(path)
+287
+288# Search for the file
+289matches=self.smbClient.listPath(
+290shareName=self.smb_share,
+291path=tmp_search_path+ntpath.sep+'*'
+292)
+293
+294# Filter the entries
+295matching_entries=[]
+296forentryinmatches:
+297ifentry.is_directory():
+298# Skip directories
+299continue
+300ifentry.get_longname()==filename:
+301matching_entries.append(entry)
+302elif'*'infilename:
+303regexp=filename.replace('.','\\.').replace('*','.*')
+304ifre.match(regexp,entry.get_longname()):
+305matching_entries.append(entry)
+306
+307matching_entries=sorted(list(set(matching_entries)),key=lambdax:x.get_longname())
+308
+309forentryinmatching_entries:
+310ifentry.is_directory():
+311self.logger.debug("[>] Skipping '%s' because it is a directory."%(tmp_search_path+ntpath.sep+entry.get_longname()))
+312else:
+313try:
+314ifntpath.sepinpath:
+315outputfile=ntpath.dirname(path)+ntpath.sep+entry.get_longname()
+316else:
+317outputfile=entry.get_longname()
+318f=LocalFileIO(
+319mode="wb",
+320path=outputfile,
+321expected_size=entry.get_filesize(),
+322debug=self.config.debug,
+323keepRemotePath=keepRemotePath
+324)
+325self.smbClient.getFile(
+326shareName=self.smb_share,
+327pathName=tmp_search_path+ntpath.sep+entry.get_longname(),
+328callback=f.write
+329)
+330f.close()
+331except(BrokenPipeError,KeyboardInterrupt)ase:
+332f.close()
+333print("\x1b[v\x1b[o\r[!] Interrupted.")
+334self.close_smb_session()
+335self.init_smb_session()
+336
+337returnNone
@@ -3246,79 +3353,79 @@
-
318defget_file_recursively(self,path=None):
-319"""
-320 Recursively retrieves files from a specified path on the SMB share.
-321
-322 This method navigates through all directories starting from the given path,
-323 and downloads all files found. It handles directories recursively, ensuring
-324 that all nested files are retrieved. The method skips over directory entries
-325 and handles errors gracefully, attempting to continue the operation where possible.
-326
-327 Parameters:
-328 path (str): The initial directory path from which to start the recursive file retrieval.
-329 If None, it starts from the root of the configured SMB share.
-330 """
-331
-332defrecurse_action(base_dir="",path=[]):
-333iflen(base_dir)==0:
-334remote_smb_path=ntpath.sep.join(path)
-335else:
-336remote_smb_path=base_dir+ntpath.sep+ntpath.sep.join(path)
-337remote_smb_path=ntpath.normpath(remote_smb_path)
-338
-339entries=self.smbClient.listPath(
-340shareName=self.smb_share,
-341path=remote_smb_path+'\\*'
-342)
-343iflen(entries)!=0:
-344files=[entryforentryinentriesifnotentry.is_directory()]
-345directories=[entryforentryinentriesifentry.is_directory()andentry.get_longname()notin[".",".."]]
-346
-347# Files
-348iflen(files)!=0:
-349print("[>] Retrieving files of '%s'"%remote_smb_path)
-350forentry_fileinfiles:
-351ifnotentry_file.is_directory():
-352f=LocalFileIO(
-353mode="wb",
-354path=remote_smb_path+ntpath.sep+entry_file.get_longname(),
-355expected_size=entry_file.get_filesize(),
-356keepRemotePath=True,
-357debug=self.config.debug
-358)
-359try:
-360self.smbClient.getFile(
-361shareName=self.smb_share,
-362pathName=remote_smb_path+ntpath.sep+entry_file.get_longname(),
-363callback=f.write
-364)
-365f.close()
-366exceptBrokenPipeErroraserr:
-367f.set_error(message="[bold red]Failed downloading '%s': %s"%(f.path,err))
-368f.close(remove=True)
-369break
-370exceptExceptionaserr:
-371f.set_error(message="[bold red]Failed downloading '%s': %s"%(f.path,err))
-372f.close(remove=True)
-373
-374# Directories
-375forentry_directoryindirectories:
-376ifentry_directory.is_directory():
-377recurse_action(
-378base_dir=self.smb_cwd,
-379path=path+[entry_directory.get_longname()]
-380)
-381# Entrypoint
-382try:
-383recurse_action(
-384base_dir=self.smb_cwd,
-385path=[path]
-386)
-387except(BrokenPipeError,KeyboardInterrupt)ase:
-388print("\x1b[v\x1b[o\r[!] Interrupted.")
-389self.close_smb_session()
-390self.init_smb_session()
+
339defget_file_recursively(self,path=None):
+340"""
+341 Recursively retrieves files from a specified path on the SMB share.
+342
+343 This method navigates through all directories starting from the given path,
+344 and downloads all files found. It handles directories recursively, ensuring
+345 that all nested files are retrieved. The method skips over directory entries
+346 and handles errors gracefully, attempting to continue the operation where possible.
+347
+348 Parameters:
+349 path (str): The initial directory path from which to start the recursive file retrieval.
+350 If None, it starts from the root of the configured SMB share.
+351 """
+352
+353defrecurse_action(base_dir="",path=[]):
+354iflen(base_dir)==0:
+355remote_smb_path=ntpath.sep.join(path)
+356else:
+357remote_smb_path=base_dir+ntpath.sep+ntpath.sep.join(path)
+358remote_smb_path=ntpath.normpath(remote_smb_path)
+359
+360entries=self.smbClient.listPath(
+361shareName=self.smb_share,
+362path=remote_smb_path+'\\*'
+363)
+364iflen(entries)!=0:
+365files=[entryforentryinentriesifnotentry.is_directory()]
+366directories=[entryforentryinentriesifentry.is_directory()andentry.get_longname()notin[".",".."]]
+367
+368# Files
+369iflen(files)!=0:
+370self.logger.print("[>] Retrieving files of '%s'"%remote_smb_path)
+371forentry_fileinfiles:
+372ifnotentry_file.is_directory():
+373f=LocalFileIO(
+374mode="wb",
+375path=remote_smb_path+ntpath.sep+entry_file.get_longname(),
+376expected_size=entry_file.get_filesize(),
+377keepRemotePath=True,
+378debug=self.config.debug
+379)
+380try:
+381self.smbClient.getFile(
+382shareName=self.smb_share,
+383pathName=remote_smb_path+ntpath.sep+entry_file.get_longname(),
+384callback=f.write
+385)
+386f.close()
+387exceptBrokenPipeErroraserr:
+388f.set_error(message="[bold red]Failed downloading '%s': %s"%(f.path,err))
+389f.close(remove=True)
+390break
+391exceptExceptionaserr:
+392f.set_error(message="[bold red]Failed downloading '%s': %s"%(f.path,err))
+393f.close(remove=True)
+394
+395# Directories
+396forentry_directoryindirectories:
+397ifentry_directory.is_directory():
+398recurse_action(
+399base_dir=self.smb_cwd,
+400path=path+[entry_directory.get_longname()]
+401)
+402# Entrypoint
+403try:
+404recurse_action(
+405base_dir=self.smb_cwd,
+406path=[path]
+407)
+408except(BrokenPipeError,KeyboardInterrupt)ase:
+409print("\x1b[v\x1b[o\r[!] Interrupted.")
+410self.close_smb_session()
+411self.init_smb_session()
@@ -3347,29 +3454,31 @@
-
392defget_entry(self,path=None):
-393"""
-394 Retrieves information about a specific entry located at the provided path on the SMB share.
-395
-396 This method checks if the specified path exists on the SMB share. If the path exists, it retrieves the details of the entry at that path, including the directory name and file name. If the entry is found, it returns the entry object; otherwise, it returns None.
-397
-398 Args:
-399 path (str): The path of the entry to retrieve information about.
-400
-401 Returns:
-402 Entry: An object representing the entry at the specified path, or None if the entry is not found.
-403 """
-404
-405ifself.path_exists(path=path):
-406matches=self.smbClient.listPath(shareName=self.smb_share,path=path)
-407
-408iflen(matches)==1:
-409returnmatches[0]
-410else:
-411returnNone
-412
-413else:
-414returnNone
+
413defget_entry(self,path=None):
+414"""
+415 Retrieves information about a specific entry located at the provided path on the SMB share.
+416
+417 This method checks if the specified path exists on the SMB share. If the path exists, it retrieves the details of the entry at that path, including the directory name and file name. If the entry is found, it returns the entry object; otherwise, it returns None.
+418
+419 Args:
+420 path (str): The path of the entry to retrieve information about.
+421
+422 Returns:
+423 Entry: An object representing the entry at the specified path, or None if the entry is not found.
+424 """
+425
+426ifself.path_exists(path=path):
+427matches=self.smbClient.listPath(
+428shareName=self.smb_share,
+429path=path
+430)
+431
+432iflen(matches)==1:
+433returnmatches[0]
+434else:
+435returnNone
+436else:
+437returnNone
@@ -3397,80 +3506,80 @@
-
416definfo(self,share=True,server=True):
-417"""
-418 Displays information about the server and optionally the shares.
-419
-420 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.
-421
-422 Parameters:
-423 share (bool): If True, display information about the current share.
-424 server (bool): If True, display information about the server.
-425
-426 Returns:
-427 None
-428 """
-429
-430ifserver:
-431ifself.config.no_colors:
-432print("[+] Server:")
-433print(" ├─NetBIOS:")
-434print(" │ ├─ NetBIOS Hostname ──────── : %s"%(self.smbClient.getServerName()))
-435print(" │ └─ NetBIOS Domain ────────── : %s"%(self.smbClient.getServerDomain()))
-436print(" ├─DNS:")
-437print(" │ ├─ DNS Hostname ──────────── : %s"%(self.smbClient.getServerDNSHostName()))
-438print(" │ └─ DNS Domain ────────────── : %s"%(self.smbClient.getServerDNSDomainName()))
-439print(" ├─OS:")
-440print(" │ ├─ OS Name ───────────────── : %s"%(self.smbClient.getServerOS()))
-441print(" │ └─ OS Version ────────────── : %s.%s.%s"%(self.smbClient.getServerOSMajor(),self.smbClient.getServerOSMinor(),self.smbClient.getServerOSBuild()))
-442print(" ├─Server:")
-443print(" │ ├─ Signing Required ──────── : %s"%(self.smbClient.isSigningRequired()))
-444print(" │ ├─ Login Required ────────── : %s"%(self.smbClient.isLoginRequired()))
-445print(" │ ├─ Supports NTLMv2 ───────── : %s"%(self.smbClient.doesSupportNTLMv2()))
-446MaxReadSize=self.smbClient.getIOCapabilities()["MaxReadSize"]
-447print(" │ ├─ Max size of read chunk ── : %d bytes (%s)"%(MaxReadSize,b_filesize(MaxReadSize)))
-448MaxWriteSize=self.smbClient.getIOCapabilities()["MaxWriteSize"]
-449print(" │ └─ Max size of write chunk ─ : %d bytes (%s)"%(MaxWriteSize,b_filesize(MaxWriteSize)))
-450print(" └─")
-451else:
-452print("[+] Server:")
-453print(" ├─NetBIOS:")
-454print(" │ ├─ \x1b[94mNetBIOS Hostname\x1b[0m \x1b[90m────────\x1b[0m : \x1b[93m%s\x1b[0m"%(self.smbClient.getServerName()))
-455print(" │ └─ \x1b[94mNetBIOS Domain\x1b[0m \x1b[90m──────────\x1b[0m : \x1b[93m%s\x1b[0m"%(self.smbClient.getServerDomain()))
-456print(" ├─DNS:")
-457print(" │ ├─ \x1b[94mDNS Hostname\x1b[0m \x1b[90m────────────\x1b[0m : \x1b[93m%s\x1b[0m"%(self.smbClient.getServerDNSHostName()))
-458print(" │ └─ \x1b[94mDNS Domain\x1b[0m \x1b[90m──────────────\x1b[0m : \x1b[93m%s\x1b[0m"%(self.smbClient.getServerDNSDomainName()))
-459print(" ├─OS:")
-460print(" │ ├─ \x1b[94mOS Name\x1b[0m \x1b[90m─────────────────\x1b[0m : \x1b[93m%s\x1b[0m"%(self.smbClient.getServerOS()))
-461print(" │ └─ \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()))
-462print(" ├─Server:")
-463print(" │ ├─ \x1b[94mSigning Required\x1b[0m \x1b[90m────────\x1b[0m : \x1b[93m%s\x1b[0m"%(self.smbClient.isSigningRequired()))
-464print(" │ ├─ \x1b[94mLogin Required\x1b[0m \x1b[90m──────────\x1b[0m : \x1b[93m%s\x1b[0m"%(self.smbClient.isLoginRequired()))
-465print(" │ ├─ \x1b[94mSupports NTLMv2\x1b[0m \x1b[90m─────────\x1b[0m : \x1b[93m%s\x1b[0m"%(self.smbClient.doesSupportNTLMv2()))
-466MaxReadSize=self.smbClient.getIOCapabilities()["MaxReadSize"]
-467print(" │ ├─ \x1b[94mMax size of read chunk\x1b[0m \x1b[90m──\x1b[0m : \x1b[93m%d bytes (%s)\x1b[0m"%(MaxReadSize,b_filesize(MaxReadSize)))
-468MaxWriteSize=self.smbClient.getIOCapabilities()["MaxWriteSize"]
-469print(" │ └─ \x1b[94mMax size of write chunk\x1b[0m \x1b[90m─\x1b[0m : \x1b[93m%d bytes (%s)\x1b[0m"%(MaxWriteSize,b_filesize(MaxWriteSize)))
-470print(" └─")
-471
-472ifshareandself.smb_shareisnotNone:
-473share_name=self.available_shares.get(self.smb_share.lower(),"")["name"]
-474share_comment=self.available_shares.get(self.smb_share.lower(),"")["comment"]
-475share_type=self.available_shares.get(self.smb_share.lower(),"")["type"]
-476share_type=', '.join([s.replace("STYPE_","")forsinshare_type])
-477share_rawtype=self.available_shares.get(self.smb_share.lower(),"")["rawtype"]
-478ifself.config.no_colors:
-479print("\n[+] Share:")
-480print(" ├─ Name ──────────── : %s"%(share_name))
-481print(" ├─ Description ───── : %s"%(share_comment))
-482print(" ├─ Type ──────────── : %s"%(share_type))
-483print(" └─ Raw type value ── : %s"%(share_rawtype))
-484else:
-485print("\n[+] Share:")
-486print(" ├─ \x1b[94mName\x1b[0m \x1b[90m────────────\x1b[0m : \x1b[93m%s\x1b[0m"%(share_name))
-487print(" ├─ \x1b[94mDescription\x1b[0m \x1b[90m─────\x1b[0m : \x1b[93m%s\x1b[0m"%(share_comment))
-488print(" ├─ \x1b[94mType\x1b[0m \x1b[90m────────────\x1b[0m : \x1b[93m%s\x1b[0m"%(share_type))
-489print(" └─ \x1b[94mRaw type value\x1b[0m \x1b[90m──\x1b[0m : \x1b[93m%s\x1b[0m"%(share_rawtype))
+
439definfo(self,share=True,server=True):
+440"""
+441 Displays information about the server and optionally the shares.
+442
+443 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.
+444
+445 Parameters:
+446 share (bool): If True, display information about the current share.
+447 server (bool): If True, display information about the server.
+448
+449 Returns:
+450 None
+451 """
+452
+453ifserver:
+454ifself.config.no_colors:
+455self.logger.print("[+] Server:")
+456self.logger.print(" ├─NetBIOS:")
+457self.logger.print(" │ ├─ NetBIOS Hostname ──────── : %s"%(self.smbClient.getServerName()))
+458self.logger.print(" │ └─ NetBIOS Domain ────────── : %s"%(self.smbClient.getServerDomain()))
+459self.logger.print(" ├─DNS:")
+460self.logger.print(" │ ├─ DNS Hostname ──────────── : %s"%(self.smbClient.getServerDNSHostName()))
+461self.logger.print(" │ └─ DNS Domain ────────────── : %s"%(self.smbClient.getServerDNSDomainName()))
+462self.logger.print(" ├─OS:")
+463self.logger.print(" │ ├─ OS Name ───────────────── : %s"%(self.smbClient.getServerOS()))
+464self.logger.print(" │ └─ OS Version ────────────── : %s.%s.%s"%(self.smbClient.getServerOSMajor(),self.smbClient.getServerOSMinor(),self.smbClient.getServerOSBuild()))
+465self.logger.print(" ├─Server:")
+466self.logger.print(" │ ├─ Signing Required ──────── : %s"%(self.smbClient.isSigningRequired()))
+467self.logger.print(" │ ├─ Login Required ────────── : %s"%(self.smbClient.isLoginRequired()))
+468self.logger.print(" │ ├─ Supports NTLMv2 ───────── : %s"%(self.smbClient.doesSupportNTLMv2()))
+469MaxReadSize=self.smbClient.getIOCapabilities()["MaxReadSize"]
+470self.logger.print(" │ ├─ Max size of read chunk ── : %d bytes (%s)"%(MaxReadSize,b_filesize(MaxReadSize)))
+471MaxWriteSize=self.smbClient.getIOCapabilities()["MaxWriteSize"]
+472self.logger.print(" │ └─ Max size of write chunk ─ : %d bytes (%s)"%(MaxWriteSize,b_filesize(MaxWriteSize)))
+473self.logger.print(" └─")
+474else:
+475self.logger.print("[+] Server:")
+476self.logger.print(" ├─NetBIOS:")
+477self.logger.print(" │ ├─ \x1b[94mNetBIOS Hostname\x1b[0m \x1b[90m────────\x1b[0m : \x1b[93m%s\x1b[0m"%(self.smbClient.getServerName()))
+478self.logger.print(" │ └─ \x1b[94mNetBIOS Domain\x1b[0m \x1b[90m──────────\x1b[0m : \x1b[93m%s\x1b[0m"%(self.smbClient.getServerDomain()))
+479self.logger.print(" ├─DNS:")
+480self.logger.print(" │ ├─ \x1b[94mDNS Hostname\x1b[0m \x1b[90m────────────\x1b[0m : \x1b[93m%s\x1b[0m"%(self.smbClient.getServerDNSHostName()))
+481self.logger.print(" │ └─ \x1b[94mDNS Domain\x1b[0m \x1b[90m──────────────\x1b[0m : \x1b[93m%s\x1b[0m"%(self.smbClient.getServerDNSDomainName()))
+482self.logger.print(" ├─OS:")
+483self.logger.print(" │ ├─ \x1b[94mOS Name\x1b[0m \x1b[90m─────────────────\x1b[0m : \x1b[93m%s\x1b[0m"%(self.smbClient.getServerOS()))
+484self.logger.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()))
+485self.logger.print(" ├─Server:")
+486self.logger.print(" │ ├─ \x1b[94mSigning Required\x1b[0m \x1b[90m────────\x1b[0m : \x1b[93m%s\x1b[0m"%(self.smbClient.isSigningRequired()))
+487self.logger.print(" │ ├─ \x1b[94mLogin Required\x1b[0m \x1b[90m──────────\x1b[0m : \x1b[93m%s\x1b[0m"%(self.smbClient.isLoginRequired()))
+488self.logger.print(" │ ├─ \x1b[94mSupports NTLMv2\x1b[0m \x1b[90m─────────\x1b[0m : \x1b[93m%s\x1b[0m"%(self.smbClient.doesSupportNTLMv2()))
+489MaxReadSize=self.smbClient.getIOCapabilities()["MaxReadSize"]
+490self.logger.print(" │ ├─ \x1b[94mMax size of read chunk\x1b[0m \x1b[90m──\x1b[0m : \x1b[93m%d bytes (%s)\x1b[0m"%(MaxReadSize,b_filesize(MaxReadSize)))
+491MaxWriteSize=self.smbClient.getIOCapabilities()["MaxWriteSize"]
+492self.logger.print(" │ └─ \x1b[94mMax size of write chunk\x1b[0m \x1b[90m─\x1b[0m : \x1b[93m%d bytes (%s)\x1b[0m"%(MaxWriteSize,b_filesize(MaxWriteSize)))
+493self.logger.print(" └─")
+494
+495ifshareandself.smb_shareisnotNone:
+496share_name=self.available_shares.get(self.smb_share.lower(),"")["name"]
+497share_comment=self.available_shares.get(self.smb_share.lower(),"")["comment"]
+498share_type=self.available_shares.get(self.smb_share.lower(),"")["type"]
+499share_type=', '.join([s.replace("STYPE_","")forsinshare_type])
+500share_rawtype=self.available_shares.get(self.smb_share.lower(),"")["rawtype"]
+501ifself.config.no_colors:
+502self.logger.print("\n[+] Share:")
+503self.logger.print(" ├─ Name ──────────── : %s"%(share_name))
+504self.logger.print(" ├─ Description ───── : %s"%(share_comment))
+505self.logger.print(" ├─ Type ──────────── : %s"%(share_type))
+506self.logger.print(" └─ Raw type value ── : %s"%(share_rawtype))
+507else:
+508self.logger.print("\n[+] Share:")
+509self.logger.print(" ├─ \x1b[94mName\x1b[0m \x1b[90m────────────\x1b[0m : \x1b[93m%s\x1b[0m"%(share_name))
+510self.logger.print(" ├─ \x1b[94mDescription\x1b[0m \x1b[90m─────\x1b[0m : \x1b[93m%s\x1b[0m"%(share_comment))
+511self.logger.print(" ├─ \x1b[94mType\x1b[0m \x1b[90m────────────\x1b[0m : \x1b[93m%s\x1b[0m"%(share_type))
+512self.logger.print(" └─ \x1b[94mRaw type value\x1b[0m \x1b[90m──\x1b[0m : \x1b[93m%s\x1b[0m"%(share_rawtype))
@@ -3499,37 +3608,37 @@
-
491deflist_contents(self,path=None):
-492"""
-493 Lists the contents of a specified directory on the SMB share.
-494
-495 This method retrieves the contents of a directory specified by `shareName` and `path`. If `shareName` or `path`
-496 is not provided, it defaults to the instance's current SMB share or path. The method returns a dictionary with
-497 the long names of the files and directories as keys and their respective SMB entry objects as values.
-498
-499 Args:
-500 shareName (str, optional): The name of the SMB share. Defaults to the current SMB share if None.
-501 path (str, optional): The directory path to list contents from. Defaults to the current path if None.
-502
-503 Returns:
-504 dict: A dictionary with file and directory names as keys and their SMB entry objects as values.
-505 """
-506
-507dest_path=[self.smb_cwd.rstrip(ntpath.sep),]
-508ifpathisnotNoneandlen(path)>0:
-509dest_path.append(path.rstrip(ntpath.sep))
-510dest_path.append('*')
-511path=ntpath.sep.join(dest_path)
-512
-513contents={}
-514entries=self.smbClient.listPath(
-515shareName=self.smb_share,
-516path=path
-517)
-518forentryinentries:
-519contents[entry.get_longname()]=entry
-520
-521returncontents
+
514deflist_contents(self,path=None):
+515"""
+516 Lists the contents of a specified directory on the SMB share.
+517
+518 This method retrieves the contents of a directory specified by `shareName` and `path`. If `shareName` or `path`
+519 is not provided, it defaults to the instance's current SMB share or path. The method returns a dictionary with
+520 the long names of the files and directories as keys and their respective SMB entry objects as values.
+521
+522 Args:
+523 shareName (str, optional): The name of the SMB share. Defaults to the current SMB share if None.
+524 path (str, optional): The directory path to list contents from. Defaults to the current path if None.
+525
+526 Returns:
+527 dict: A dictionary with file and directory names as keys and their SMB entry objects as values.
+528 """
+529
+530dest_path=[self.smb_cwd.rstrip(ntpath.sep),]
+531ifpathisnotNoneandlen(path)>0:
+532dest_path.append(path.rstrip(ntpath.sep))
+533dest_path.append('*')
+534path=ntpath.sep.join(dest_path)
+535
+536contents={}
+537entries=self.smbClient.listPath(
+538shareName=self.smb_share,
+539path=path
+540)
+541forentryinentries:
+542contents[entry.get_longname()]=entry
+543
+544returncontents
@@ -3560,41 +3669,41 @@
-
523deflist_shares(self):
-524"""
-525 Lists all the shares available on the connected SMB server.
-526
-527 This method queries the SMB server to retrieve a list of all available shares. It populates the `shares` dictionary
-528 with key-value pairs where the key is the share name and the value is a dictionary containing details about the share
-529 such as its name, type, raw type, and any comments associated with the share.
-530
-531 Returns:
-532 dict: A dictionary containing information about each share available on the server.
-533 """
-534
-535self.available_shares={}
-536
-537ifself.connected:
-538ifself.smbClientisnotNone:
-539resp=self.smbClient.listShares()
-540
-541forshareinresp:
-542# SHARE_INFO_1 structure (lmshare.h)
-543# https://learn.microsoft.com/en-us/windows/win32/api/lmshare/ns-lmshare-share_info_1
-544sharename=share["shi1_netname"][:-1]
-545sharecomment=share["shi1_remark"][:-1]
-546sharetype=share["shi1_type"]
-547
-548self.available_shares[sharename.lower()]={
-549"name":sharename,
-550"type":STYPE_MASK(sharetype),
-551"rawtype":sharetype,
-552"comment":sharecomment
-553}
-554else:
-555print("[!] Error: SMBSession.smbClient is None.")
-556
-557returnself.available_shares
+
546deflist_shares(self):
+547"""
+548 Lists all the shares available on the connected SMB server.
+549
+550 This method queries the SMB server to retrieve a list of all available shares. It populates the `shares` dictionary
+551 with key-value pairs where the key is the share name and the value is a dictionary containing details about the share
+552 such as its name, type, raw type, and any comments associated with the share.
+553
+554 Returns:
+555 dict: A dictionary containing information about each share available on the server.
+556 """
+557
+558self.available_shares={}
+559
+560ifself.connected:
+561ifself.smbClientisnotNone:
+562resp=self.smbClient.listShares()
+563
+564forshareinresp:
+565# SHARE_INFO_1 structure (lmshare.h)
+566# https://learn.microsoft.com/en-us/windows/win32/api/lmshare/ns-lmshare-share_info_1
+567sharename=share["shi1_netname"][:-1]
+568sharecomment=share["shi1_remark"][:-1]
+569sharetype=share["shi1_type"]
+570
+571self.available_shares[sharename.lower()]={
+572"name":sharename,
+573"type":STYPE_MASK(sharetype),
+574"rawtype":sharetype,
+575"comment":sharecomment
+576}
+577else:
+578self.logger.error("Error: SMBSession.smbClient is None.")
+579
+580returnself.available_shares
@@ -3621,49 +3730,49 @@
-
559defmkdir(self,path=None):
-560"""
-561 Creates a directory at the specified path on the SMB share.
-562
-563 This method takes a path and attempts to create the directory structure on the SMB share. If the path includes
-564 nested directories, it will create each directory in the sequence. If a directory already exists, it will skip
-565 the creation for that directory without raising an error.
-566
-567 Args:
-568 path (str, optional): The full path of the directory to create on the SMB share. Defaults to None.
-569
-570 Note:
-571 The path should use forward slashes ('/') which will be converted to backslashes (ntpath.sep) for SMB compatibility.
-572 """
-573
-574ifpathisnotNone:
-575# Prepare path
-576path=path.replace('/',ntpath.sep)
-577ifntpath.sepinpath:
-578path=path.strip(ntpath.sep).split(ntpath.sep)
-579else:
-580path=[path]
-581
-582# Create each dir in the path
-583fordepthinrange(1,len(path)+1):
-584tmp_path=ntpath.sep.join(path[:depth])
-585try:
-586self.smbClient.createDirectory(
-587shareName=self.smb_share,
-588pathName=ntpath.normpath(self.smb_cwd+ntpath.sep+tmp_path+ntpath.sep)
-589)
-590exceptimpacket.smbconnection.SessionErroraserr:
-591iferr.getErrorCode()==0xc0000035:
-592# STATUS_OBJECT_NAME_COLLISION
-593# Remote directory already created, this is normal
-594# Src: https://github.com/fortra/impacket/blob/269ce69872f0e8f2188a80addb0c39fedfa6dcb8/impacket/nt_errors.py#L268C9-L268C19
-595pass
-596else:
-597print("[!] Failed to create directory '%s': %s"%(tmp_path,err))
-598ifself.config.debug:
-599traceback.print_exc()
-600else:
-601pass
+
582defmkdir(self,path=None):
+583"""
+584 Creates a directory at the specified path on the SMB share.
+585
+586 This method takes a path and attempts to create the directory structure on the SMB share. If the path includes
+587 nested directories, it will create each directory in the sequence. If a directory already exists, it will skip
+588 the creation for that directory without raising an error.
+589
+590 Args:
+591 path (str, optional): The full path of the directory to create on the SMB share. Defaults to None.
+592
+593 Note:
+594 The path should use forward slashes ('/') which will be converted to backslashes (ntpath.sep) for SMB compatibility.
+595 """
+596
+597ifpathisnotNone:
+598# Prepare path
+599path=path.replace('/',ntpath.sep)
+600ifntpath.sepinpath:
+601path=path.strip(ntpath.sep).split(ntpath.sep)
+602else:
+603path=[path]
+604
+605# Create each dir in the path
+606fordepthinrange(1,len(path)+1):
+607tmp_path=ntpath.sep.join(path[:depth])
+608try:
+609self.smbClient.createDirectory(
+610shareName=self.smb_share,
+611pathName=ntpath.normpath(self.smb_cwd+ntpath.sep+tmp_path+ntpath.sep)
+612)
+613exceptimpacket.smbconnection.SessionErroraserr:
+614iferr.getErrorCode()==0xc0000035:
+615# STATUS_OBJECT_NAME_COLLISION
+616# Remote directory already created, this is normal
+617# Src: https://github.com/fortra/impacket/blob/269ce69872f0e8f2188a80addb0c39fedfa6dcb8/impacket/nt_errors.py#L268C9-L268C19
+618pass
+619else:
+620self.logger.error("Failed to create directory '%s': %s"%(tmp_path,err))
+621ifself.config.debug:
+622traceback.print_exc()
+623else:
+624pass
@@ -3693,50 +3802,50 @@
-
603defmount(self,local_mount_point,remote_path):
-604"""
-605 Generates the command to mount an SMB share on different platforms.
-606
-607 This method takes the local mount point and the remote path of the SMB share and generates the appropriate mount command based on the platform.
-608 It constructs the mount command using the provided parameters and executes it using the os.system() function.
-609
-610 Args:
-611 local_mount_point (str): The local directory where the SMB share will be mounted.
-612 remote_path (str): The remote path on the SMB share to be mounted.
-613
-614 Note:
-615 - For Windows platform, the command uses 'net use' to mount the share.
-616 - For Linux platform, the command uses 'mount' to mount the share.
-617 - For macOS platform, the command uses 'mount_smbfs' to mount the share.
-618 - If the platform is not supported, an error message is displayed.
-619
-620 Returns:
-621 None
-622 """
-623
-624ifnotos.path.exists(local_mount_point):
-625pass
-626
-627ifsys.platform.startswith('win'):
-628remote_path=remote_path.replace('/',ntpath.sep)
-629command=f"net use {local_mount_point}\\\\{self.address}\\{self.smb_share}\\{remote_path}"
-630
-631elifsys.platform.startswith('linux'):
-632remote_path=remote_path.replace(ntpath.sep,'/')
-633command=f"mount -t cifs //{self.address}/{self.smb_share}/{remote_path}{local_mount_point} -o username={self.username},password={self.password}"
-634
-635elifsys.platform.startswith('darwin'):
-636remote_path=remote_path.replace(ntpath.sep,'/')
-637command=f"mount_smbfs //{self.username}:{self.password}@{self.address}/{self.smb_share}/{remote_path}{local_mount_point}"
-638
-639else:
-640command=None
-641print("[!] Unsupported platform for mounting SMB share.")
-642
-643ifcommandisnotNone:
-644ifself.config.debug:
-645print("[debug] Executing: %s"%command)
-646os.system(command)
+
626defmount(self,local_mount_point,remote_path):
+627"""
+628 Generates the command to mount an SMB share on different platforms.
+629
+630 This method takes the local mount point and the remote path of the SMB share and generates the appropriate mount command based on the platform.
+631 It constructs the mount command using the provided parameters and executes it using the os.system() function.
+632
+633 Args:
+634 local_mount_point (str): The local directory where the SMB share will be mounted.
+635 remote_path (str): The remote path on the SMB share to be mounted.
+636
+637 Note:
+638 - For Windows platform, the command uses 'net use' to mount the share.
+639 - For Linux platform, the command uses 'mount' to mount the share.
+640 - For macOS platform, the command uses 'mount_smbfs' to mount the share.
+641 - If the platform is not supported, an error message is displayed.
+642
+643 Returns:
+644 None
+645 """
+646
+647ifnotos.path.exists(local_mount_point):
+648pass
+649
+650ifsys.platform.startswith('win'):
+651remote_path=remote_path.replace('/',ntpath.sep)
+652command=f"net use {local_mount_point}\\\\{self.host}\\{self.smb_share}\\{remote_path}"
+653
+654elifsys.platform.startswith('linux'):
+655remote_path=remote_path.replace(ntpath.sep,'/')
+656command=f"mount -t cifs //{self.host}/{self.smb_share}/{remote_path}{local_mount_point} -o username={self.credentials.username},password={self.credentials.password}"
+657
+658elifsys.platform.startswith('darwin'):
+659remote_path=remote_path.replace(ntpath.sep,'/')
+660command=f"mount_smbfs //{self.credentials.username}:{self.credentials.password}@{self.host}/{self.smb_share}/{remote_path}{local_mount_point}"
+661
+662else:
+663command=None
+664self.logger.error("Unsupported platform for mounting SMB share.")
+665
+666ifcommandisnotNone:
+667ifself.config.debug:
+668self.logger.debug("Executing: %s"%command)
+669os.system(command)
@@ -3772,32 +3881,33 @@
-
648defpath_exists(self,path=None):
-649"""
-650 Checks if the specified path exists on the SMB share.
-651
-652 This method determines if a given path exists on the SMB share by attempting to list the contents of the path.
-653 If the path listing is successful and returns one or more entries, the path is considered to exist.
-654
-655 Args:
-656 path (str, optional): The path to check on the SMB share. Defaults to None.
-657
-658 Returns:
-659 bool: True if the path exists, False otherwise or if an error occurs.
-660 """
-661
-662ifpathisnotNone:
-663path=path.replace('*','')
-664try:
-665contents=self.smbClient.listPath(
-666shareName=self.smb_share,
-667path=ntpath.normpath(self.smb_cwd+ntpath.sep+path+ntpath.sep)
-668)
-669return(len(contents)!=0)
-670exceptExceptionase:
-671returnFalse
-672else:
-673returnFalse
+
671defpath_exists(self,path=None):
+672"""
+673 Checks if the specified path exists on the SMB share.
+674
+675 This method determines if a given path exists on the SMB share by attempting to list the contents of the path.
+676 If the path listing is successful and returns one or more entries, the path is considered to exist.
+677
+678 Args:
+679 path (str, optional): The path to check on the SMB share. Defaults to None.
+680
+681 Returns:
+682 bool: True if the path exists, False otherwise or if an error occurs.
+683 """
+684
+685ifpathisnotNone:
+686path=path.replace('*','')
+687path=path.replace('/',ntpath.sep)
+688try:
+689contents=self.smbClient.listPath(
+690shareName=self.smb_share,
+691path=ntpath.normpath(self.smb_cwd+ntpath.sep+path+ntpath.sep)
+692)
+693return(len(contents)!=0)
+694exceptExceptionase:
+695returnFalse
+696else:
+697returnFalse
@@ -3826,49 +3936,48 @@
-
675defpath_isdir(self,pathFromRoot=None):
-676"""
-677 Checks if the specified path is a directory on the SMB share.
-678
-679 This method determines if a given path corresponds to a directory on the SMB share. It does this by listing the
-680 contents of the path and filtering for entries that match the basename of the path and are marked as directories.
-681
-682 Args:
-683 path (str, optional): The path to check on the SMB share. Defaults to None.
-684
-685 Returns:
-686 bool: True if the path is a directory, False otherwise or if an error occurs.
-687 """
-688
-689ifpathFromRootisnotNone:
-690# Replace slashes if any
-691path=pathFromRoot.replace('/',ntpath.sep)
-692
-693# Strip wildcards to avoid injections
-694path=path.replace('*','')
-695
-696# Normalize path and strip leading backslash
-697path=ntpath.normpath(path+ntpath.sep).lstrip(ntpath.sep)
-698
-699ifpath.strip()in['','.','..']:
-700# By defininition they exist on the filesystem
-701returnTrue
-702else:
-703try:
-704contents=self.smbClient.listPath(
-705shareName=self.smb_share,
-706path=path+'*'
-707)
-708# Filter on directories
-709contents=[
-710cforcincontents
-711ifc.get_longname()==ntpath.basename(path)andc.is_directory()
-712]
-713return(len(contents)!=0)
-714exceptExceptionase:
-715returnFalse
-716else:
-717returnFalse
+
699defpath_isdir(self,pathFromRoot=None):
+700"""
+701 Checks if the specified path is a directory on the SMB share.
+702
+703 This method determines if a given path corresponds to a directory on the SMB share. It does this by listing the
+704 contents of the path and filtering for entries that match the basename of the path and are marked as directories.
+705
+706 Args:
+707 path (str, optional): The path to check on the SMB share. Defaults to None.
+708
+709 Returns:
+710 bool: True if the path is a directory, False otherwise or if an error occurs.
+711 """
+712
+713ifpathFromRootisnotNone:
+714# Strip wildcards to avoid injections
+715path=pathFromRoot.replace('*','')
+716# Replace slashes if any
+717path=path.replace('/',ntpath.sep)
+718
+719# Normalize path and strip leading backslash
+720path=ntpath.normpath(path+ntpath.sep).lstrip(ntpath.sep)
+721
+722ifpath.strip()in['','.','..']:
+723# By defininition they exist on the filesystem
+724returnTrue
+725else:
+726try:
+727contents=self.smbClient.listPath(
+728shareName=self.smb_share,
+729path=path+'*'
+730)
+731# Filter on directories
+732contents=[
+733cforcincontents
+734ifc.get_longname()==ntpath.basename(path)andc.is_directory()
+735]
+736return(len(contents)!=0)
+737exceptExceptionase:
+738returnFalse
+739else:
+740returnFalse
719defpath_isfile(self,path=None):
-720"""
-721 Checks if the specified path is a file on the SMB share.
-722
-723 This method determines if a given path corresponds to a file on the SMB share. It does this by listing the
-724 contents of the path and filtering for entries that match the basename of the path and are not marked as directories.
-725
-726 Args:
-727 path (str, optional): The path to check on the SMB share. Defaults to None.
-728
-729 Returns:
-730 bool: True if the path is a file, False otherwise or if an error occurs.
-731 """
-732
-733ifpathisnotNone:
-734path=path.replace('*','')
-735search_dir=ntpath.normpath(self.smb_cwd+ntpath.sep+path)
-736search_dir=ntpath.dirname(search_dir)+ntpath.sep+'*'
-737try:
-738contents=self.smbClient.listPath(
-739shareName=self.smb_share,
-740path=search_dir
-741)
-742# Filter on files
-743contents=[
-744cforcincontents
-745ifc.get_longname()==ntpath.basename(path)andnotc.is_directory()
-746]
-747return(len(contents)!=0)
-748exceptExceptionase:
-749returnFalse
-750else:
-751returnFalse
+
742defpath_isfile(self,pathFromRoot=None):
+743"""
+744 Checks if the specified path is a file on the SMB share.
+745
+746 This method determines if a given path corresponds to a file on the SMB share. It does this by listing the
+747 contents of the path and filtering for entries that match the basename of the path and are not marked as directories.
+748
+749 Args:
+750 path (str, optional): The path to check on the SMB share. Defaults to None.
+751
+752 Returns:
+753 bool: True if the path is a file, False otherwise or if an error occurs.
+754 """
+755
+756ifpathFromRootisnotNone:
+757# Strip wildcards to avoid injections
+758path=pathFromRoot.replace('*','')
+759# Replace slashes if any
+760path=path.replace('/',ntpath.sep)
+761
+762# Normalize path and strip leading backslash
+763path=ntpath.normpath(path+ntpath.sep).lstrip(ntpath.sep)
+764
+765try:
+766contents=self.smbClient.listPath(
+767shareName=self.smb_share,
+768path=ntpath.dirname(path)+ntpath.sep+'*'
+769)
+770# Filter on files
+771contents=[
+772cforcincontents
+773ifc.get_longname()==ntpath.basename(path)andnotc.is_directory()
+774]
+775return(len(contents)!=0)
+776exceptExceptionase:
+777returnFalse
+778else:
+779returnFalse
@@ -3946,47 +4060,6 @@
-
-
-
-
-
- def
- ping_smb_session(self):
-
-
-
-
-
-
753defping_smb_session(self):
-754"""
-755 Tests the connectivity to the SMB server by sending an echo command.
-756
-757 This method attempts to send an echo command to the SMB server to check if the session is still active.
-758 It updates the `connected` attribute of the class based on the success or failure of the echo command.
-759
-760 Returns:
-761 bool: True if the echo command succeeds (indicating the session is active), False otherwise.
-762 """
-763
-764try:
-765self.smbClient.getSMBServer().echo()
-766exceptExceptionase:
-767self.connected=False
-768returnself.connected
-
-
-
-
Tests the connectivity to the SMB server by sending an echo command.
-
-
This method attempts to send an echo command to the SMB server to check if the session is still active.
-It updates the connected attribute of the class based on the success or failure of the echo command.
-
-
Returns:
- bool: True if the echo command succeeds (indicating the session is active), False otherwise.
-
-
-
@@ -3999,72 +4072,81 @@
-
770defput_file(self,localpath=None):
-771"""
-772 Uploads a single file to the SMB share.
-773
-774 This method takes a local file path, opens the file, and uploads it to the SMB share at the specified path.
-775 It handles exceptions such as broken pipe errors or keyboard interrupts by closing and reinitializing the SMB session.
-776 General exceptions are caught and logged, with a traceback provided if debugging is enabled.
-777
-778 Args:
-779 localpath (str, optional): The local file path of the file to be uploaded. Defaults to None.
-780 """
-781
-782# Parse path
-783localpath=localpath.replace('/',os.path.sep)
-784ifos.path.sepinlocalpath:
-785tmp_search_path=os.path.normpath(os.getcwd()+os.path.sep+os.path.dirname(localpath))
-786else:
-787tmp_search_path=os.path.normpath(os.getcwd()+os.path.sep)
-788# Parse filename
-789filename=os.path.basename(localpath)
-790
-791# Search for the file
-792matches=os.listdir(tmp_search_path)
-793# Filter the entries
-794matching_entries=[]
-795forentryinmatches:
-796ifentry==filename:
-797matching_entries.append(entry)
-798elif'*'infilename:
-799regexp=filename.replace('.','\\.').replace('*','.*')
-800ifre.match(regexp,entry):
-801matching_entries.append(entry)
-802
-803matching_entries=sorted(list(set(matching_entries)))
+
781defput_file(self,localpath=None):
+782"""
+783 Uploads a single file to the SMB share.
+784
+785 This method takes a local file path, opens the file, and uploads it to the SMB share at the specified path.
+786 It handles exceptions such as broken pipe errors or keyboard interrupts by closing and reinitializing the SMB session.
+787 General exceptions are caught and logged, with a traceback provided if debugging is enabled.
+788
+789 Args:
+790 localpath (str, optional): The local file path of the file to be uploaded. Defaults to None.
+791 """
+792
+793# Parse path
+794localpath=localpath.replace('/',os.path.sep)
+795ifos.path.sepinlocalpath:
+796iflocalpath.startswith(os.path.sep):
+797# Absolute path
+798tmp_search_path=os.path.normpath(localpath)
+799else:
+800# Relative path
+801tmp_search_path=os.path.normpath(os.getcwd()+os.path.sep+os.path.dirname(localpath))
+802else:
+803tmp_search_path=os.path.normpath(os.getcwd()+os.path.sep)804
-805# Loop and upload
-806forlocalpathinmatching_entries:
-807ifos.path.exists(localpath):
-808ifos.path.isfile(localpath):
-809try:
-810localfile=os.path.basename(localpath)
-811f=LocalFileIO(
-812mode="rb",
-813path=localpath,
-814debug=self.config.debug
-815)
-816self.smbClient.putFile(
-817shareName=self.smb_share,
-818pathName=ntpath.normpath(self.smb_cwd+ntpath.sep+localfile+ntpath.sep),
-819callback=f.read
-820)
-821f.close()
-822except(BrokenPipeError,KeyboardInterrupt)aserr:
-823print("[!] Interrupted.")
-824self.close_smb_session()
-825self.init_smb_session()
-826exceptExceptionaserr:
-827print("[!] Failed to upload '%s': %s"%(localfile,err))
-828ifself.config.debug:
-829traceback.print_exc()
-830else:
-831# [!] The specified localpath is a directory. Use 'put -r <directory>' instead.
-832pass
-833else:
-834# [!] The specified localpath does not exist.
-835pass
+805# Parse filename
+806filename=os.path.basename(localpath)
+807
+808# Search for the file
+809matches=os.listdir(tmp_search_path)
+810# Filter the entries
+811matching_entries=[]
+812forentryinmatches:
+813ifentry==filename:
+814matching_entries.append(entry)
+815elif'*'infilename:
+816regexp=filename.replace('.','\\.').replace('*','.*')
+817ifre.match(regexp,entry):
+818matching_entries.append(entry)
+819
+820matching_entries=sorted(list(set(matching_entries)))
+821
+822# Loop and upload
+823forlocalpathinmatching_entries:
+824ifos.path.exists(localpath):
+825ifos.path.isfile(localpath):
+826try:
+827localfile=os.path.basename(localpath)
+828f=LocalFileIO(
+829mode="rb",
+830path=localpath,
+831debug=self.config.debug
+832)
+833self.smbClient.putFile(
+834shareName=self.smb_share,
+835pathName=ntpath.normpath(self.smb_cwd+ntpath.sep+localfile+ntpath.sep),
+836callback=f.read
+837)
+838f.close()
+839
+840except(BrokenPipeError,KeyboardInterrupt)aserr:
+841self.logger.error("Interrupted.")
+842self.close_smb_session()
+843self.init_smb_session()
+844
+845except(Exception,PermissionError)aserr:
+846f.set_error(message="[bold red]Failed uploading '%s': %s"%(f.path,err))
+847f.close(remove=False)
+848ifself.config.debug:
+849traceback.print_exc()
+850else:
+851# [!] The specified localpath is a directory. Use 'put -r <directory>' instead.
+852pass
+853else:
+854# [!] The specified localpath does not exist.
+855pass
@@ -4091,62 +4173,65 @@
-
837defput_file_recursively(self,localpath=None):
-838"""
-839 Recursively uploads files from a specified local directory to the SMB share.
-840
-841 This method walks through the given local directory and all its subdirectories, uploading each file to the
-842 corresponding directory structure on the SMB share. It first checks if the local path is a directory. If it is,
-843 it iterates over all files and directories within the local path, creating necessary directories on the SMB share
-844 and uploading files. If the local path is not a directory, it prints an error message.
-845
-846 Args:
-847 localpath (str, optional): The local directory path from which files will be uploaded. Defaults to None.
-848 """
-849
-850ifos.path.exists(localpath):
-851ifos.path.isfile(localpath):
-852# Iterate over all files and directories within the local path
-853local_files={}
-854forroot,dirs,filesinos.walk(localpath):
-855iflen(files)!=0:
-856local_files[root]=files
-857
-858# Iterate over the found files
-859forlocal_dir_pathinsorted(local_files.keys()):
-860print("[>] Putting files of '%s'"%local_dir_path)
-861
-862# Create remote directory
-863remote_dir_path=local_dir_path.replace(os.path.sep,ntpath.sep)
-864self.mkdir(
-865path=ntpath.normpath(self.smb_cwd+ntpath.sep+remote_dir_path+ntpath.sep)
-866)
-867
-868forlocal_file_pathinlocal_files[local_dir_path]:
-869try:
-870f=LocalFileIO(
-871mode="rb",
-872path=local_dir_path+os.path.sep+local_file_path,
-873debug=self.config.debug
-874)
-875self.smbClient.putFile(
-876shareName=self.smb_share,
-877pathName=ntpath.normpath(self.smb_cwd+ntpath.sep+remote_dir_path+ntpath.sep+local_file_path),
-878callback=f.read
-879)
-880f.close()
+
857defput_file_recursively(self,localpath=None):
+858"""
+859 Recursively uploads files from a specified local directory to the SMB share.
+860
+861 This method walks through the given local directory and all its subdirectories, uploading each file to the
+862 corresponding directory structure on the SMB share. It first checks if the local path is a directory. If it is,
+863 it iterates over all files and directories within the local path, creating necessary directories on the SMB share
+864 and uploading files. If the local path is not a directory, it prints an error message.
+865
+866 Args:
+867 localpath (str, optional): The local directory path from which files will be uploaded. Defaults to None.
+868 """
+869
+870ifos.path.exists(localpath):
+871ifos.path.isdir(localpath):
+872# Iterate over all files and directories within the local path
+873local_files={}
+874forroot,dirs,filesinos.walk(localpath):
+875iflen(files)!=0:
+876local_files[root]=files
+877
+878# Iterate over the found files
+879forlocal_dir_pathinsorted(local_files.keys()):
+880self.logger.print("[>] Putting files of '%s'"%local_dir_path)881
-882exceptBrokenPipeErroraserr:
-883f.set_error(message="[bold red]Failed uploading '%s': %s"%(f.path,err))
-884f.close(remove=True)
-885break
-886exceptExceptionaserr:
-887f.set_error(message="[bold red]Failed uploading '%s': %s"%(f.path,err))
-888f.close(remove=True)
-889else:
-890print("[!] The specified localpath is a file. Use 'put <file>' instead.")
-891else:
-892print("[!] The specified localpath does not exist.")
+882# Create remote directory
+883remote_dir_path=local_dir_path.replace(os.path.sep,ntpath.sep)
+884self.mkdir(
+885path=ntpath.normpath(remote_dir_path+ntpath.sep)
+886)
+887
+888forlocal_file_pathinlocal_files[local_dir_path]:
+889try:
+890f=LocalFileIO(
+891mode="rb",
+892path=local_dir_path+os.path.sep+local_file_path,
+893debug=self.config.debug
+894)
+895self.smbClient.putFile(
+896shareName=self.smb_share,
+897pathName=ntpath.normpath(self.smb_cwd+ntpath.sep+remote_dir_path+ntpath.sep+local_file_path),
+898callback=f.read
+899)
+900f.close()
+901
+902except(BrokenPipeError,KeyboardInterrupt)aserr:
+903self.logger.error("Interrupted.")
+904self.close_smb_session()
+905self.init_smb_session()
+906
+907except(Exception,PermissionError)aserr:
+908f.set_error(message="[bold red]Failed uploading '%s': %s"%(f.path,err))
+909f.close(remove=False)
+910ifself.config.debug:
+911traceback.print_exc()
+912else:
+913self.logger.error("The specified localpath is a file. Use 'put <file>' instead.")
+914else:
+915self.logger.error("The specified localpath does not exist.")
@@ -4162,6 +4247,72 @@
+
+
+
+
+
+ def
+ read_file(self, path=None):
+
+
+
+
+
+
917defread_file(self,path=None):
+918"""
+919 Reads a file from the SMB share.
+920
+921 This method attempts to read the contents of a file specified by the `path` parameter from the SMB share.
+922 It constructs the full path to the file, checks if the path is a valid file, and then reads the file content
+923 into a byte stream which is returned to the caller.
+924
+925 Args:
+926 path (str, optional): The path of the file to be read from the SMB share. Defaults to None.
+927
+928 Returns:
+929 bytes: The content of the file as a byte stream, or None if the file does not exist or an error occurs.
+930 """
+931
+932ifself.path_isfile(pathFromRoot=path):
+933path=path.replace('/',ntpath.sep)
+934ifpath.startswith(ntpath.sep):
+935# Absolute path
+936tmp_file_path=ntpath.normpath(path)
+937else:
+938# Relative path
+939tmp_file_path=ntpath.normpath(self.smb_cwd+ntpath.sep+path)
+940tmp_file_path=tmp_file_path.lstrip(ntpath.sep)
+941
+942fh=io.BytesIO()
+943try:
+944# opening the files in streams instead of mounting shares allows
+945# for running the script from unprivileged containers
+946self.smbClient.getFile(self.smb_share,tmp_file_path,fh.write)
+947exceptimpacket.smbconnection.SessionErrorase:
+948returnNone
+949rawdata=fh.getvalue()
+950fh.close()
+951returnrawdata
+952else:
+953returnNone
+
+
+
+
Reads a file from the SMB share.
+
+
This method attempts to read the contents of a file specified by the path parameter from the SMB share.
+It constructs the full path to the file, checks if the path is a valid file, and then reads the file content
+into a byte stream which is returned to the caller.
+
+
Args:
+ path (str, optional): The path of the file to be read from the SMB share. Defaults to None.
+
+
Returns:
+ bytes: The content of the file as a byte stream, or None if the file does not exist or an error occurs.
+
+
+
@@ -4174,26 +4325,26 @@
-
894defrmdir(self,path=None):
-895"""
-896 Removes a directory from the SMB share at the specified path.
-897
-898 This method attempts to delete a directory located at the given path on the SMB share. If the operation fails,
-899 it prints an error message indicating the failure and the reason. If debugging is enabled, it also prints
-900 the stack trace of the exception.
-901
-902 Args:
-903 path (str, optional): The path of the directory to be removed on the SMB share. Defaults to None.
-904 """
-905try:
-906self.smbClient.deleteDirectory(
-907shareName=self.smb_share,
-908pathName=ntpath.normpath(self.smb_cwd+ntpath.sep+path),
-909)
-910exceptExceptionaserr:
-911print("[!] Failed to remove directory '%s': %s"%(path,err))
-912ifself.config.debug:
-913traceback.print_exc()
+
955defrmdir(self,path=None):
+956"""
+957 Removes a directory from the SMB share at the specified path.
+958
+959 This method attempts to delete a directory located at the given path on the SMB share. If the operation fails,
+960 it prints an error message indicating the failure and the reason. If debugging is enabled, it also prints
+961 the stack trace of the exception.
+962
+963 Args:
+964 path (str, optional): The path of the directory to be removed on the SMB share. Defaults to None.
+965 """
+966try:
+967self.smbClient.deleteDirectory(
+968shareName=self.smb_share,
+969pathName=ntpath.normpath(self.smb_cwd+ntpath.sep+path),
+970)
+971exceptExceptionaserr:
+972self.logger.error("Failed to remove directory '%s': %s"%(path,err))
+973ifself.config.debug:
+974traceback.print_exc()
@@ -4220,58 +4371,58 @@
-
915defrm(self,path=None):
-916"""
-917 Removes a file from the SMB share at the specified path.
-918
-919 This method attempts to delete a file located at the given path on the SMB share. If the operation fails,
-920 it prints an error message indicating the failure and the reason. If debugging is enabled, it also prints
-921 the stack trace of the exception.
-922
-923 Args:
-924 path (str, optional): The path of the file to be removed on the SMB share. Defaults to None.
-925 """
-926
-927# Parse path
-928path=path.replace('/',ntpath.sep)
-929ifntpath.sepinpath:
-930tmp_search_path=ntpath.normpath(self.smb_cwd+ntpath.sep+ntpath.dirname(path))
-931else:
-932tmp_search_path=ntpath.normpath(self.smb_cwd+ntpath.sep)
-933# Parse filename
-934filename=ntpath.basename(path)
-935
-936# Search for the file
-937matches=self.smbClient.listPath(
-938shareName=self.smb_share,
-939path=tmp_search_path+ntpath.sep+'*'
-940)
-941
-942# Filter the entries
-943matching_entries=[]
-944forentryinmatches:
-945ifentry.is_directory():
-946# Skip directories
-947continue
-948ifentry.get_longname()==filename:
-949matching_entries.append(entry)
-950elif'*'infilename:
-951regexp=filename.replace('.','\\.').replace('*','.*')
-952ifre.match(regexp,entry.get_longname()):
-953matching_entries.append(entry)
-954
-955matching_entries=sorted(list(set(matching_entries)),key=lambdax:x.get_longname())
-956
-957forentryinmatching_entries:
-958try:
-959self.smbClient.deleteFile(
-960shareName=self.smb_share,
-961pathName=ntpath.normpath(tmp_search_path+ntpath.sep+entry.get_longname()),
-962)
-963exceptExceptionaserr:
-964print("[!] Failed to remove file '%s': %s"%(path,err))
-965ifself.config.debug:
-966traceback.print_exc()
+
976defrm(self,path=None):
+ 977"""
+ 978 Removes a file from the SMB share at the specified path.
+ 979
+ 980 This method attempts to delete a file located at the given path on the SMB share. If the operation fails,
+ 981 it prints an error message indicating the failure and the reason. If debugging is enabled, it also prints
+ 982 the stack trace of the exception.
+ 983
+ 984 Args:
+ 985 path (str, optional): The path of the file to be removed on the SMB share. Defaults to None.
+ 986 """
+ 987
+ 988# Parse path
+ 989path=path.replace('/',ntpath.sep)
+ 990ifntpath.sepinpath:
+ 991tmp_search_path=ntpath.normpath(self.smb_cwd+ntpath.sep+ntpath.dirname(path))
+ 992else:
+ 993tmp_search_path=ntpath.normpath(self.smb_cwd+ntpath.sep)
+ 994# Parse filename
+ 995filename=ntpath.basename(path)
+ 996
+ 997# Search for the file
+ 998matches=self.smbClient.listPath(
+ 999shareName=self.smb_share,
+1000path=tmp_search_path+ntpath.sep+'*'
+1001)
+1002
+1003# Filter the entries
+1004matching_entries=[]
+1005forentryinmatches:
+1006ifentry.is_directory():
+1007# Skip directories
+1008continue
+1009ifentry.get_longname()==filename:
+1010matching_entries.append(entry)
+1011elif'*'infilename:
+1012regexp=filename.replace('.','\\.').replace('*','.*')
+1013ifre.match(regexp,entry.get_longname()):
+1014matching_entries.append(entry)
+1015
+1016matching_entries=sorted(list(set(matching_entries)),key=lambdax:x.get_longname())
+1017
+1018forentryinmatching_entries:
+1019try:
+1020self.smbClient.deleteFile(
+1021shareName=self.smb_share,
+1022pathName=ntpath.normpath(tmp_search_path+ntpath.sep+entry.get_longname()),
+1023)
+1024exceptExceptionaserr:
+1025self.logger.error("Failed to remove file '%s': %s"%(path,err))
+1026ifself.config.debug:
+1027traceback.print_exc()
@@ -4298,135 +4449,135 @@
-
968deftree(self,path=None):
- 969"""
- 970 Recursively lists the directory structure of the SMB share starting from the specified path.
- 971
- 972 This function prints a visual representation of the directory tree of the remote SMB share. It uses
- 973 recursion to navigate through directories and lists all files and subdirectories in each directory.
- 974 The output is color-coded and formatted to enhance readability, with directories highlighted in cyan.
- 975
- 976 Args:
- 977 path (str, optional): The starting path on the SMB share from which to begin listing the tree.
- 978 Defaults to the root of the current share.
- 979 """
- 980
- 981defrecurse_action(base_dir="",path=[],prompt=[]):
- 982bars=["│ ","├── ","└── "]
- 983
- 984remote_smb_path=ntpath.normpath(base_dir+ntpath.sep+ntpath.sep.join(path))
- 985
- 986entries=[]
- 987try:
- 988entries=self.smbClient.listPath(
- 989shareName=self.smb_share,
- 990path=remote_smb_path+'\\*'
- 991)
- 992exceptimpacket.smbconnection.SessionErroraserr:
- 993code,const,text=err.getErrorCode(),err.getErrorString()[0],err.getErrorString()[1]
- 994errmsg="Error 0x%08x (%s): %s"%(code,const,text)
- 995ifself.config.no_colors:
- 996print("%s%s"%(''.join(prompt+[bars[2]]),errmsg))
- 997else:
- 998print("%s\x1b[1;91m%s\x1b[0m"%(''.join(prompt+[bars[2]]),errmsg))
- 999return
-1000
-1001entries=[eforeinentriesife.get_longname()notin[".",".."]]
-1002entries=sorted(entries,key=lambdax:x.get_longname())
-1003
-1004#
-1005iflen(entries)>1:
-1006index=0
-1007forentryinentries:
-1008index+=1
-1009# This is the first entry
-1010ifindex==0:
-1011ifentry.is_directory():
-1012ifself.config.no_colors:
-1013print("%s%s\\"%(''.join(prompt+[bars[1]]),entry.get_longname()))
-1014else:
-1015print("%s\x1b[1;96m%s\x1b[0m\\"%(''.join(prompt+[bars[1]]),entry.get_longname()))
-1016recurse_action(
-1017base_dir=base_dir,
-1018path=path+[entry.get_longname()],
-1019prompt=prompt+["│ "]
-1020)
-1021else:
-1022ifself.config.no_colors:
-1023print("%s%s"%(''.join(prompt+[bars[1]]),entry.get_longname()))
-1024else:
-1025print("%s\x1b[1m%s\x1b[0m"%(''.join(prompt+[bars[1]]),entry.get_longname()))
-1026
-1027# This is the last entry
-1028elifindex==len(entries):
-1029ifentry.is_directory():
-1030ifself.config.no_colors:
-1031print("%s%s\\"%(''.join(prompt+[bars[2]]),entry.get_longname()))
-1032else:
-1033print("%s\x1b[1;96m%s\x1b[0m\\"%(''.join(prompt+[bars[2]]),entry.get_longname()))
-1034recurse_action(
-1035base_dir=base_dir,
-1036path=path+[entry.get_longname()],
-1037prompt=prompt+[" "]
-1038)
-1039else:
-1040ifself.config.no_colors:
-1041print("%s%s"%(''.join(prompt+[bars[2]]),entry.get_longname()))
-1042else:
-1043print("%s\x1b[1m%s\x1b[0m"%(''.join(prompt+[bars[2]]),entry.get_longname()))
-1044
-1045# These are entries in the middle
-1046else:
-1047ifentry.is_directory():
-1048ifself.config.no_colors:
-1049print("%s%s\\"%(''.join(prompt+[bars[1]]),entry.get_longname()))
-1050else:
-1051print("%s\x1b[1;96m%s\x1b[0m\\"%(''.join(prompt+[bars[1]]),entry.get_longname()))
-1052recurse_action(
-1053base_dir=base_dir,
-1054path=path+[entry.get_longname()],
-1055prompt=prompt+["│ "]
-1056)
-1057else:
-1058ifself.config.no_colors:
-1059print("%s%s"%(''.join(prompt+[bars[1]]),entry.get_longname()))
-1060else:
-1061print("%s\x1b[1m%s\x1b[0m"%(''.join(prompt+[bars[1]]),entry.get_longname()))
-1062
-1063#
-1064eliflen(entries)==1:
-1065entry=entries[0]
-1066ifentry.is_directory():
-1067ifself.config.no_colors:
-1068print("%s%s\\"%(''.join(prompt+[bars[2]]),entry.get_longname()))
-1069else:
-1070print("%s\x1b[1;96m%s\x1b[0m\\"%(''.join(prompt+[bars[2]]),entry.get_longname()))
-1071recurse_action(
-1072base_dir=base_dir,
-1073path=path+[entry.get_longname()],
-1074prompt=prompt+[" "]
-1075)
-1076else:
-1077ifself.config.no_colors:
-1078print("%s%s"%(''.join(prompt+[bars[2]]),entry.get_longname()))
-1079else:
-1080print("%s\x1b[1m%s\x1b[0m"%(''.join(prompt+[bars[2]]),entry.get_longname()))
-1081
-1082# Entrypoint
-1083try:
-1084ifself.config.no_colors:
-1085print("%s\\"%path)
-1086else:
-1087print("\x1b[1;96m%s\x1b[0m\\"%path)
-1088recurse_action(
-1089base_dir=self.smb_cwd,
-1090path=[path],
-1091prompt=[""]
-1092)
-1093except(BrokenPipeError,KeyboardInterrupt)ase:
-1094print("[!] Interrupted.")
-1095self.close_smb_session()
-1096self.init_smb_session()
+
1029deftree(self,path=None):
+1030"""
+1031 Recursively lists the directory structure of the SMB share starting from the specified path.
+1032
+1033 This function prints a visual representation of the directory tree of the remote SMB share. It uses
+1034 recursion to navigate through directories and lists all files and subdirectories in each directory.
+1035 The output is color-coded and formatted to enhance readability, with directories highlighted in cyan.
+1036
+1037 Args:
+1038 path (str, optional): The starting path on the SMB share from which to begin listing the tree.
+1039 Defaults to the root of the current share.
+1040 """
+1041
+1042defrecurse_action(base_dir="",path=[],prompt=[]):
+1043bars=["│ ","├── ","└── "]
+1044
+1045remote_smb_path=ntpath.normpath(base_dir+ntpath.sep+ntpath.sep.join(path))
+1046
+1047entries=[]
+1048try:
+1049entries=self.smbClient.listPath(
+1050shareName=self.smb_share,
+1051path=remote_smb_path+'\\*'
+1052)
+1053exceptimpacket.smbconnection.SessionErroraserr:
+1054code,const,text=err.getErrorCode(),err.getErrorString()[0],err.getErrorString()[1]
+1055errmsg="Error 0x%08x (%s): %s"%(code,const,text)
+1056ifself.config.no_colors:
+1057self.logger.print("%s%s"%(''.join(prompt+[bars[2]]),errmsg))
+1058else:
+1059self.logger.print("%s\x1b[1;91m%s\x1b[0m"%(''.join(prompt+[bars[2]]),errmsg))
+1060return
+1061
+1062entries=[eforeinentriesife.get_longname()notin[".",".."]]
+1063entries=sorted(entries,key=lambdax:x.get_longname())
+1064
+1065#
+1066iflen(entries)>1:
+1067index=0
+1068forentryinentries:
+1069index+=1
+1070# This is the first entry
+1071ifindex==0:
+1072ifentry.is_directory():
+1073ifself.config.no_colors:
+1074self.logger.print("%s%s\\"%(''.join(prompt+[bars[1]]),entry.get_longname()))
+1075else:
+1076self.logger.print("%s\x1b[1;96m%s\x1b[0m\\"%(''.join(prompt+[bars[1]]),entry.get_longname()))
+1077recurse_action(
+1078base_dir=base_dir,
+1079path=path+[entry.get_longname()],
+1080prompt=prompt+["│ "]
+1081)
+1082else:
+1083ifself.config.no_colors:
+1084self.logger.print("%s%s"%(''.join(prompt+[bars[1]]),entry.get_longname()))
+1085else:
+1086self.logger.print("%s\x1b[1m%s\x1b[0m"%(''.join(prompt+[bars[1]]),entry.get_longname()))
+1087
+1088# This is the last entry
+1089elifindex==len(entries):
+1090ifentry.is_directory():
+1091ifself.config.no_colors:
+1092self.logger.print("%s%s\\"%(''.join(prompt+[bars[2]]),entry.get_longname()))
+1093else:
+1094self.logger.print("%s\x1b[1;96m%s\x1b[0m\\"%(''.join(prompt+[bars[2]]),entry.get_longname()))
+1095recurse_action(
+1096base_dir=base_dir,
+1097path=path+[entry.get_longname()],
+1098prompt=prompt+[" "]
+1099)
+1100else:
+1101ifself.config.no_colors:
+1102self.logger.print("%s%s"%(''.join(prompt+[bars[2]]),entry.get_longname()))
+1103else:
+1104self.logger.print("%s\x1b[1m%s\x1b[0m"%(''.join(prompt+[bars[2]]),entry.get_longname()))
+1105
+1106# These are entries in the middle
+1107else:
+1108ifentry.is_directory():
+1109ifself.config.no_colors:
+1110self.logger.print("%s%s\\"%(''.join(prompt+[bars[1]]),entry.get_longname()))
+1111else:
+1112self.logger.print("%s\x1b[1;96m%s\x1b[0m\\"%(''.join(prompt+[bars[1]]),entry.get_longname()))
+1113recurse_action(
+1114base_dir=base_dir,
+1115path=path+[entry.get_longname()],
+1116prompt=prompt+["│ "]
+1117)
+1118else:
+1119ifself.config.no_colors:
+1120self.logger.print("%s%s"%(''.join(prompt+[bars[1]]),entry.get_longname()))
+1121else:
+1122self.logger.print("%s\x1b[1m%s\x1b[0m"%(''.join(prompt+[bars[1]]),entry.get_longname()))
+1123
+1124#
+1125eliflen(entries)==1:
+1126entry=entries[0]
+1127ifentry.is_directory():
+1128ifself.config.no_colors:
+1129self.logger.print("%s%s\\"%(''.join(prompt+[bars[2]]),entry.get_longname()))
+1130else:
+1131self.logger.print("%s\x1b[1;96m%s\x1b[0m\\"%(''.join(prompt+[bars[2]]),entry.get_longname()))
+1132recurse_action(
+1133base_dir=base_dir,
+1134path=path+[entry.get_longname()],
+1135prompt=prompt+[" "]
+1136)
+1137else:
+1138ifself.config.no_colors:
+1139self.logger.print("%s%s"%(''.join(prompt+[bars[2]]),entry.get_longname()))
+1140else:
+1141self.logger.print("%s\x1b[1m%s\x1b[0m"%(''.join(prompt+[bars[2]]),entry.get_longname()))
+1142
+1143# Entrypoint
+1144try:
+1145ifself.config.no_colors:
+1146self.logger.print("%s\\"%path)
+1147else:
+1148self.logger.print("\x1b[1;96m%s\x1b[0m\\"%path)
+1149recurse_action(
+1150base_dir=self.smb_cwd,
+1151path=[path],
+1152prompt=[""]
+1153)
+1154except(BrokenPipeError,KeyboardInterrupt)ase:
+1155self.logger.error("Interrupted.")
+1156self.close_smb_session()
+1157self.init_smb_session()
@@ -4454,37 +4605,36 @@
-
1098defumount(self,local_mount_point):
-1099"""
-1100 Unmounts the specified local mount point of the remote share.
-1101
-1102 This method unmounts the specified local mount point of the remote share based on the platform.
-1103 It supports Windows, Linux, and macOS platforms for unmounting.
-1104
-1105 Parameters:
-1106 local_mount_point (str): The local mount point to unmount.
-1107
-1108 Raises:
-1109 None
-1110 """
-1111
-1112ifos.path.exists(local_mount_point):
-1113ifsys.platform.startswith('win'):
-1114command=f"net use {local_mount_point} /delete"
-1115
-1116elifsys.platform.startswith('linux')orsys.platform.startswith('darwin'):
-1117command=f"umount {local_mount_point}"
-1118
-1119else:
-1120command=None
-1121print("[!] Unsupported platform for unmounting SMB share.")
-1122
-1123ifcommandisnotNone:
-1124ifself.config.debug:
-1125print("[debug] Executing: %s"%command)
-1126os.system(command)
-1127else:
-1128print("[!] Cannot unmount a non existing path.")
+
1159defumount(self,local_mount_point):
+1160"""
+1161 Unmounts the specified local mount point of the remote share.
+1162
+1163 This method unmounts the specified local mount point of the remote share based on the platform.
+1164 It supports Windows, Linux, and macOS platforms for unmounting.
+1165
+1166 Parameters:
+1167 local_mount_point (str): The local mount point to unmount.
+1168
+1169 Raises:
+1170 None
+1171 """
+1172
+1173ifos.path.exists(local_mount_point):
+1174ifsys.platform.startswith('win'):
+1175command=f"net use {local_mount_point} /delete"
+1176
+1177elifsys.platform.startswith('linux')orsys.platform.startswith('darwin'):
+1178command=f"umount {local_mount_point}"
+1179
+1180else:
+1181command=None
+1182self.logger.error("Unsupported platform for unmounting SMB share.")
+1183
+1184ifcommandisnotNone:
+1185self.logger.debug("Executing: %s"%command)
+1186os.system(command)
+1187else:
+1188self.logger.error("Cannot unmount a non existing path.")
@@ -4513,41 +4663,41 @@
-
1132deftest_rights(self,sharename):
-1133"""
-1134 Tests the read and write access rights of the current SMB session.
-1135
-1136 This method checks the read and write access rights of the current SMB session by attempting to list paths and create/delete temporary directories.
-1137
-1138 Returns:
-1139 dict: A dictionary containing the read and write access rights status.
-1140 - "readable" (bool): Indicates if the session has read access rights.
-1141 - "writable" (bool): Indicates if the session has write access rights.
-1142 """
-1143
-1144# Restore the current share
-1145current_share=self.smb_share
-1146self.set_share(shareName=sharename)
-1147
-1148access_rights={"readable":False,"writable":False}
-1149try:
-1150self.smbClient.listPath(self.smb_share,'*',password=None)
-1151access_rights["readable"]=True
-1152exceptimpacket.smbconnection.SessionErrorase:
-1153access_rights["readable"]=False
-1154
-1155try:
-1156temp_dir=ntpath.normpath("\\"+''.join([random.choice("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPRSTUVWXYZ0123456759")forkinrange(16)]))
-1157self.smbClient.createDirectory(self.smb_share,temp_dir)
-1158self.smbClient.deleteDirectory(self.smb_share,temp_dir)
-1159access_rights["writable"]=True
-1160exceptimpacket.smbconnection.SessionErrorase:
-1161access_rights["writable"]=False
-1162
-1163# Restore the current share
-1164self.set_share(shareName=current_share)
-1165
-1166returnaccess_rights
+
1192deftest_rights(self,sharename):
+1193"""
+1194 Tests the read and write access rights of the current SMB session.
+1195
+1196 This method checks the read and write access rights of the current SMB session by attempting to list paths and create/delete temporary directories.
+1197
+1198 Returns:
+1199 dict: A dictionary containing the read and write access rights status.
+1200 - "readable" (bool): Indicates if the session has read access rights.
+1201 - "writable" (bool): Indicates if the session has write access rights.
+1202 """
+1203
+1204# Restore the current share
+1205current_share=self.smb_share
+1206self.set_share(shareName=sharename)
+1207
+1208access_rights={"readable":False,"writable":False}
+1209try:
+1210self.smbClient.listPath(self.smb_share,'*',password=None)
+1211access_rights["readable"]=True
+1212exceptimpacket.smbconnection.SessionErrorase:
+1213access_rights["readable"]=False
+1214
+1215try:
+1216temp_dir=ntpath.normpath("\\"+''.join([random.choice("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPRSTUVWXYZ0123456759")forkinrange(16)]))
+1217self.smbClient.createDirectory(self.smb_share,temp_dir)
+1218self.smbClient.deleteDirectory(self.smb_share,temp_dir)
+1219access_rights["writable"]=True
+1220exceptimpacket.smbconnection.SessionErrorase:
+1221access_rights["writable"]=False
+1222
+1223# Restore the current share
+1224self.set_share(shareName=current_share)
+1225
+1226returnaccess_rights
@@ -4574,32 +4724,32 @@
-
1170defset_share(self,shareName):
-1171"""
-1172 Sets the current SMB share to the specified share name.
-1173
-1174 This method updates the SMB session to use the specified share name. It checks if the share name is valid
-1175 and updates the smb_share attribute of the SMBSession instance.
-1176
-1177 Parameters:
-1178 shareName (str): The name of the share to set as the current SMB share.
-1179
-1180 Raises:
-1181 ValueError: If the shareName is None or an empty string.
-1182 """
-1183
-1184ifshareNameisnotNone:
-1185self.list_shares()
-1186ifshareName.lower()inself.available_shares.keys():
-1187# Doing this in order to keep the case of the share adevertised by the remote machine
-1188self.smb_share=self.available_shares[shareName.lower()]["name"]
-1189self.smb_cwd=""
-1190# Connects the tree
-1191self.smb_tree_id=self.smbClient.connectTree(self.smb_share)
-1192else:
-1193print("[!] Could not set share '%s', it does not exist remotely."%shareName)
-1194else:
-1195self.smb_share=None
+
1230defset_share(self,shareName):
+1231"""
+1232 Sets the current SMB share to the specified share name.
+1233
+1234 This method updates the SMB session to use the specified share name. It checks if the share name is valid
+1235 and updates the smb_share attribute of the SMBSession instance.
+1236
+1237 Parameters:
+1238 shareName (str): The name of the share to set as the current SMB share.
+1239
+1240 Raises:
+1241 ValueError: If the shareName is None or an empty string.
+1242 """
+1243
+1244ifshareNameisnotNone:
+1245self.list_shares()
+1246ifshareName.lower()inself.available_shares.keys():
+1247# Doing this in order to keep the case of the share adevertised by the remote machine
+1248self.smb_share=self.available_shares[shareName.lower()]["name"]
+1249self.smb_cwd=""
+1250# Connects the tree
+1251self.smb_tree_id=self.smbClient.connectTree(self.smb_share)
+1252else:
+1253self.logger.error("Could not set share '%s', it does not exist remotely."%shareName)
+1254else:
+1255self.smb_share=None
@@ -4628,48 +4778,48 @@
-
1197defset_cwd(self,path=None):
-1198"""
-1199 Sets the current working directory on the SMB share to the specified path.
-1200
-1201 This method updates the current working directory (cwd) of the SMB session to the given path if it is a valid directory.
-1202 If the specified path is not a directory, the cwd remains unchanged.
-1203
-1204 Parameters:
-1205 path (str): The path to set as the current working directory.
-1206
-1207 Raises:
-1208 ValueError: If the specified path is not a directory.
-1209 """
-1210
-1211ifpathisnotNone:
-1212# Set path separators to ntpath sep
-1213if'/'inpath:
-1214path=path.replace('/',ntpath.sep)
-1215
-1216ifpath.startswith(ntpath.sep):
-1217# Absolute path
-1218path=path+ntpath.sep
-1219else:
-1220# Relative path to the CWD
-1221iflen(self.smb_cwd)==0:
-1222path=path+ntpath.sep
-1223else:
-1224path=self.smb_cwd+ntpath.sep+path
-1225
-1226# Path normalization
-1227path=ntpath.normpath(path)
-1228path=re.sub(r'\\+',r'\\',path)
-1229
-1230ifpathin["",".",".."]:
-1231self.smb_cwd=""
-1232else:
-1233ifself.path_isdir(pathFromRoot=path.strip(ntpath.sep)):
-1234# Path exists on the remote
-1235self.smb_cwd=ntpath.normpath(path)
-1236else:
-1237# Path does not exists or is not a directory on the remote
-1238print("[!] Remote directory '%s' does not exist."%path)
+
1257defset_cwd(self,path=None):
+1258"""
+1259 Sets the current working directory on the SMB share to the specified path.
+1260
+1261 This method updates the current working directory (cwd) of the SMB session to the given path if it is a valid directory.
+1262 If the specified path is not a directory, the cwd remains unchanged.
+1263
+1264 Parameters:
+1265 path (str): The path to set as the current working directory.
+1266
+1267 Raises:
+1268 ValueError: If the specified path is not a directory.
+1269 """
+1270
+1271ifpathisnotNone:
+1272# Set path separators to ntpath sep
+1273if'/'inpath:
+1274path=path.replace('/',ntpath.sep)
+1275
+1276ifpath.startswith(ntpath.sep):
+1277# Absolute path
+1278path=path+ntpath.sep
+1279else:
+1280# Relative path to the CWD
+1281iflen(self.smb_cwd)==0:
+1282path=path+ntpath.sep
+1283else:
+1284path=self.smb_cwd+ntpath.sep+path
+1285
+1286# Path normalization
+1287path=ntpath.normpath(path)
+1288path=re.sub(r'\\+',r'\\',path)
+1289
+1290ifpathin["",".",".."]:
+1291self.smb_cwd=""
+1292else:
+1293ifself.path_isdir(pathFromRoot=path.strip(ntpath.sep)):
+1294# Path exists on the remote
+1295self.smb_cwd=ntpath.normpath(path)
+1296else:
+1297# Path does not exists or is not a directory on the remote
+1298self.logger.error("Remote directory '%s' does not exist."%path)
1#!/usr/bin/env python3
+ 2# -*- coding: utf-8 -*-
+ 3# File name : SessionsManager.py
+ 4# Author : Podalirius (@podalirius_)
+ 5# Date created : 20 may 2024
+ 6
+ 7importdatetime
+ 8fromsmbclientng.core.CredentialsimportCredentials
+ 9fromsmbclientng.core.ModuleArgumentParserimportModuleArgumentParser
+ 10fromsmbclientng.core.SMBSessionimportSMBSession
+ 11importtime
+ 12
+ 13
+ 14classSessionsManager(object):
+ 15"""
+ 16 A class to manage SMB sessions.
+ 17
+ 18 This class is responsible for creating, managing, and switching between multiple SMB sessions. It allows for the creation of new sessions with specified credentials and hosts, and provides methods to switch between existing sessions. It also keeps track of the current session and its ID.
+ 19
+ 20 Attributes:
+ 21 next_session_id (int): The next available session ID.
+ 22 current_session (SMBSession): The currently active SMB session.
+ 23 current_session_id (int): The ID of the currently active session.
+ 24 sessions (dict): A dictionary of all active sessions, keyed by their session ID.
+ 25 """
+ 26
+ 27next_session_id=1
+ 28current_session=None
+ 29current_session_id=None
+ 30sessions={}
+ 31
+ 32def__init__(self,config,logger):
+ 33self.sessions={}
+ 34self.next_session_id=1
+ 35self.current_session=None
+ 36self.current_session_id=None
+ 37
+ 38self.config=config
+ 39self.logger=logger
+ 40
+ 41defcreate_new_session(self,credentials,host,port=445):
+ 42"""
+ 43 Creates a new session with the given session information.
+ 44
+ 45 Args:
+ 46 session_info (dict): Information necessary to start a new session.
+ 47
+ 48 Returns:
+ 49 None
+ 50 """
+ 51
+ 52smbSession=SMBSession(
+ 53host=host,
+ 54port=port,
+ 55credentials=credentials,
+ 56config=self.config,
+ 57logger=self.logger
+ 58)
+ 59smbSession.init_smb_session()
+ 60
+ 61self.sessions[self.next_session_id]={
+ 62"id":self.next_session_id,
+ 63"smbSession":smbSession,
+ 64"created_at":int(time.time()),
+ 65}
+ 66self.switch_session(self.next_session_id)
+ 67self.next_session_id+=1
+ 68
+ 69defswitch_session(self,session_id):
+ 70"""
+ 71 Switches the current session to the session with the specified ID.
+ 72
+ 73 Args:
+ 74 session_id (int): The ID of the session to switch to.
+ 75
+ 76 Returns:
+ 77 bool: True if the session was successfully switched, False otherwise.
+ 78 """
+ 79
+ 80ifsession_idinself.sessions.keys():
+ 81self.current_session=self.sessions[session_id]["smbSession"]
+ 82self.current_session_id=session_id
+ 83returnTrue
+ 84else:
+ 85returnFalse
+ 86
+ 87defdelete_session(self,session_id):
+ 88"""
+ 89 Deletes a session with the given session ID.
+ 90
+ 91 Args:
+ 92 session_id (int): The ID of the session to delete.
+ 93
+ 94 Returns:
+ 95 bool: True if the session was successfully deleted, False otherwise.
+ 96 """
+ 97
+ 98ifsession_idinself.sessions.keys():
+ 99self.sessions[session_id]["smbSession"].close_smb_session()
+100delself.sessions[session_id]
+101ifself.current_session_id==session_id:
+102self.current_session=None
+103self.current_session_id=None
+104returnTrue
+105returnFalse
+106
+107defprocess_command_line(self,arguments):
+108"""
+109 Processes command line arguments to manage SMB sessions.
+110
+111 This function parses the command line arguments provided to the application and determines the appropriate action to take,
+112 such as creating, interacting, deleting, or listing SMB sessions, or executing a command in one or more sessions.
+113
+114 Args:
+115 arguments (list of str): The command line arguments.
+116
+117 Returns:
+118 None
+119 """
+120
+121parser=ModuleArgumentParser(add_help=True,prog="sessions",description="")
+122
+123# interact
+124mode_interact=ModuleArgumentParser(add_help=False,description="Switch to the specified session.")
+125mode_interact.add_argument("-i","--session-id",type=int,default=None,required=True,help="Session ID to interact with.")
+126
+127# Create
+128mode_create=ModuleArgumentParser(add_help=False,description="Create a new session.")
+129group_target=mode_create.add_argument_group("Target")
+130group_target.add_argument("--host",action="store",metavar="HOST",required=True,type=str,help="IP address or hostname of the SMB Server to connect to.")
+131group_target.add_argument("--port",action="store",metavar="PORT",type=int,default=445,help="Port of the SMB Server to connect to. (default: 445)")
+132authconn=mode_create.add_argument_group("Authentication & connection")
+133authconn.add_argument("--kdcHost",dest="kdcHost",action="store",metavar="FQDN KDC",help="FQDN of KDC for Kerberos.")
+134authconn.add_argument("-d","--domain",dest="auth_domain",metavar="DOMAIN",action="store",default='.',help="(FQDN) domain to authenticate to.")
+135authconn.add_argument("-u","--user",dest="auth_username",metavar="USER",action="store",help="User to authenticate with.")
+136secret=mode_create.add_argument_group()
+137cred=secret.add_mutually_exclusive_group()
+138cred.add_argument("--no-pass",action="store_true",help="Don't ask for password (useful for -k).")
+139cred.add_argument("-p","--password",dest="auth_password",metavar="PASSWORD",action="store",nargs="?",help="Password to authenticate with.")
+140cred.add_argument("-H","--hashes",dest="auth_hashes",action="store",metavar="[LMHASH:]NTHASH",help="NT/LM hashes, format is LMhash:NThash.")
+141cred.add_argument("--aes-key",dest="aesKey",action="store",metavar="hex key",help="AES key to use for Kerberos Authentication (128 or 256 bits).")
+142secret.add_argument("-k","--kerberos",dest="use_kerberos",action="store_true",help="Use Kerberos authentication. Grabs credentials from .ccache file (KRB5CCNAME) based on target parameters. If valid credentials cannot be found, it will use the ones specified in the command line.")
+143
+144# Delete
+145mode_delete=ModuleArgumentParser(add_help=False,description="Delete the specified session.")
+146group_sessions=mode_delete.add_mutually_exclusive_group(required=True)
+147group_sessions.add_argument("-i","--session-id",type=int,default=[],action="append",help="One or more ID of sessions to target.")
+148group_sessions.add_argument("-a","--all",default=False,action="store_true",help="Delete all sessions.")
+149
+150# Execute
+151mode_execute=ModuleArgumentParser(add_help=False,description="Send a smbclient-ng command line in one or more sessions.")
+152group_sessions=mode_execute.add_mutually_exclusive_group(required=True)
+153group_sessions.add_argument("-i","--session-id",type=int,default=[],action="append",help="One or more ID of sessions to target.")
+154group_sessions.add_argument("-a","--all",default=False,action="store_true",help="Execute command in all sessions.")
+155mode_execute.add_argument("-c","--command",type=str,required=True,help="Command to execute in the target sessions.")
+156
+157# List
+158mode_list=ModuleArgumentParser(add_help=False,description="List the registered sessions.")
+159
+160# Register subparsers
+161subparsers=parser.add_subparsers(help="Action",dest="action",required=True)
+162subparsers.add_parser("interact",parents=[mode_interact],help=mode_interact.description)
+163subparsers.add_parser("create",parents=[mode_create],help=mode_create.description)
+164subparsers.add_parser("delete",parents=[mode_delete],help=mode_delete.description)
+165subparsers.add_parser("execute",parents=[mode_execute],help=mode_execute.description)
+166subparsers.add_parser("list",parents=[mode_list],help=mode_list.description)
+167
+168try:
+169options=parser.parse_args(arguments)
+170exceptSystemExitase:
+171pass
+172
+173# Process actions
+174
+175#
+176ifoptions.action=="interact":
+177ifoptions.session_idisnotNone:
+178ifoptions.session_idinself.sessions.keys():
+179self.logger.info("Switching to session #%d"%options.session_id)
+180self.switch_session(session_id=options.session_id)
+181else:
+182self.logger.error("No session with id #%d"%options.session_id)
+183
+184#
+185elifoptions.action=="create":
+186credentials=Credentials(
+187domain=options.auth_domain,
+188username=options.auth_username,
+189password=options.auth_password,
+190hashes=options.auth_hashes,
+191use_kerberos=options.use_kerberos,
+192aesKey=options.aesKey,
+193kdcHost=options.kdcHost
+194)
+195self.create_new_session(
+196credentials=credentials,
+197host=options.host,
+198port=options.port
+199)
+200
+201#
+202elifoptions.action=="delete":
+203iflen(options.session_id)!=0:
+204forsession_idinoptions.session_id:
+205ifsession_idinself.sessions.keys():
+206self.logger.info("Closing and deleting session #%d"%session_id)
+207self.delete_session(session_id=session_id)
+208else:
+209self.logger.error("No session with id #%d"%session_id)
+210elifoptions.all==True:
+211all_session_ids=list(self.sessions.keys())
+212forsession_idinall_session_ids:
+213print("[+] Closing and deleting session #%d"%session_id)
+214self.delete_session(session_id=session_id)
+215
+216#
+217elifoptions.action=="execute":
+218ifoptions.commandisnotNone:
+219iflen(options.session_id)!=0:
+220forsession_idinsession_id:
+221ifsession_idinself.sessions.keys():
+222self.logger.info("Executing '%s to session #%d"%(options.command,options.session_id))
+223else:
+224self.logger.error("No session with id #%d"%options.session_id)
+225elifoptions.all==True:
+226all_session_ids=list(self.sessions.keys())
+227forsession_idinall_session_ids:
+228pass
+229
+230#
+231elifoptions.action=="list":
+232forsessionIdinsorted(self.sessions.keys()):
+233session=self.sessions[sessionId]["smbSession"]
+234created_at_str=str(datetime.datetime.fromtimestamp(self.sessions[sessionId]["created_at"]))
+235ifsessionId==self.current_session_id:
+236ifself.config.no_colors:
+237print(f"=> [#{sessionId:<2} - '{session.credentials.domain}\\{session.credentials.username}' @ {session.host}:{session.port}] created at [{created_at_str}] [current session]")
+238else:
+239print(f"\x1b[48;2;50;50;50m=> #{sessionId:<2} - '\x1b[1;96m{session.credentials.domain}\x1b[0m\x1b[48;2;50;50;50m\\\x1b[1;96m{session.credentials.username}\x1b[0m\x1b[48;2;50;50;50m\x1b[1m' @ {session.host}:{session.port} created at [{created_at_str}]\x1b[0m\x1b[48;2;50;50;50m [\x1b[93mcurrent session\x1b[0m\x1b[48;2;50;50;50m]\x1b[0m")
+240else:
+241print(f"── #{sessionId:<2} - '\x1b[1;96m{session.credentials.domain}\x1b[0m\\\x1b[1;96m{session.credentials.username}\x1b[0m\x1b[1m' @ {session.host}:{session.port} created at [{created_at_str}]\x1b[0m")
+242
+
+
+
+
+
+
+
+
+ class
+ SessionsManager:
+
+
+
+
+
+
15classSessionsManager(object):
+ 16"""
+ 17 A class to manage SMB sessions.
+ 18
+ 19 This class is responsible for creating, managing, and switching between multiple SMB sessions. It allows for the creation of new sessions with specified credentials and hosts, and provides methods to switch between existing sessions. It also keeps track of the current session and its ID.
+ 20
+ 21 Attributes:
+ 22 next_session_id (int): The next available session ID.
+ 23 current_session (SMBSession): The currently active SMB session.
+ 24 current_session_id (int): The ID of the currently active session.
+ 25 sessions (dict): A dictionary of all active sessions, keyed by their session ID.
+ 26 """
+ 27
+ 28next_session_id=1
+ 29current_session=None
+ 30current_session_id=None
+ 31sessions={}
+ 32
+ 33def__init__(self,config,logger):
+ 34self.sessions={}
+ 35self.next_session_id=1
+ 36self.current_session=None
+ 37self.current_session_id=None
+ 38
+ 39self.config=config
+ 40self.logger=logger
+ 41
+ 42defcreate_new_session(self,credentials,host,port=445):
+ 43"""
+ 44 Creates a new session with the given session information.
+ 45
+ 46 Args:
+ 47 session_info (dict): Information necessary to start a new session.
+ 48
+ 49 Returns:
+ 50 None
+ 51 """
+ 52
+ 53smbSession=SMBSession(
+ 54host=host,
+ 55port=port,
+ 56credentials=credentials,
+ 57config=self.config,
+ 58logger=self.logger
+ 59)
+ 60smbSession.init_smb_session()
+ 61
+ 62self.sessions[self.next_session_id]={
+ 63"id":self.next_session_id,
+ 64"smbSession":smbSession,
+ 65"created_at":int(time.time()),
+ 66}
+ 67self.switch_session(self.next_session_id)
+ 68self.next_session_id+=1
+ 69
+ 70defswitch_session(self,session_id):
+ 71"""
+ 72 Switches the current session to the session with the specified ID.
+ 73
+ 74 Args:
+ 75 session_id (int): The ID of the session to switch to.
+ 76
+ 77 Returns:
+ 78 bool: True if the session was successfully switched, False otherwise.
+ 79 """
+ 80
+ 81ifsession_idinself.sessions.keys():
+ 82self.current_session=self.sessions[session_id]["smbSession"]
+ 83self.current_session_id=session_id
+ 84returnTrue
+ 85else:
+ 86returnFalse
+ 87
+ 88defdelete_session(self,session_id):
+ 89"""
+ 90 Deletes a session with the given session ID.
+ 91
+ 92 Args:
+ 93 session_id (int): The ID of the session to delete.
+ 94
+ 95 Returns:
+ 96 bool: True if the session was successfully deleted, False otherwise.
+ 97 """
+ 98
+ 99ifsession_idinself.sessions.keys():
+100self.sessions[session_id]["smbSession"].close_smb_session()
+101delself.sessions[session_id]
+102ifself.current_session_id==session_id:
+103self.current_session=None
+104self.current_session_id=None
+105returnTrue
+106returnFalse
+107
+108defprocess_command_line(self,arguments):
+109"""
+110 Processes command line arguments to manage SMB sessions.
+111
+112 This function parses the command line arguments provided to the application and determines the appropriate action to take,
+113 such as creating, interacting, deleting, or listing SMB sessions, or executing a command in one or more sessions.
+114
+115 Args:
+116 arguments (list of str): The command line arguments.
+117
+118 Returns:
+119 None
+120 """
+121
+122parser=ModuleArgumentParser(add_help=True,prog="sessions",description="")
+123
+124# interact
+125mode_interact=ModuleArgumentParser(add_help=False,description="Switch to the specified session.")
+126mode_interact.add_argument("-i","--session-id",type=int,default=None,required=True,help="Session ID to interact with.")
+127
+128# Create
+129mode_create=ModuleArgumentParser(add_help=False,description="Create a new session.")
+130group_target=mode_create.add_argument_group("Target")
+131group_target.add_argument("--host",action="store",metavar="HOST",required=True,type=str,help="IP address or hostname of the SMB Server to connect to.")
+132group_target.add_argument("--port",action="store",metavar="PORT",type=int,default=445,help="Port of the SMB Server to connect to. (default: 445)")
+133authconn=mode_create.add_argument_group("Authentication & connection")
+134authconn.add_argument("--kdcHost",dest="kdcHost",action="store",metavar="FQDN KDC",help="FQDN of KDC for Kerberos.")
+135authconn.add_argument("-d","--domain",dest="auth_domain",metavar="DOMAIN",action="store",default='.',help="(FQDN) domain to authenticate to.")
+136authconn.add_argument("-u","--user",dest="auth_username",metavar="USER",action="store",help="User to authenticate with.")
+137secret=mode_create.add_argument_group()
+138cred=secret.add_mutually_exclusive_group()
+139cred.add_argument("--no-pass",action="store_true",help="Don't ask for password (useful for -k).")
+140cred.add_argument("-p","--password",dest="auth_password",metavar="PASSWORD",action="store",nargs="?",help="Password to authenticate with.")
+141cred.add_argument("-H","--hashes",dest="auth_hashes",action="store",metavar="[LMHASH:]NTHASH",help="NT/LM hashes, format is LMhash:NThash.")
+142cred.add_argument("--aes-key",dest="aesKey",action="store",metavar="hex key",help="AES key to use for Kerberos Authentication (128 or 256 bits).")
+143secret.add_argument("-k","--kerberos",dest="use_kerberos",action="store_true",help="Use Kerberos authentication. Grabs credentials from .ccache file (KRB5CCNAME) based on target parameters. If valid credentials cannot be found, it will use the ones specified in the command line.")
+144
+145# Delete
+146mode_delete=ModuleArgumentParser(add_help=False,description="Delete the specified session.")
+147group_sessions=mode_delete.add_mutually_exclusive_group(required=True)
+148group_sessions.add_argument("-i","--session-id",type=int,default=[],action="append",help="One or more ID of sessions to target.")
+149group_sessions.add_argument("-a","--all",default=False,action="store_true",help="Delete all sessions.")
+150
+151# Execute
+152mode_execute=ModuleArgumentParser(add_help=False,description="Send a smbclient-ng command line in one or more sessions.")
+153group_sessions=mode_execute.add_mutually_exclusive_group(required=True)
+154group_sessions.add_argument("-i","--session-id",type=int,default=[],action="append",help="One or more ID of sessions to target.")
+155group_sessions.add_argument("-a","--all",default=False,action="store_true",help="Execute command in all sessions.")
+156mode_execute.add_argument("-c","--command",type=str,required=True,help="Command to execute in the target sessions.")
+157
+158# List
+159mode_list=ModuleArgumentParser(add_help=False,description="List the registered sessions.")
+160
+161# Register subparsers
+162subparsers=parser.add_subparsers(help="Action",dest="action",required=True)
+163subparsers.add_parser("interact",parents=[mode_interact],help=mode_interact.description)
+164subparsers.add_parser("create",parents=[mode_create],help=mode_create.description)
+165subparsers.add_parser("delete",parents=[mode_delete],help=mode_delete.description)
+166subparsers.add_parser("execute",parents=[mode_execute],help=mode_execute.description)
+167subparsers.add_parser("list",parents=[mode_list],help=mode_list.description)
+168
+169try:
+170options=parser.parse_args(arguments)
+171exceptSystemExitase:
+172pass
+173
+174# Process actions
+175
+176#
+177ifoptions.action=="interact":
+178ifoptions.session_idisnotNone:
+179ifoptions.session_idinself.sessions.keys():
+180self.logger.info("Switching to session #%d"%options.session_id)
+181self.switch_session(session_id=options.session_id)
+182else:
+183self.logger.error("No session with id #%d"%options.session_id)
+184
+185#
+186elifoptions.action=="create":
+187credentials=Credentials(
+188domain=options.auth_domain,
+189username=options.auth_username,
+190password=options.auth_password,
+191hashes=options.auth_hashes,
+192use_kerberos=options.use_kerberos,
+193aesKey=options.aesKey,
+194kdcHost=options.kdcHost
+195)
+196self.create_new_session(
+197credentials=credentials,
+198host=options.host,
+199port=options.port
+200)
+201
+202#
+203elifoptions.action=="delete":
+204iflen(options.session_id)!=0:
+205forsession_idinoptions.session_id:
+206ifsession_idinself.sessions.keys():
+207self.logger.info("Closing and deleting session #%d"%session_id)
+208self.delete_session(session_id=session_id)
+209else:
+210self.logger.error("No session with id #%d"%session_id)
+211elifoptions.all==True:
+212all_session_ids=list(self.sessions.keys())
+213forsession_idinall_session_ids:
+214print("[+] Closing and deleting session #%d"%session_id)
+215self.delete_session(session_id=session_id)
+216
+217#
+218elifoptions.action=="execute":
+219ifoptions.commandisnotNone:
+220iflen(options.session_id)!=0:
+221forsession_idinsession_id:
+222ifsession_idinself.sessions.keys():
+223self.logger.info("Executing '%s to session #%d"%(options.command,options.session_id))
+224else:
+225self.logger.error("No session with id #%d"%options.session_id)
+226elifoptions.all==True:
+227all_session_ids=list(self.sessions.keys())
+228forsession_idinall_session_ids:
+229pass
+230
+231#
+232elifoptions.action=="list":
+233forsessionIdinsorted(self.sessions.keys()):
+234session=self.sessions[sessionId]["smbSession"]
+235created_at_str=str(datetime.datetime.fromtimestamp(self.sessions[sessionId]["created_at"]))
+236ifsessionId==self.current_session_id:
+237ifself.config.no_colors:
+238print(f"=> [#{sessionId:<2} - '{session.credentials.domain}\\{session.credentials.username}' @ {session.host}:{session.port}] created at [{created_at_str}] [current session]")
+239else:
+240print(f"\x1b[48;2;50;50;50m=> #{sessionId:<2} - '\x1b[1;96m{session.credentials.domain}\x1b[0m\x1b[48;2;50;50;50m\\\x1b[1;96m{session.credentials.username}\x1b[0m\x1b[48;2;50;50;50m\x1b[1m' @ {session.host}:{session.port} created at [{created_at_str}]\x1b[0m\x1b[48;2;50;50;50m [\x1b[93mcurrent session\x1b[0m\x1b[48;2;50;50;50m]\x1b[0m")
+241else:
+242print(f"── #{sessionId:<2} - '\x1b[1;96m{session.credentials.domain}\x1b[0m\\\x1b[1;96m{session.credentials.username}\x1b[0m\x1b[1m' @ {session.host}:{session.port} created at [{created_at_str}]\x1b[0m")
+
+
+
+
A class to manage SMB sessions.
+
+
This class is responsible for creating, managing, and switching between multiple SMB sessions. It allows for the creation of new sessions with specified credentials and hosts, and provides methods to switch between existing sessions. It also keeps track of the current session and its ID.
+
+
Attributes:
+ next_session_id (int): The next available session ID.
+ current_session (SMBSession): The currently active SMB session.
+ current_session_id (int): The ID of the currently active session.
+ sessions (dict): A dictionary of all active sessions, keyed by their session ID.
70defswitch_session(self,session_id):
+71"""
+72 Switches the current session to the session with the specified ID.
+73
+74 Args:
+75 session_id (int): The ID of the session to switch to.
+76
+77 Returns:
+78 bool: True if the session was successfully switched, False otherwise.
+79 """
+80
+81ifsession_idinself.sessions.keys():
+82self.current_session=self.sessions[session_id]["smbSession"]
+83self.current_session_id=session_id
+84returnTrue
+85else:
+86returnFalse
+
+
+
+
Switches the current session to the session with the specified ID.
+
+
Args:
+ session_id (int): The ID of the session to switch to.
+
+
Returns:
+ bool: True if the session was successfully switched, False otherwise.
108defprocess_command_line(self,arguments):
+109"""
+110 Processes command line arguments to manage SMB sessions.
+111
+112 This function parses the command line arguments provided to the application and determines the appropriate action to take,
+113 such as creating, interacting, deleting, or listing SMB sessions, or executing a command in one or more sessions.
+114
+115 Args:
+116 arguments (list of str): The command line arguments.
+117
+118 Returns:
+119 None
+120 """
+121
+122parser=ModuleArgumentParser(add_help=True,prog="sessions",description="")
+123
+124# interact
+125mode_interact=ModuleArgumentParser(add_help=False,description="Switch to the specified session.")
+126mode_interact.add_argument("-i","--session-id",type=int,default=None,required=True,help="Session ID to interact with.")
+127
+128# Create
+129mode_create=ModuleArgumentParser(add_help=False,description="Create a new session.")
+130group_target=mode_create.add_argument_group("Target")
+131group_target.add_argument("--host",action="store",metavar="HOST",required=True,type=str,help="IP address or hostname of the SMB Server to connect to.")
+132group_target.add_argument("--port",action="store",metavar="PORT",type=int,default=445,help="Port of the SMB Server to connect to. (default: 445)")
+133authconn=mode_create.add_argument_group("Authentication & connection")
+134authconn.add_argument("--kdcHost",dest="kdcHost",action="store",metavar="FQDN KDC",help="FQDN of KDC for Kerberos.")
+135authconn.add_argument("-d","--domain",dest="auth_domain",metavar="DOMAIN",action="store",default='.',help="(FQDN) domain to authenticate to.")
+136authconn.add_argument("-u","--user",dest="auth_username",metavar="USER",action="store",help="User to authenticate with.")
+137secret=mode_create.add_argument_group()
+138cred=secret.add_mutually_exclusive_group()
+139cred.add_argument("--no-pass",action="store_true",help="Don't ask for password (useful for -k).")
+140cred.add_argument("-p","--password",dest="auth_password",metavar="PASSWORD",action="store",nargs="?",help="Password to authenticate with.")
+141cred.add_argument("-H","--hashes",dest="auth_hashes",action="store",metavar="[LMHASH:]NTHASH",help="NT/LM hashes, format is LMhash:NThash.")
+142cred.add_argument("--aes-key",dest="aesKey",action="store",metavar="hex key",help="AES key to use for Kerberos Authentication (128 or 256 bits).")
+143secret.add_argument("-k","--kerberos",dest="use_kerberos",action="store_true",help="Use Kerberos authentication. Grabs credentials from .ccache file (KRB5CCNAME) based on target parameters. If valid credentials cannot be found, it will use the ones specified in the command line.")
+144
+145# Delete
+146mode_delete=ModuleArgumentParser(add_help=False,description="Delete the specified session.")
+147group_sessions=mode_delete.add_mutually_exclusive_group(required=True)
+148group_sessions.add_argument("-i","--session-id",type=int,default=[],action="append",help="One or more ID of sessions to target.")
+149group_sessions.add_argument("-a","--all",default=False,action="store_true",help="Delete all sessions.")
+150
+151# Execute
+152mode_execute=ModuleArgumentParser(add_help=False,description="Send a smbclient-ng command line in one or more sessions.")
+153group_sessions=mode_execute.add_mutually_exclusive_group(required=True)
+154group_sessions.add_argument("-i","--session-id",type=int,default=[],action="append",help="One or more ID of sessions to target.")
+155group_sessions.add_argument("-a","--all",default=False,action="store_true",help="Execute command in all sessions.")
+156mode_execute.add_argument("-c","--command",type=str,required=True,help="Command to execute in the target sessions.")
+157
+158# List
+159mode_list=ModuleArgumentParser(add_help=False,description="List the registered sessions.")
+160
+161# Register subparsers
+162subparsers=parser.add_subparsers(help="Action",dest="action",required=True)
+163subparsers.add_parser("interact",parents=[mode_interact],help=mode_interact.description)
+164subparsers.add_parser("create",parents=[mode_create],help=mode_create.description)
+165subparsers.add_parser("delete",parents=[mode_delete],help=mode_delete.description)
+166subparsers.add_parser("execute",parents=[mode_execute],help=mode_execute.description)
+167subparsers.add_parser("list",parents=[mode_list],help=mode_list.description)
+168
+169try:
+170options=parser.parse_args(arguments)
+171exceptSystemExitase:
+172pass
+173
+174# Process actions
+175
+176#
+177ifoptions.action=="interact":
+178ifoptions.session_idisnotNone:
+179ifoptions.session_idinself.sessions.keys():
+180self.logger.info("Switching to session #%d"%options.session_id)
+181self.switch_session(session_id=options.session_id)
+182else:
+183self.logger.error("No session with id #%d"%options.session_id)
+184
+185#
+186elifoptions.action=="create":
+187credentials=Credentials(
+188domain=options.auth_domain,
+189username=options.auth_username,
+190password=options.auth_password,
+191hashes=options.auth_hashes,
+192use_kerberos=options.use_kerberos,
+193aesKey=options.aesKey,
+194kdcHost=options.kdcHost
+195)
+196self.create_new_session(
+197credentials=credentials,
+198host=options.host,
+199port=options.port
+200)
+201
+202#
+203elifoptions.action=="delete":
+204iflen(options.session_id)!=0:
+205forsession_idinoptions.session_id:
+206ifsession_idinself.sessions.keys():
+207self.logger.info("Closing and deleting session #%d"%session_id)
+208self.delete_session(session_id=session_id)
+209else:
+210self.logger.error("No session with id #%d"%session_id)
+211elifoptions.all==True:
+212all_session_ids=list(self.sessions.keys())
+213forsession_idinall_session_ids:
+214print("[+] Closing and deleting session #%d"%session_id)
+215self.delete_session(session_id=session_id)
+216
+217#
+218elifoptions.action=="execute":
+219ifoptions.commandisnotNone:
+220iflen(options.session_id)!=0:
+221forsession_idinsession_id:
+222ifsession_idinself.sessions.keys():
+223self.logger.info("Executing '%s to session #%d"%(options.command,options.session_id))
+224else:
+225self.logger.error("No session with id #%d"%options.session_id)
+226elifoptions.all==True:
+227all_session_ids=list(self.sessions.keys())
+228forsession_idinall_session_ids:
+229pass
+230
+231#
+232elifoptions.action=="list":
+233forsessionIdinsorted(self.sessions.keys()):
+234session=self.sessions[sessionId]["smbSession"]
+235created_at_str=str(datetime.datetime.fromtimestamp(self.sessions[sessionId]["created_at"]))
+236ifsessionId==self.current_session_id:
+237ifself.config.no_colors:
+238print(f"=> [#{sessionId:<2} - '{session.credentials.domain}\\{session.credentials.username}' @ {session.host}:{session.port}] created at [{created_at_str}] [current session]")
+239else:
+240print(f"\x1b[48;2;50;50;50m=> #{sessionId:<2} - '\x1b[1;96m{session.credentials.domain}\x1b[0m\x1b[48;2;50;50;50m\\\x1b[1;96m{session.credentials.username}\x1b[0m\x1b[48;2;50;50;50m\x1b[1m' @ {session.host}:{session.port} created at [{created_at_str}]\x1b[0m\x1b[48;2;50;50;50m [\x1b[93mcurrent session\x1b[0m\x1b[48;2;50;50;50m]\x1b[0m")
+241else:
+242print(f"── #{sessionId:<2} - '\x1b[1;96m{session.credentials.domain}\x1b[0m\\\x1b[1;96m{session.credentials.username}\x1b[0m\x1b[1m' @ {session.host}:{session.port} created at [{created_at_str}]\x1b[0m")
+
+
+
+
Processes command line arguments to manage SMB sessions.
+
+
This function parses the command line arguments provided to the application and determines the appropriate action to take,
+such as creating, interacting, deleting, or listing SMB sessions, or executing a command in one or more sessions.
+
+
Args:
+ arguments (list of str): The command line arguments.
+
+
Returns:
+ None
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/documentation/smbclientng/core/utils.html b/documentation/smbclientng/core/utils.html
index 068c3e3..66bafb5 100644
--- a/documentation/smbclientng/core/utils.html
+++ b/documentation/smbclientng/core/utils.html
@@ -48,6 +48,15 @@
6 7 8importdatetime
- 9importos
+ 9importfnmatch 10importntpath
- 11importre
- 12importstat
- 13
- 14
- 15defparse_lm_nt_hashes(lm_nt_hashes_string):
- 16"""
- 17 Parse the input string containing LM and NT hash values and return them separately.
- 18
- 19 This function takes a string containing LM and NT hash values, typically separated by a colon (:).
- 20 It returns the LM and NT hash values as separate strings. If only one hash value is provided, it is
- 21 assumed to be the NT hash and the LM hash is set to its default value. If no valid hash values are
- 22 found, both return values are empty strings.
- 23
- 24 Args:
- 25 lm_nt_hashes_string (str): A string containing LM and NT hash values separated by a colon.
- 26
- 27 Returns:
- 28 tuple: A tuple containing two strings (lm_hash_value, nt_hash_value).
- 29 - lm_hash_value: The LM hash value or its default if not provided.
- 30 - nt_hash_value: The NT hash value or its default if not provided.
- 31
- 32 Extracted from p0dalirius/sectools library
- 33 Src: https://github.com/p0dalirius/sectools/blob/7bb3f5cb7815ad4d4845713c8739e2e2b0ea4e75/sectools/windows/crypto.py#L11-L24
- 34 """
- 35
- 36lm_hash_value,nt_hash_value="",""
- 37iflm_nt_hashes_stringisnotNone:
- 38matched=re.match("([0-9a-f]{32})?(:)?([0-9a-f]{32})?",lm_nt_hashes_string.strip().lower())
- 39m_lm_hash,m_sep,m_nt_hash=matched.groups()
- 40ifm_lm_hashisNoneandm_sepisNoneandm_nt_hashisNone:
- 41lm_hash_value,nt_hash_value="",""
- 42elifm_lm_hashisNoneandm_nt_hashisnotNone:
- 43lm_hash_value="aad3b435b51404eeaad3b435b51404ee"
- 44nt_hash_value=m_nt_hash
- 45elifm_lm_hashisnotNoneandm_nt_hashisNone:
- 46lm_hash_value=m_lm_hash
- 47nt_hash_value="31d6cfe0d16ae931b73c59d7e0c089c0"
- 48returnlm_hash_value,nt_hash_value
- 49
- 50
- 51defb_filesize(l):
- 52"""
- 53 Convert a file size from bytes to a more readable format using the largest appropriate unit.
- 54
- 55 This function takes an integer representing a file size in bytes and converts it to a human-readable
- 56 string using the largest appropriate unit from bytes (B) to petabytes (PB). The result is rounded to
- 57 two decimal places.
- 58
- 59 Args:
- 60 l (int): The file size in bytes.
- 61
- 62 Returns:
- 63 str: A string representing the file size in a more readable format, including the appropriate unit.
- 64 """
- 65
- 66units=['B','kB','MB','GB','TB','PB']
- 67forkinrange(len(units)):
- 68ifl<(1024**(k+1)):
- 69break
- 70return"%4.2f%s"%(round(l/(1024**(k)),2),units[k])
- 71
- 72
- 73defunix_permissions(entryname):
- 74"""
- 75 Generate a string representing the Unix-style permissions for a given file or directory.
- 76
- 77 This function uses the os.lstat() method to retrieve the status of the specified file or directory,
- 78 then constructs a string that represents the Unix-style permissions based on the mode of the file.
- 79
- 80 Args:
- 81 entryname (str): The path to the file or directory for which permissions are being determined.
- 82
- 83 Returns:
- 84 str: A string of length 10 representing the Unix-style permissions (e.g., '-rwxr-xr--').
- 85 The first character is either 'd' (directory), '-' (not a directory), followed by
- 86 three groups of 'r', 'w', 'x' (read, write, execute permissions) for owner, group,
- 87 and others respectively.
- 88 """
- 89
- 90mode=os.lstat(entryname).st_mode
- 91permissions=[]
- 92
- 93permissions.append('d'ifstat.S_ISDIR(mode)else'-')
+ 11importos
+ 12importre
+ 13importsocket
+ 14importstat
+ 15
+ 16
+ 17defparse_lm_nt_hashes(lm_nt_hashes_string):
+ 18"""
+ 19 Parse the input string containing LM and NT hash values and return them separately.
+ 20
+ 21 This function takes a string containing LM and NT hash values, typically separated by a colon (:).
+ 22 It returns the LM and NT hash values as separate strings. If only one hash value is provided, it is
+ 23 assumed to be the NT hash and the LM hash is set to its default value. If no valid hash values are
+ 24 found, both return values are empty strings.
+ 25
+ 26 Args:
+ 27 lm_nt_hashes_string (str): A string containing LM and NT hash values separated by a colon.
+ 28
+ 29 Returns:
+ 30 tuple: A tuple containing two strings (lm_hash_value, nt_hash_value).
+ 31 - lm_hash_value: The LM hash value or its default if not provided.
+ 32 - nt_hash_value: The NT hash value or its default if not provided.
+ 33
+ 34 Extracted from p0dalirius/sectools library
+ 35 Src: https://github.com/p0dalirius/sectools/blob/7bb3f5cb7815ad4d4845713c8739e2e2b0ea4e75/sectools/windows/crypto.py#L11-L24
+ 36 """
+ 37
+ 38lm_hash_value,nt_hash_value="",""
+ 39iflm_nt_hashes_stringisnotNone:
+ 40matched=re.match("([0-9a-f]{32})?(:)?([0-9a-f]{32})?",lm_nt_hashes_string.strip().lower())
+ 41m_lm_hash,m_sep,m_nt_hash=matched.groups()
+ 42ifm_lm_hashisNoneandm_sepisNoneandm_nt_hashisNone:
+ 43lm_hash_value,nt_hash_value="",""
+ 44elifm_lm_hashisNoneandm_nt_hashisnotNone:
+ 45lm_hash_value="aad3b435b51404eeaad3b435b51404ee"
+ 46nt_hash_value=m_nt_hash
+ 47elifm_lm_hashisnotNoneandm_nt_hashisNone:
+ 48lm_hash_value=m_lm_hash
+ 49nt_hash_value="31d6cfe0d16ae931b73c59d7e0c089c0"
+ 50returnlm_hash_value,nt_hash_value
+ 51
+ 52
+ 53defb_filesize(l):
+ 54"""
+ 55 Convert a file size from bytes to a more readable format using the largest appropriate unit.
+ 56
+ 57 This function takes an integer representing a file size in bytes and converts it to a human-readable
+ 58 string using the largest appropriate unit from bytes (B) to petabytes (PB). The result is rounded to
+ 59 two decimal places.
+ 60
+ 61 Args:
+ 62 l (int): The file size in bytes.
+ 63
+ 64 Returns:
+ 65 str: A string representing the file size in a more readable format, including the appropriate unit.
+ 66 """
+ 67
+ 68units=['B','kB','MB','GB','TB','PB']
+ 69forkinrange(len(units)):
+ 70ifl<(1024**(k+1)):
+ 71break
+ 72return"%4.2f%s"%(round(l/(1024**(k)),2),units[k])
+ 73
+ 74
+ 75defunix_permissions(entryname):
+ 76"""
+ 77 Generate a string representing the Unix-style permissions for a given file or directory.
+ 78
+ 79 This function uses the os.lstat() method to retrieve the status of the specified file or directory,
+ 80 then constructs a string that represents the Unix-style permissions based on the mode of the file.
+ 81
+ 82 Args:
+ 83 entryname (str): The path to the file or directory for which permissions are being determined.
+ 84
+ 85 Returns:
+ 86 str: A string of length 10 representing the Unix-style permissions (e.g., '-rwxr-xr--').
+ 87 The first character is either 'd' (directory), '-' (not a directory), followed by
+ 88 three groups of 'r', 'w', 'x' (read, write, execute permissions) for owner, group,
+ 89 and others respectively.
+ 90 """
+ 91
+ 92mode=os.lstat(entryname).st_mode
+ 93permissions=[] 94
- 95permissions.append('r'ifmode&stat.S_IRUSRelse'-')
- 96permissions.append('w'ifmode&stat.S_IWUSRelse'-')
- 97permissions.append('x'ifmode&stat.S_IXUSRelse'-')
- 98
- 99permissions.append('r'ifmode&stat.S_IRGRPelse'-')
-100permissions.append('w'ifmode&stat.S_IWGRPelse'-')
-101permissions.append('x'ifmode&stat.S_IXGRPelse'-')
-102
-103permissions.append('r'ifmode&stat.S_IROTHelse'-')
-104permissions.append('w'ifmode&stat.S_IWOTHelse'-')
-105permissions.append('x'ifmode&stat.S_IXOTHelse'-')
-106
-107return''.join(permissions)
+ 95permissions.append('d'ifstat.S_ISDIR(mode)else'-')
+ 96
+ 97permissions.append('r'ifmode&stat.S_IRUSRelse'-')
+ 98permissions.append('w'ifmode&stat.S_IWUSRelse'-')
+ 99permissions.append('x'ifmode&stat.S_IXUSRelse'-')
+100
+101permissions.append('r'ifmode&stat.S_IRGRPelse'-')
+102permissions.append('w'ifmode&stat.S_IWGRPelse'-')
+103permissions.append('x'ifmode&stat.S_IXGRPelse'-')
+104
+105permissions.append('r'ifmode&stat.S_IROTHelse'-')
+106permissions.append('w'ifmode&stat.S_IWOTHelse'-')
+107permissions.append('x'ifmode&stat.S_IXOTHelse'-')108
-109
-110defSTYPE_MASK(stype_value):
-111"""
-112 Extracts the share type flags from a given share type value.
-113
-114 This function uses bitwise operations to determine which share type flags are set in the provided `stype_value`.
-115 It checks against known share type flags and returns a list of the flags that are set.
-116
-117 Parameters:
-118 stype_value (int): The share type value to analyze, typically obtained from SMB share properties.
-119
-120 Returns:
-121 list: A list of strings, where each string represents a share type flag that is set in the input value.
-122 """
-123
-124known_flags={
-125## One of the following values may be specified. You can isolate these values by using the STYPE_MASK value.
-126# Disk drive.
-127"STYPE_DISKTREE":0x0,
-128
-129# Print queue.
-130"STYPE_PRINTQ":0x1,
-131
-132# Communication device.
-133"STYPE_DEVICE":0x2,
-134
-135# Interprocess communication (IPC).
-136"STYPE_IPC":0x3,
-137
-138## In addition, one or both of the following values may be specified.
-139# Special share reserved for interprocess communication (IPC$) or remote administration of the server (ADMIN$).
-140# Can also refer to administrative shares such as C$, D$, E$, and so forth. For more information, see Network Share Functions.
-141"STYPE_SPECIAL":0x80000000,
-142
-143# A temporary share.
-144"STYPE_TEMPORARY":0x40000000
-145}
-146flags=[]
-147if(stype_value&0b11)==known_flags["STYPE_DISKTREE"]:
-148flags.append("STYPE_DISKTREE")
-149elif(stype_value&0b11)==known_flags["STYPE_PRINTQ"]:
-150flags.append("STYPE_PRINTQ")
-151elif(stype_value&0b11)==known_flags["STYPE_DEVICE"]:
-152flags.append("STYPE_DEVICE")
-153elif(stype_value&0b11)==known_flags["STYPE_IPC"]:
-154flags.append("STYPE_IPC")
-155if(stype_value&known_flags["STYPE_SPECIAL"])==known_flags["STYPE_SPECIAL"]:
-156flags.append("STYPE_SPECIAL")
-157if(stype_value&known_flags["STYPE_TEMPORARY"])==known_flags["STYPE_TEMPORARY"]:
-158flags.append("STYPE_TEMPORARY")
-159returnflags
-160
-161
-162defwindows_ls_entry(entry,config,pathToPrint=None):
-163"""
-164 This function generates a metadata string based on the attributes of the provided entry object.
-165
-166 Parameters:
-167 entry (object): An object representing a file or directory entry.
-168
-169 Returns:
-170 str: A string representing the metadata of the entry, including attributes like directory, archive, compressed, hidden, normal, readonly, system, and temporary.
-171 """
-172
-173ifpathToPrintisnotNone:
-174pathToPrint=pathToPrint+ntpath.sep+entry.get_longname()
-175else:
-176pathToPrint=entry.get_longname()
-177
-178meta_string=""
-179meta_string+=("d"ifentry.is_directory()else"-")
-180meta_string+=("a"ifentry.is_archive()else"-")
-181meta_string+=("c"ifentry.is_compressed()else"-")
-182meta_string+=("h"ifentry.is_hidden()else"-")
-183meta_string+=("n"ifentry.is_normal()else"-")
-184meta_string+=("r"ifentry.is_readonly()else"-")
-185meta_string+=("s"ifentry.is_system()else"-")
-186meta_string+=("t"ifentry.is_temporary()else"-")
-187
-188size_str=b_filesize(entry.get_filesize())
+109return''.join(permissions)
+110
+111
+112defSTYPE_MASK(stype_value):
+113"""
+114 Extracts the share type flags from a given share type value.
+115
+116 This function uses bitwise operations to determine which share type flags are set in the provided `stype_value`.
+117 It checks against known share type flags and returns a list of the flags that are set.
+118
+119 Parameters:
+120 stype_value (int): The share type value to analyze, typically obtained from SMB share properties.
+121
+122 Returns:
+123 list: A list of strings, where each string represents a share type flag that is set in the input value.
+124 """
+125
+126known_flags={
+127## One of the following values may be specified. You can isolate these values by using the STYPE_MASK value.
+128# Disk drive.
+129"STYPE_DISKTREE":0x0,
+130
+131# Print queue.
+132"STYPE_PRINTQ":0x1,
+133
+134# Communication device.
+135"STYPE_DEVICE":0x2,
+136
+137# Interprocess communication (IPC).
+138"STYPE_IPC":0x3,
+139
+140## In addition, one or both of the following values may be specified.
+141# Special share reserved for interprocess communication (IPC$) or remote administration of the server (ADMIN$).
+142# Can also refer to administrative shares such as C$, D$, E$, and so forth. For more information, see Network Share Functions.
+143"STYPE_SPECIAL":0x80000000,
+144
+145# A temporary share.
+146"STYPE_TEMPORARY":0x40000000
+147}
+148flags=[]
+149if(stype_value&0b11)==known_flags["STYPE_DISKTREE"]:
+150flags.append("STYPE_DISKTREE")
+151elif(stype_value&0b11)==known_flags["STYPE_PRINTQ"]:
+152flags.append("STYPE_PRINTQ")
+153elif(stype_value&0b11)==known_flags["STYPE_DEVICE"]:
+154flags.append("STYPE_DEVICE")
+155elif(stype_value&0b11)==known_flags["STYPE_IPC"]:
+156flags.append("STYPE_IPC")
+157if(stype_value&known_flags["STYPE_SPECIAL"])==known_flags["STYPE_SPECIAL"]:
+158flags.append("STYPE_SPECIAL")
+159if(stype_value&known_flags["STYPE_TEMPORARY"])==known_flags["STYPE_TEMPORARY"]:
+160flags.append("STYPE_TEMPORARY")
+161returnflags
+162
+163
+164defwindows_ls_entry(entry,config,pathToPrint=None):
+165"""
+166 This function generates a metadata string based on the attributes of the provided entry object.
+167
+168 Parameters:
+169 entry (object): An object representing a file or directory entry.
+170
+171 Returns:
+172 str: A string representing the metadata of the entry, including attributes like directory, archive, compressed, hidden, normal, readonly, system, and temporary.
+173 """
+174
+175ifpathToPrintisnotNone:
+176pathToPrint=pathToPrint+ntpath.sep+entry.get_longname()
+177else:
+178pathToPrint=entry.get_longname()
+179
+180meta_string=""
+181meta_string+=("d"ifentry.is_directory()else"-")
+182meta_string+=("a"ifentry.is_archive()else"-")
+183meta_string+=("c"ifentry.is_compressed()else"-")
+184meta_string+=("h"ifentry.is_hidden()else"-")
+185meta_string+=("n"ifentry.is_normal()else"-")
+186meta_string+=("r"ifentry.is_readonly()else"-")
+187meta_string+=("s"ifentry.is_system()else"-")
+188meta_string+=("t"ifentry.is_temporary()else"-")189
-190date_str=datetime.datetime.fromtimestamp(entry.get_atime_epoch()).strftime("%Y-%m-%d %H:%M")
-191
-192ifentry.is_directory():
-193ifconfig.no_colors:
-194print("%s%10s%s%s\\"%(meta_string,size_str,date_str,pathToPrint))
-195else:
-196print("%s%10s%s\x1b[1;96m%s\x1b[0m\\"%(meta_string,size_str,date_str,pathToPrint))
-197else:
-198ifconfig.no_colors:
-199print("%s%10s%s%s"%(meta_string,size_str,date_str,pathToPrint))
-200else:
-201print("%s%10s%s\x1b[1m%s\x1b[0m"%(meta_string,size_str,date_str,pathToPrint))
-202
-203
-204deflocal_tree(path,config):
-205"""
-206 This function recursively lists the contents of a directory in a tree-like format.
-207
-208 Parameters:
-209 path (str): The path to the directory to list.
-210 config (object): Configuration settings which may affect the output, such as whether to use colors.
-211
-212 Returns:
-213 None: This function does not return anything but prints the directory tree to the console.
-214 """
-215
-216defrecurse_action(base_dir="",path=[],prompt=[]):
-217bars=["│ ","├── ","└── "]
-218
-219local_path=os.path.normpath(base_dir+os.path.sep+os.path.sep.join(path)+os.path.sep)
+190size_str=b_filesize(entry.get_filesize())
+191
+192date_str=datetime.datetime.fromtimestamp(entry.get_atime_epoch()).strftime("%Y-%m-%d %H:%M")
+193
+194ifentry.is_directory():
+195ifconfig.no_colors:
+196print("%s%10s%s%s\\"%(meta_string,size_str,date_str,pathToPrint))
+197else:
+198print("%s%10s%s\x1b[1;96m%s\x1b[0m\\"%(meta_string,size_str,date_str,pathToPrint))
+199else:
+200ifconfig.no_colors:
+201print("%s%10s%s%s"%(meta_string,size_str,date_str,pathToPrint))
+202else:
+203print("%s%10s%s\x1b[1m%s\x1b[0m"%(meta_string,size_str,date_str,pathToPrint))
+204
+205
+206deflocal_tree(path,config):
+207"""
+208 This function recursively lists the contents of a directory in a tree-like format.
+209
+210 Parameters:
+211 path (str): The path to the directory to list.
+212 config (object): Configuration settings which may affect the output, such as whether to use colors.
+213
+214 Returns:
+215 None: This function does not return anything but prints the directory tree to the console.
+216 """
+217
+218defrecurse_action(base_dir="",path=[],prompt=[]):
+219bars=["│ ","├── ","└── "]220
-221entries=[]
-222try:
-223entries=os.listdir(local_path)
-224exceptExceptionaserr:
-225ifconfig.no_colors:
-226print("%s%s"%(''.join(prompt+[bars[2]]),err))
-227else:
-228print("%s\x1b[1;91m%s\x1b[0m"%(''.join(prompt+[bars[2]]),err))
-229return
-230
-231entries=sorted(entries)
+221local_path=os.path.normpath(base_dir+os.path.sep+os.path.sep.join(path)+os.path.sep)
+222
+223entries=[]
+224try:
+225entries=os.listdir(local_path)
+226exceptExceptionaserr:
+227ifconfig.no_colors:
+228print("%s%s"%(''.join(prompt+[bars[2]]),err))
+229else:
+230print("%s\x1b[1;91m%s\x1b[0m"%(''.join(prompt+[bars[2]]),err))
+231return232
-233#
-234iflen(entries)>1:
-235index=0
-236forentryinentries:
-237index+=1
-238# This is the first entry
-239ifindex==0:
-240ifos.path.isdir(local_path+os.path.sep+entry):
-241ifconfig.no_colors:
-242print("%s%s%s"%(''.join(prompt+[bars[1]]),entry,os.path.sep))
-243else:
-244print("%s\x1b[1;96m%s\x1b[0m%s"%(''.join(prompt+[bars[1]]),entry,os.path.sep))
-245recurse_action(
-246base_dir=base_dir,
-247path=path+[entry],
-248prompt=prompt+["│ "]
-249)
-250else:
-251ifconfig.no_colors:
-252print("%s%s"%(''.join(prompt+[bars[1]]),entry))
-253else:
-254print("%s\x1b[1m%s\x1b[0m"%(''.join(prompt+[bars[1]]),entry))
-255
-256# This is the last entry
-257elifindex==len(entries):
-258ifos.path.isdir(local_path+os.path.sep+entry):
-259ifconfig.no_colors:
-260print("%s%s%s"%(''.join(prompt+[bars[2]]),entry,os.path.sep))
-261else:
-262print("%s\x1b[1;96m%s\x1b[0m%s"%(''.join(prompt+[bars[2]]),entry,os.path.sep))
-263recurse_action(
-264base_dir=base_dir,
-265path=path+[entry],
-266prompt=prompt+[" "]
-267)
-268else:
-269ifconfig.no_colors:
-270print("%s%s"%(''.join(prompt+[bars[2]]),entry))
-271else:
-272print("%s\x1b[1m%s\x1b[0m"%(''.join(prompt+[bars[2]]),entry))
-273
-274# These are entries in the middle
-275else:
-276ifos.path.isdir(local_path+os.path.sep+entry):
-277ifconfig.no_colors:
-278print("%s%s%s"%(''.join(prompt+[bars[1]]),entry,os.path.sep))
-279else:
-280print("%s\x1b[1;96m%s\x1b[0m%s"%(''.join(prompt+[bars[1]]),entry,os.path.sep))
-281recurse_action(
-282base_dir=base_dir,
-283path=path+[entry],
-284prompt=prompt+["│ "]
-285)
-286else:
-287ifconfig.no_colors:
-288print("%s%s"%(''.join(prompt+[bars[1]]),entry))
-289else:
-290print("%s\x1b[1m%s\x1b[0m"%(''.join(prompt+[bars[1]]),entry))
-291
-292#
-293eliflen(entries)==1:
-294entry=entries[0]
-295ifos.path.isdir(local_path+os.path.sep+entry):
-296ifconfig.no_colors:
-297print("%s%s%s"%(''.join(prompt+[bars[2]]),entry,os.path.sep))
-298else:
-299print("%s\x1b[1;96m%s\x1b[0m%s"%(''.join(prompt+[bars[2]]),entry,os.path.sep))
-300recurse_action(
-301base_dir=base_dir,
-302path=path+[entry],
-303prompt=prompt+[" "]
-304)
-305else:
-306ifconfig.no_colors:
-307print("%s%s"%(''.join(prompt+[bars[2]]),entry))
-308else:
-309print("%s\x1b[1m%s\x1b[0m"%(''.join(prompt+[bars[2]]),entry))
-310
-311# Entrypoint
-312try:
-313ifconfig.no_colors:
-314print("%s%s"%(path,os.path.sep))
-315else:
-316print("\x1b[1;96m%s\x1b[0m%s"%(path,os.path.sep))
-317recurse_action(
-318base_dir=os.getcwd(),
-319path=[path],
-320prompt=[""]
-321)
-322except(BrokenPipeError,KeyboardInterrupt)ase:
-323print("[!] Interrupted.")
+233entries=sorted(entries)
+234
+235#
+236iflen(entries)>1:
+237index=0
+238forentryinentries:
+239index+=1
+240# This is the first entry
+241ifindex==0:
+242ifos.path.isdir(local_path+os.path.sep+entry):
+243ifconfig.no_colors:
+244print("%s%s%s"%(''.join(prompt+[bars[1]]),entry,os.path.sep))
+245else:
+246print("%s\x1b[1;96m%s\x1b[0m%s"%(''.join(prompt+[bars[1]]),entry,os.path.sep))
+247recurse_action(
+248base_dir=base_dir,
+249path=path+[entry],
+250prompt=prompt+["│ "]
+251)
+252else:
+253ifconfig.no_colors:
+254print("%s%s"%(''.join(prompt+[bars[1]]),entry))
+255else:
+256print("%s\x1b[1m%s\x1b[0m"%(''.join(prompt+[bars[1]]),entry))
+257
+258# This is the last entry
+259elifindex==len(entries):
+260ifos.path.isdir(local_path+os.path.sep+entry):
+261ifconfig.no_colors:
+262print("%s%s%s"%(''.join(prompt+[bars[2]]),entry,os.path.sep))
+263else:
+264print("%s\x1b[1;96m%s\x1b[0m%s"%(''.join(prompt+[bars[2]]),entry,os.path.sep))
+265recurse_action(
+266base_dir=base_dir,
+267path=path+[entry],
+268prompt=prompt+[" "]
+269)
+270else:
+271ifconfig.no_colors:
+272print("%s%s"%(''.join(prompt+[bars[2]]),entry))
+273else:
+274print("%s\x1b[1m%s\x1b[0m"%(''.join(prompt+[bars[2]]),entry))
+275
+276# These are entries in the middle
+277else:
+278ifos.path.isdir(local_path+os.path.sep+entry):
+279ifconfig.no_colors:
+280print("%s%s%s"%(''.join(prompt+[bars[1]]),entry,os.path.sep))
+281else:
+282print("%s\x1b[1;96m%s\x1b[0m%s"%(''.join(prompt+[bars[1]]),entry,os.path.sep))
+283recurse_action(
+284base_dir=base_dir,
+285path=path+[entry],
+286prompt=prompt+["│ "]
+287)
+288else:
+289ifconfig.no_colors:
+290print("%s%s"%(''.join(prompt+[bars[1]]),entry))
+291else:
+292print("%s\x1b[1m%s\x1b[0m"%(''.join(prompt+[bars[1]]),entry))
+293
+294#
+295eliflen(entries)==1:
+296entry=entries[0]
+297ifos.path.isdir(local_path+os.path.sep+entry):
+298ifconfig.no_colors:
+299print("%s%s%s"%(''.join(prompt+[bars[2]]),entry,os.path.sep))
+300else:
+301print("%s\x1b[1;96m%s\x1b[0m%s"%(''.join(prompt+[bars[2]]),entry,os.path.sep))
+302recurse_action(
+303base_dir=base_dir,
+304path=path+[entry],
+305prompt=prompt+[" "]
+306)
+307else:
+308ifconfig.no_colors:
+309print("%s%s"%(''.join(prompt+[bars[2]]),entry))
+310else:
+311print("%s\x1b[1m%s\x1b[0m"%(''.join(prompt+[bars[2]]),entry))
+312
+313# Entrypoint
+314try:
+315ifconfig.no_colors:
+316print("%s%s"%(path,os.path.sep))
+317else:
+318print("\x1b[1;96m%s\x1b[0m%s"%(path,os.path.sep))
+319recurse_action(
+320base_dir=os.getcwd(),
+321path=[path],
+322prompt=[""]
+323)
+324except(BrokenPipeError,KeyboardInterrupt)ase:
+325print("[!] Interrupted.")
+326
+327
+328defresolve_local_files(arguments):
+329"""
+330 Resolves local file paths based on the provided arguments.
+331
+332 This function takes a list of arguments, which can include wildcard patterns, and resolves them to actual file paths.
+333 If an argument contains a wildcard ('*'), it attempts to match files in the specified directory against the pattern.
+334 If the argument does not contain a wildcard, it is added to the list of resolved files as is.
+335
+336 Args:
+337 arguments (list): A list of file path arguments, which may include wildcard patterns.
+338
+339 Returns:
+340 list: A list of resolved file paths that match the provided arguments.
+341 """
+342
+343resolved_files=[]
+344forarginarguments:
+345if'*'inarg:
+346try:
+347path=os.path.dirname(arg)or'.'
+348pattern=os.path.basename(arg)
+349forentryinos.listdir(path):
+350iffnmatch.fnmatch(entry,pattern):
+351resolved_files.append(os.path.join(path,entry))
+352exceptFileNotFoundErroraserr:
+353pass
+354else:
+355resolved_files.append(arg)
+356resolved_files=sorted(list(set(resolved_files)))
+357returnresolved_files
+358
+359
+360defresolve_remote_files(smbSession,arguments):
+361"""
+362 Resolves remote file paths based on the provided arguments using an SMB session.
+363
+364 This function takes a list of arguments, which can include wildcard patterns, and resolves them to actual remote file paths.
+365 If an argument contains a wildcard ('*'), it attempts to match files in the specified remote directory against the pattern.
+366 If the argument does not contain a wildcard, it is added to the list of resolved files as is.
+367
+368 Args:
+369 smbsession (SMBSession): The SMB session through which to access the files.
+370 arguments (list): A list of file path arguments, which may include wildcard patterns.
+371
+372 Returns:
+373 list: A list of resolved remote file paths that match the provided arguments.
+374 """
+375
+376resolved_files=[]
+377forarginarguments:
+378if'*'inarg:
+379ifarg=='*':
+380path=smbSession.smb_cwd
+381elifarg.startswith(ntpath.sep):
+382path=ntpath.dirname(arg)
+383else:
+384path=ntpath.normpath(smbSession.smb_cwd+ntpath.sep+ntpath.dirname(arg))
+385
+386try:
+387contents=smbSession.smbClient.listPath(
+388shareName=smbSession.smb_share,
+389path=path+ntpath.sep+'*'
+390)
+391contents=[eforeincontentsife.get_longname()notin['.','..']]
+392
+393forentryincontents:
+394iffnmatch.fnmatch(entry.get_longname(),ntpath.basename(arg)):
+395resolved_files.append(ntpath.join(path,entry.get_longname()))
+396
+397exceptExceptionaserr:
+398pass
+399else:
+400resolved_files.append(arg)
+401resolved_files=sorted(list(set(resolved_files)))
+402returnresolved_files
+403
+404
+405defis_port_open(target,port)->bool:
+406"""
+407 Check if a specific port on a target host is open.
+408
+409 This function attempts to establish a TCP connection to the specified port on the target host.
+410 If the connection is successful, it indicates that the port is open. If the connection fails,
+411 it indicates that the port is closed or the host is unreachable.
+412
+413 Args:
+414 target (str): The hostname or IP address of the target host.
+415 port (int): The port number to check.
+416
+417 Returns:
+418 bool: True if the port is open, False otherwise.
+419 """
+420
+421withsocket.socket(socket.AF_INET,socket.SOCK_STREAM)ass:
+422s.settimeout(0.1)
+423# Non-existant domains cause a lot of errors, added error handling
+424try:
+425returns.connect_ex((target,port))==0
+426exceptExceptionase:
+427returnFalse
@@ -407,40 +520,40 @@
-
16defparse_lm_nt_hashes(lm_nt_hashes_string):
-17"""
-18 Parse the input string containing LM and NT hash values and return them separately.
-19
-20 This function takes a string containing LM and NT hash values, typically separated by a colon (:).
-21 It returns the LM and NT hash values as separate strings. If only one hash value is provided, it is
-22 assumed to be the NT hash and the LM hash is set to its default value. If no valid hash values are
-23 found, both return values are empty strings.
-24
-25 Args:
-26 lm_nt_hashes_string (str): A string containing LM and NT hash values separated by a colon.
-27
-28 Returns:
-29 tuple: A tuple containing two strings (lm_hash_value, nt_hash_value).
-30 - lm_hash_value: The LM hash value or its default if not provided.
-31 - nt_hash_value: The NT hash value or its default if not provided.
-32
-33 Extracted from p0dalirius/sectools library
-34 Src: https://github.com/p0dalirius/sectools/blob/7bb3f5cb7815ad4d4845713c8739e2e2b0ea4e75/sectools/windows/crypto.py#L11-L24
-35 """
-36
-37lm_hash_value,nt_hash_value="",""
-38iflm_nt_hashes_stringisnotNone:
-39matched=re.match("([0-9a-f]{32})?(:)?([0-9a-f]{32})?",lm_nt_hashes_string.strip().lower())
-40m_lm_hash,m_sep,m_nt_hash=matched.groups()
-41ifm_lm_hashisNoneandm_sepisNoneandm_nt_hashisNone:
-42lm_hash_value,nt_hash_value="",""
-43elifm_lm_hashisNoneandm_nt_hashisnotNone:
-44lm_hash_value="aad3b435b51404eeaad3b435b51404ee"
-45nt_hash_value=m_nt_hash
-46elifm_lm_hashisnotNoneandm_nt_hashisNone:
-47lm_hash_value=m_lm_hash
-48nt_hash_value="31d6cfe0d16ae931b73c59d7e0c089c0"
-49returnlm_hash_value,nt_hash_value
+
18defparse_lm_nt_hashes(lm_nt_hashes_string):
+19"""
+20 Parse the input string containing LM and NT hash values and return them separately.
+21
+22 This function takes a string containing LM and NT hash values, typically separated by a colon (:).
+23 It returns the LM and NT hash values as separate strings. If only one hash value is provided, it is
+24 assumed to be the NT hash and the LM hash is set to its default value. If no valid hash values are
+25 found, both return values are empty strings.
+26
+27 Args:
+28 lm_nt_hashes_string (str): A string containing LM and NT hash values separated by a colon.
+29
+30 Returns:
+31 tuple: A tuple containing two strings (lm_hash_value, nt_hash_value).
+32 - lm_hash_value: The LM hash value or its default if not provided.
+33 - nt_hash_value: The NT hash value or its default if not provided.
+34
+35 Extracted from p0dalirius/sectools library
+36 Src: https://github.com/p0dalirius/sectools/blob/7bb3f5cb7815ad4d4845713c8739e2e2b0ea4e75/sectools/windows/crypto.py#L11-L24
+37 """
+38
+39lm_hash_value,nt_hash_value="",""
+40iflm_nt_hashes_stringisnotNone:
+41matched=re.match("([0-9a-f]{32})?(:)?([0-9a-f]{32})?",lm_nt_hashes_string.strip().lower())
+42m_lm_hash,m_sep,m_nt_hash=matched.groups()
+43ifm_lm_hashisNoneandm_sepisNoneandm_nt_hashisNone:
+44lm_hash_value,nt_hash_value="",""
+45elifm_lm_hashisNoneandm_nt_hashisnotNone:
+46lm_hash_value="aad3b435b51404eeaad3b435b51404ee"
+47nt_hash_value=m_nt_hash
+48elifm_lm_hashisnotNoneandm_nt_hashisNone:
+49lm_hash_value=m_lm_hash
+50nt_hash_value="31d6cfe0d16ae931b73c59d7e0c089c0"
+51returnlm_hash_value,nt_hash_value
@@ -476,26 +589,26 @@
-
52defb_filesize(l):
-53"""
-54 Convert a file size from bytes to a more readable format using the largest appropriate unit.
-55
-56 This function takes an integer representing a file size in bytes and converts it to a human-readable
-57 string using the largest appropriate unit from bytes (B) to petabytes (PB). The result is rounded to
-58 two decimal places.
-59
-60 Args:
-61 l (int): The file size in bytes.
-62
-63 Returns:
-64 str: A string representing the file size in a more readable format, including the appropriate unit.
-65 """
-66
-67units=['B','kB','MB','GB','TB','PB']
-68forkinrange(len(units)):
-69ifl<(1024**(k+1)):
-70break
-71return"%4.2f%s"%(round(l/(1024**(k)),2),units[k])
+
54defb_filesize(l):
+55"""
+56 Convert a file size from bytes to a more readable format using the largest appropriate unit.
+57
+58 This function takes an integer representing a file size in bytes and converts it to a human-readable
+59 string using the largest appropriate unit from bytes (B) to petabytes (PB). The result is rounded to
+60 two decimal places.
+61
+62 Args:
+63 l (int): The file size in bytes.
+64
+65 Returns:
+66 str: A string representing the file size in a more readable format, including the appropriate unit.
+67 """
+68
+69units=['B','kB','MB','GB','TB','PB']
+70forkinrange(len(units)):
+71ifl<(1024**(k+1)):
+72break
+73return"%4.2f%s"%(round(l/(1024**(k)),2),units[k])
@@ -525,41 +638,41 @@
-
74defunix_permissions(entryname):
- 75"""
- 76 Generate a string representing the Unix-style permissions for a given file or directory.
- 77
- 78 This function uses the os.lstat() method to retrieve the status of the specified file or directory,
- 79 then constructs a string that represents the Unix-style permissions based on the mode of the file.
- 80
- 81 Args:
- 82 entryname (str): The path to the file or directory for which permissions are being determined.
- 83
- 84 Returns:
- 85 str: A string of length 10 representing the Unix-style permissions (e.g., '-rwxr-xr--').
- 86 The first character is either 'd' (directory), '-' (not a directory), followed by
- 87 three groups of 'r', 'w', 'x' (read, write, execute permissions) for owner, group,
- 88 and others respectively.
- 89 """
- 90
- 91mode=os.lstat(entryname).st_mode
- 92permissions=[]
- 93
- 94permissions.append('d'ifstat.S_ISDIR(mode)else'-')
+
76defunix_permissions(entryname):
+ 77"""
+ 78 Generate a string representing the Unix-style permissions for a given file or directory.
+ 79
+ 80 This function uses the os.lstat() method to retrieve the status of the specified file or directory,
+ 81 then constructs a string that represents the Unix-style permissions based on the mode of the file.
+ 82
+ 83 Args:
+ 84 entryname (str): The path to the file or directory for which permissions are being determined.
+ 85
+ 86 Returns:
+ 87 str: A string of length 10 representing the Unix-style permissions (e.g., '-rwxr-xr--').
+ 88 The first character is either 'd' (directory), '-' (not a directory), followed by
+ 89 three groups of 'r', 'w', 'x' (read, write, execute permissions) for owner, group,
+ 90 and others respectively.
+ 91 """
+ 92
+ 93mode=os.lstat(entryname).st_mode
+ 94permissions=[] 95
- 96permissions.append('r'ifmode&stat.S_IRUSRelse'-')
- 97permissions.append('w'ifmode&stat.S_IWUSRelse'-')
- 98permissions.append('x'ifmode&stat.S_IXUSRelse'-')
- 99
-100permissions.append('r'ifmode&stat.S_IRGRPelse'-')
-101permissions.append('w'ifmode&stat.S_IWGRPelse'-')
-102permissions.append('x'ifmode&stat.S_IXGRPelse'-')
-103
-104permissions.append('r'ifmode&stat.S_IROTHelse'-')
-105permissions.append('w'ifmode&stat.S_IWOTHelse'-')
-106permissions.append('x'ifmode&stat.S_IXOTHelse'-')
-107
-108return''.join(permissions)
+ 96permissions.append('d'ifstat.S_ISDIR(mode)else'-')
+ 97
+ 98permissions.append('r'ifmode&stat.S_IRUSRelse'-')
+ 99permissions.append('w'ifmode&stat.S_IWUSRelse'-')
+100permissions.append('x'ifmode&stat.S_IXUSRelse'-')
+101
+102permissions.append('r'ifmode&stat.S_IRGRPelse'-')
+103permissions.append('w'ifmode&stat.S_IWGRPelse'-')
+104permissions.append('x'ifmode&stat.S_IXGRPelse'-')
+105
+106permissions.append('r'ifmode&stat.S_IROTHelse'-')
+107permissions.append('w'ifmode&stat.S_IWOTHelse'-')
+108permissions.append('x'ifmode&stat.S_IXOTHelse'-')
+109
+110return''.join(permissions)
@@ -591,56 +704,56 @@
-
111defSTYPE_MASK(stype_value):
-112"""
-113 Extracts the share type flags from a given share type value.
-114
-115 This function uses bitwise operations to determine which share type flags are set in the provided `stype_value`.
-116 It checks against known share type flags and returns a list of the flags that are set.
-117
-118 Parameters:
-119 stype_value (int): The share type value to analyze, typically obtained from SMB share properties.
-120
-121 Returns:
-122 list: A list of strings, where each string represents a share type flag that is set in the input value.
-123 """
-124
-125known_flags={
-126## One of the following values may be specified. You can isolate these values by using the STYPE_MASK value.
-127# Disk drive.
-128"STYPE_DISKTREE":0x0,
-129
-130# Print queue.
-131"STYPE_PRINTQ":0x1,
-132
-133# Communication device.
-134"STYPE_DEVICE":0x2,
-135
-136# Interprocess communication (IPC).
-137"STYPE_IPC":0x3,
-138
-139## In addition, one or both of the following values may be specified.
-140# Special share reserved for interprocess communication (IPC$) or remote administration of the server (ADMIN$).
-141# Can also refer to administrative shares such as C$, D$, E$, and so forth. For more information, see Network Share Functions.
-142"STYPE_SPECIAL":0x80000000,
-143
-144# A temporary share.
-145"STYPE_TEMPORARY":0x40000000
-146}
-147flags=[]
-148if(stype_value&0b11)==known_flags["STYPE_DISKTREE"]:
-149flags.append("STYPE_DISKTREE")
-150elif(stype_value&0b11)==known_flags["STYPE_PRINTQ"]:
-151flags.append("STYPE_PRINTQ")
-152elif(stype_value&0b11)==known_flags["STYPE_DEVICE"]:
-153flags.append("STYPE_DEVICE")
-154elif(stype_value&0b11)==known_flags["STYPE_IPC"]:
-155flags.append("STYPE_IPC")
-156if(stype_value&known_flags["STYPE_SPECIAL"])==known_flags["STYPE_SPECIAL"]:
-157flags.append("STYPE_SPECIAL")
-158if(stype_value&known_flags["STYPE_TEMPORARY"])==known_flags["STYPE_TEMPORARY"]:
-159flags.append("STYPE_TEMPORARY")
-160returnflags
+
113defSTYPE_MASK(stype_value):
+114"""
+115 Extracts the share type flags from a given share type value.
+116
+117 This function uses bitwise operations to determine which share type flags are set in the provided `stype_value`.
+118 It checks against known share type flags and returns a list of the flags that are set.
+119
+120 Parameters:
+121 stype_value (int): The share type value to analyze, typically obtained from SMB share properties.
+122
+123 Returns:
+124 list: A list of strings, where each string represents a share type flag that is set in the input value.
+125 """
+126
+127known_flags={
+128## One of the following values may be specified. You can isolate these values by using the STYPE_MASK value.
+129# Disk drive.
+130"STYPE_DISKTREE":0x0,
+131
+132# Print queue.
+133"STYPE_PRINTQ":0x1,
+134
+135# Communication device.
+136"STYPE_DEVICE":0x2,
+137
+138# Interprocess communication (IPC).
+139"STYPE_IPC":0x3,
+140
+141## In addition, one or both of the following values may be specified.
+142# Special share reserved for interprocess communication (IPC$) or remote administration of the server (ADMIN$).
+143# Can also refer to administrative shares such as C$, D$, E$, and so forth. For more information, see Network Share Functions.
+144"STYPE_SPECIAL":0x80000000,
+145
+146# A temporary share.
+147"STYPE_TEMPORARY":0x40000000
+148}
+149flags=[]
+150if(stype_value&0b11)==known_flags["STYPE_DISKTREE"]:
+151flags.append("STYPE_DISKTREE")
+152elif(stype_value&0b11)==known_flags["STYPE_PRINTQ"]:
+153flags.append("STYPE_PRINTQ")
+154elif(stype_value&0b11)==known_flags["STYPE_DEVICE"]:
+155flags.append("STYPE_DEVICE")
+156elif(stype_value&0b11)==known_flags["STYPE_IPC"]:
+157flags.append("STYPE_IPC")
+158if(stype_value&known_flags["STYPE_SPECIAL"])==known_flags["STYPE_SPECIAL"]:
+159flags.append("STYPE_SPECIAL")
+160if(stype_value&known_flags["STYPE_TEMPORARY"])==known_flags["STYPE_TEMPORARY"]:
+161flags.append("STYPE_TEMPORARY")
+162returnflags
@@ -669,46 +782,46 @@
-
163defwindows_ls_entry(entry,config,pathToPrint=None):
-164"""
-165 This function generates a metadata string based on the attributes of the provided entry object.
-166
-167 Parameters:
-168 entry (object): An object representing a file or directory entry.
-169
-170 Returns:
-171 str: A string representing the metadata of the entry, including attributes like directory, archive, compressed, hidden, normal, readonly, system, and temporary.
-172 """
-173
-174ifpathToPrintisnotNone:
-175pathToPrint=pathToPrint+ntpath.sep+entry.get_longname()
-176else:
-177pathToPrint=entry.get_longname()
-178
-179meta_string=""
-180meta_string+=("d"ifentry.is_directory()else"-")
-181meta_string+=("a"ifentry.is_archive()else"-")
-182meta_string+=("c"ifentry.is_compressed()else"-")
-183meta_string+=("h"ifentry.is_hidden()else"-")
-184meta_string+=("n"ifentry.is_normal()else"-")
-185meta_string+=("r"ifentry.is_readonly()else"-")
-186meta_string+=("s"ifentry.is_system()else"-")
-187meta_string+=("t"ifentry.is_temporary()else"-")
-188
-189size_str=b_filesize(entry.get_filesize())
+
165defwindows_ls_entry(entry,config,pathToPrint=None):
+166"""
+167 This function generates a metadata string based on the attributes of the provided entry object.
+168
+169 Parameters:
+170 entry (object): An object representing a file or directory entry.
+171
+172 Returns:
+173 str: A string representing the metadata of the entry, including attributes like directory, archive, compressed, hidden, normal, readonly, system, and temporary.
+174 """
+175
+176ifpathToPrintisnotNone:
+177pathToPrint=pathToPrint+ntpath.sep+entry.get_longname()
+178else:
+179pathToPrint=entry.get_longname()
+180
+181meta_string=""
+182meta_string+=("d"ifentry.is_directory()else"-")
+183meta_string+=("a"ifentry.is_archive()else"-")
+184meta_string+=("c"ifentry.is_compressed()else"-")
+185meta_string+=("h"ifentry.is_hidden()else"-")
+186meta_string+=("n"ifentry.is_normal()else"-")
+187meta_string+=("r"ifentry.is_readonly()else"-")
+188meta_string+=("s"ifentry.is_system()else"-")
+189meta_string+=("t"ifentry.is_temporary()else"-")190
-191date_str=datetime.datetime.fromtimestamp(entry.get_atime_epoch()).strftime("%Y-%m-%d %H:%M")
-192
-193ifentry.is_directory():
-194ifconfig.no_colors:
-195print("%s%10s%s%s\\"%(meta_string,size_str,date_str,pathToPrint))
-196else:
-197print("%s%10s%s\x1b[1;96m%s\x1b[0m\\"%(meta_string,size_str,date_str,pathToPrint))
-198else:
-199ifconfig.no_colors:
-200print("%s%10s%s%s"%(meta_string,size_str,date_str,pathToPrint))
-201else:
-202print("%s%10s%s\x1b[1m%s\x1b[0m"%(meta_string,size_str,date_str,pathToPrint))
+191size_str=b_filesize(entry.get_filesize())
+192
+193date_str=datetime.datetime.fromtimestamp(entry.get_atime_epoch()).strftime("%Y-%m-%d %H:%M")
+194
+195ifentry.is_directory():
+196ifconfig.no_colors:
+197print("%s%10s%s%s\\"%(meta_string,size_str,date_str,pathToPrint))
+198else:
+199print("%s%10s%s\x1b[1;96m%s\x1b[0m\\"%(meta_string,size_str,date_str,pathToPrint))
+200else:
+201ifconfig.no_colors:
+202print("%s%10s%s%s"%(meta_string,size_str,date_str,pathToPrint))
+203else:
+204print("%s%10s%s\x1b[1m%s\x1b[0m"%(meta_string,size_str,date_str,pathToPrint))
@@ -734,126 +847,126 @@
-
205deflocal_tree(path,config):
-206"""
-207 This function recursively lists the contents of a directory in a tree-like format.
-208
-209 Parameters:
-210 path (str): The path to the directory to list.
-211 config (object): Configuration settings which may affect the output, such as whether to use colors.
-212
-213 Returns:
-214 None: This function does not return anything but prints the directory tree to the console.
-215 """
-216
-217defrecurse_action(base_dir="",path=[],prompt=[]):
-218bars=["│ ","├── ","└── "]
-219
-220local_path=os.path.normpath(base_dir+os.path.sep+os.path.sep.join(path)+os.path.sep)
+
207deflocal_tree(path,config):
+208"""
+209 This function recursively lists the contents of a directory in a tree-like format.
+210
+211 Parameters:
+212 path (str): The path to the directory to list.
+213 config (object): Configuration settings which may affect the output, such as whether to use colors.
+214
+215 Returns:
+216 None: This function does not return anything but prints the directory tree to the console.
+217 """
+218
+219defrecurse_action(base_dir="",path=[],prompt=[]):
+220bars=["│ ","├── ","└── "]221
-222entries=[]
-223try:
-224entries=os.listdir(local_path)
-225exceptExceptionaserr:
-226ifconfig.no_colors:
-227print("%s%s"%(''.join(prompt+[bars[2]]),err))
-228else:
-229print("%s\x1b[1;91m%s\x1b[0m"%(''.join(prompt+[bars[2]]),err))
-230return
-231
-232entries=sorted(entries)
+222local_path=os.path.normpath(base_dir+os.path.sep+os.path.sep.join(path)+os.path.sep)
+223
+224entries=[]
+225try:
+226entries=os.listdir(local_path)
+227exceptExceptionaserr:
+228ifconfig.no_colors:
+229print("%s%s"%(''.join(prompt+[bars[2]]),err))
+230else:
+231print("%s\x1b[1;91m%s\x1b[0m"%(''.join(prompt+[bars[2]]),err))
+232return233
-234#
-235iflen(entries)>1:
-236index=0
-237forentryinentries:
-238index+=1
-239# This is the first entry
-240ifindex==0:
-241ifos.path.isdir(local_path+os.path.sep+entry):
-242ifconfig.no_colors:
-243print("%s%s%s"%(''.join(prompt+[bars[1]]),entry,os.path.sep))
-244else:
-245print("%s\x1b[1;96m%s\x1b[0m%s"%(''.join(prompt+[bars[1]]),entry,os.path.sep))
-246recurse_action(
-247base_dir=base_dir,
-248path=path+[entry],
-249prompt=prompt+["│ "]
-250)
-251else:
-252ifconfig.no_colors:
-253print("%s%s"%(''.join(prompt+[bars[1]]),entry))
-254else:
-255print("%s\x1b[1m%s\x1b[0m"%(''.join(prompt+[bars[1]]),entry))
-256
-257# This is the last entry
-258elifindex==len(entries):
-259ifos.path.isdir(local_path+os.path.sep+entry):
-260ifconfig.no_colors:
-261print("%s%s%s"%(''.join(prompt+[bars[2]]),entry,os.path.sep))
-262else:
-263print("%s\x1b[1;96m%s\x1b[0m%s"%(''.join(prompt+[bars[2]]),entry,os.path.sep))
-264recurse_action(
-265base_dir=base_dir,
-266path=path+[entry],
-267prompt=prompt+[" "]
-268)
-269else:
-270ifconfig.no_colors:
-271print("%s%s"%(''.join(prompt+[bars[2]]),entry))
-272else:
-273print("%s\x1b[1m%s\x1b[0m"%(''.join(prompt+[bars[2]]),entry))
-274
-275# These are entries in the middle
-276else:
-277ifos.path.isdir(local_path+os.path.sep+entry):
-278ifconfig.no_colors:
-279print("%s%s%s"%(''.join(prompt+[bars[1]]),entry,os.path.sep))
-280else:
-281print("%s\x1b[1;96m%s\x1b[0m%s"%(''.join(prompt+[bars[1]]),entry,os.path.sep))
-282recurse_action(
-283base_dir=base_dir,
-284path=path+[entry],
-285prompt=prompt+["│ "]
-286)
-287else:
-288ifconfig.no_colors:
-289print("%s%s"%(''.join(prompt+[bars[1]]),entry))
-290else:
-291print("%s\x1b[1m%s\x1b[0m"%(''.join(prompt+[bars[1]]),entry))
-292
-293#
-294eliflen(entries)==1:
-295entry=entries[0]
-296ifos.path.isdir(local_path+os.path.sep+entry):
-297ifconfig.no_colors:
-298print("%s%s%s"%(''.join(prompt+[bars[2]]),entry,os.path.sep))
-299else:
-300print("%s\x1b[1;96m%s\x1b[0m%s"%(''.join(prompt+[bars[2]]),entry,os.path.sep))
-301recurse_action(
-302base_dir=base_dir,
-303path=path+[entry],
-304prompt=prompt+[" "]
-305)
-306else:
-307ifconfig.no_colors:
-308print("%s%s"%(''.join(prompt+[bars[2]]),entry))
-309else:
-310print("%s\x1b[1m%s\x1b[0m"%(''.join(prompt+[bars[2]]),entry))
-311
-312# Entrypoint
-313try:
-314ifconfig.no_colors:
-315print("%s%s"%(path,os.path.sep))
-316else:
-317print("\x1b[1;96m%s\x1b[0m%s"%(path,os.path.sep))
-318recurse_action(
-319base_dir=os.getcwd(),
-320path=[path],
-321prompt=[""]
-322)
-323except(BrokenPipeError,KeyboardInterrupt)ase:
-324print("[!] Interrupted.")
+234entries=sorted(entries)
+235
+236#
+237iflen(entries)>1:
+238index=0
+239forentryinentries:
+240index+=1
+241# This is the first entry
+242ifindex==0:
+243ifos.path.isdir(local_path+os.path.sep+entry):
+244ifconfig.no_colors:
+245print("%s%s%s"%(''.join(prompt+[bars[1]]),entry,os.path.sep))
+246else:
+247print("%s\x1b[1;96m%s\x1b[0m%s"%(''.join(prompt+[bars[1]]),entry,os.path.sep))
+248recurse_action(
+249base_dir=base_dir,
+250path=path+[entry],
+251prompt=prompt+["│ "]
+252)
+253else:
+254ifconfig.no_colors:
+255print("%s%s"%(''.join(prompt+[bars[1]]),entry))
+256else:
+257print("%s\x1b[1m%s\x1b[0m"%(''.join(prompt+[bars[1]]),entry))
+258
+259# This is the last entry
+260elifindex==len(entries):
+261ifos.path.isdir(local_path+os.path.sep+entry):
+262ifconfig.no_colors:
+263print("%s%s%s"%(''.join(prompt+[bars[2]]),entry,os.path.sep))
+264else:
+265print("%s\x1b[1;96m%s\x1b[0m%s"%(''.join(prompt+[bars[2]]),entry,os.path.sep))
+266recurse_action(
+267base_dir=base_dir,
+268path=path+[entry],
+269prompt=prompt+[" "]
+270)
+271else:
+272ifconfig.no_colors:
+273print("%s%s"%(''.join(prompt+[bars[2]]),entry))
+274else:
+275print("%s\x1b[1m%s\x1b[0m"%(''.join(prompt+[bars[2]]),entry))
+276
+277# These are entries in the middle
+278else:
+279ifos.path.isdir(local_path+os.path.sep+entry):
+280ifconfig.no_colors:
+281print("%s%s%s"%(''.join(prompt+[bars[1]]),entry,os.path.sep))
+282else:
+283print("%s\x1b[1;96m%s\x1b[0m%s"%(''.join(prompt+[bars[1]]),entry,os.path.sep))
+284recurse_action(
+285base_dir=base_dir,
+286path=path+[entry],
+287prompt=prompt+["│ "]
+288)
+289else:
+290ifconfig.no_colors:
+291print("%s%s"%(''.join(prompt+[bars[1]]),entry))
+292else:
+293print("%s\x1b[1m%s\x1b[0m"%(''.join(prompt+[bars[1]]),entry))
+294
+295#
+296eliflen(entries)==1:
+297entry=entries[0]
+298ifos.path.isdir(local_path+os.path.sep+entry):
+299ifconfig.no_colors:
+300print("%s%s%s"%(''.join(prompt+[bars[2]]),entry,os.path.sep))
+301else:
+302print("%s\x1b[1;96m%s\x1b[0m%s"%(''.join(prompt+[bars[2]]),entry,os.path.sep))
+303recurse_action(
+304base_dir=base_dir,
+305path=path+[entry],
+306prompt=prompt+[" "]
+307)
+308else:
+309ifconfig.no_colors:
+310print("%s%s"%(''.join(prompt+[bars[2]]),entry))
+311else:
+312print("%s\x1b[1m%s\x1b[0m"%(''.join(prompt+[bars[2]]),entry))
+313
+314# Entrypoint
+315try:
+316ifconfig.no_colors:
+317print("%s%s"%(path,os.path.sep))
+318else:
+319print("\x1b[1;96m%s\x1b[0m%s"%(path,os.path.sep))
+320recurse_action(
+321base_dir=os.getcwd(),
+322path=[path],
+323prompt=[""]
+324)
+325except(BrokenPipeError,KeyboardInterrupt)ase:
+326print("[!] Interrupted.")
@@ -868,6 +981,191 @@
+
+
+
+
+
+ def
+ resolve_local_files(arguments):
+
+
+
+
+
+
329defresolve_local_files(arguments):
+330"""
+331 Resolves local file paths based on the provided arguments.
+332
+333 This function takes a list of arguments, which can include wildcard patterns, and resolves them to actual file paths.
+334 If an argument contains a wildcard ('*'), it attempts to match files in the specified directory against the pattern.
+335 If the argument does not contain a wildcard, it is added to the list of resolved files as is.
+336
+337 Args:
+338 arguments (list): A list of file path arguments, which may include wildcard patterns.
+339
+340 Returns:
+341 list: A list of resolved file paths that match the provided arguments.
+342 """
+343
+344resolved_files=[]
+345forarginarguments:
+346if'*'inarg:
+347try:
+348path=os.path.dirname(arg)or'.'
+349pattern=os.path.basename(arg)
+350forentryinos.listdir(path):
+351iffnmatch.fnmatch(entry,pattern):
+352resolved_files.append(os.path.join(path,entry))
+353exceptFileNotFoundErroraserr:
+354pass
+355else:
+356resolved_files.append(arg)
+357resolved_files=sorted(list(set(resolved_files)))
+358returnresolved_files
+
+
+
+
Resolves local file paths based on the provided arguments.
+
+
This function takes a list of arguments, which can include wildcard patterns, and resolves them to actual file paths.
+If an argument contains a wildcard ('*'), it attempts to match files in the specified directory against the pattern.
+If the argument does not contain a wildcard, it is added to the list of resolved files as is.
+
+
Args:
+ arguments (list): A list of file path arguments, which may include wildcard patterns.
+
+
Returns:
+ list: A list of resolved file paths that match the provided arguments.
361defresolve_remote_files(smbSession,arguments):
+362"""
+363 Resolves remote file paths based on the provided arguments using an SMB session.
+364
+365 This function takes a list of arguments, which can include wildcard patterns, and resolves them to actual remote file paths.
+366 If an argument contains a wildcard ('*'), it attempts to match files in the specified remote directory against the pattern.
+367 If the argument does not contain a wildcard, it is added to the list of resolved files as is.
+368
+369 Args:
+370 smbsession (SMBSession): The SMB session through which to access the files.
+371 arguments (list): A list of file path arguments, which may include wildcard patterns.
+372
+373 Returns:
+374 list: A list of resolved remote file paths that match the provided arguments.
+375 """
+376
+377resolved_files=[]
+378forarginarguments:
+379if'*'inarg:
+380ifarg=='*':
+381path=smbSession.smb_cwd
+382elifarg.startswith(ntpath.sep):
+383path=ntpath.dirname(arg)
+384else:
+385path=ntpath.normpath(smbSession.smb_cwd+ntpath.sep+ntpath.dirname(arg))
+386
+387try:
+388contents=smbSession.smbClient.listPath(
+389shareName=smbSession.smb_share,
+390path=path+ntpath.sep+'*'
+391)
+392contents=[eforeincontentsife.get_longname()notin['.','..']]
+393
+394forentryincontents:
+395iffnmatch.fnmatch(entry.get_longname(),ntpath.basename(arg)):
+396resolved_files.append(ntpath.join(path,entry.get_longname()))
+397
+398exceptExceptionaserr:
+399pass
+400else:
+401resolved_files.append(arg)
+402resolved_files=sorted(list(set(resolved_files)))
+403returnresolved_files
+
+
+
+
Resolves remote file paths based on the provided arguments using an SMB session.
+
+
This function takes a list of arguments, which can include wildcard patterns, and resolves them to actual remote file paths.
+If an argument contains a wildcard ('*'), it attempts to match files in the specified remote directory against the pattern.
+If the argument does not contain a wildcard, it is added to the list of resolved files as is.
+
+
Args:
+ smbsession (SMBSession): The SMB session through which to access the files.
+ arguments (list): A list of file path arguments, which may include wildcard patterns.
+
+
Returns:
+ list: A list of resolved remote file paths that match the provided arguments.
406defis_port_open(target,port)->bool:
+407"""
+408 Check if a specific port on a target host is open.
+409
+410 This function attempts to establish a TCP connection to the specified port on the target host.
+411 If the connection is successful, it indicates that the port is open. If the connection fails,
+412 it indicates that the port is closed or the host is unreachable.
+413
+414 Args:
+415 target (str): The hostname or IP address of the target host.
+416 port (int): The port number to check.
+417
+418 Returns:
+419 bool: True if the port is open, False otherwise.
+420 """
+421
+422withsocket.socket(socket.AF_INET,socket.SOCK_STREAM)ass:
+423s.settimeout(0.1)
+424# Non-existant domains cause a lot of errors, added error handling
+425try:
+426returns.connect_ex((target,port))==0
+427exceptExceptionase:
+428returnFalse
+
+
+
+
Check if a specific port on a target host is open.
+
+
This function attempts to establish a TCP connection to the specified port on the target host.
+If the connection is successful, it indicates that the port is open. If the connection fails,
+it indicates that the port is closed or the host is unreachable.
+
+
Args:
+ target (str): The hostname or IP address of the target host.
+ port (int): The port number to check.
+
+
Returns:
+ bool: True if the port is open, False otherwise.