smbclientng.modules.Find
1#!/usr/bin/env python3 2# -*- coding: utf-8 -*- 3# File name : InteractiveShell.py 4# Author : Podalirius (@podalirius_) 5# Date created : 23 may 2024 6 7 8import impacket 9import ntpath 10import re 11from smbclientng.core.Module import Module 12from smbclientng.core.ModuleArgumentParser import ModuleArgumentParser 13from smbclientng.core.utils import windows_ls_entry 14 15 16class Find(Module): 17 """ 18 A class to search for files in a directory hierarchy. 19 20 This class provides functionality to search for files based on various criteria in a directory hierarchy. 21 """ 22 23 name = "find" 24 description = "Search for files in a directory hierarchy" 25 26 def parseArgs(self, arguments): 27 """ 28 Parses the command line arguments provided to the module. 29 30 This method initializes the argument parser with the module's name and description, and defines all the necessary arguments that the module accepts. It then parses the provided command line arguments based on these definitions. 31 32 Args: 33 arguments (str): A string of command line arguments. 34 35 Returns: 36 ModuleArgumentParser.Namespace | None: The parsed arguments as a Namespace object if successful, None if there are no arguments or help is requested. 37 """ 38 39 parser = ModuleArgumentParser(prog=self.name, description=self.description) 40 41 # Adding positional arguments 42 parser.add_argument("paths", metavar="PATH", type=str, nargs="*", default=[], help="The starting point(s) for the search.") 43 44 # Adding options for filtering 45 parser.add_argument("-name", type=str, help="Base of file name (the path with the leading directories removed).") 46 parser.add_argument("-iname", type=str, help="Like -name, but the match is case insensitive.") 47 parser.add_argument("-type", type=str, default=None, help="File type (e.g., f for regular file, d for directory).") 48 parser.add_argument("-size", type=str, help="File uses n units of space.") 49 # parser.add_argument("-mtime", type=str, help="File's data was last modified n*24 hours ago") 50 # parser.add_argument("-ctime", type=str, help="File's status was last changed n*24 hours ago") 51 # parser.add_argument("-atime", type=str, help="File was last accessed n*24 hours ago") 52 53 # Adding actions 54 parser.add_argument("-ls", action="store_true", default=False, help="List current file in ls -dils format on standard output.") 55 parser.add_argument("-download", action="store_true", default=False, help="List current file in ls -dils format on standard output.") 56 57 # Other options 58 parser.add_argument("-maxdepth", type=int, help="Descend at most levels (a non-negative integer) levels of directories below the command line arguments.") 59 parser.add_argument("-mindepth", type=int, help="Do not apply any tests or actions at levels less than levels (a non-negative integer).") 60 61 if len(arguments.strip()) == 0: 62 parser.print_help() 63 return None 64 else: 65 self.options = self.processArguments(parser, arguments) 66 67 if self.options is not None: 68 if len(self.options.paths) == 0: 69 parser.print_help() 70 self.options = None 71 72 return self.options 73 74 def __find_callback(self, entry, fullpath, depth): 75 # Documentation for __find_callback function 76 """ 77 This function serves as a callback for the find operation. It applies filters based on the command line arguments 78 and decides whether to print, download, or list the entry in 'ls -dils' format if it matches the specified filters. 79 80 Args: 81 entry (SMBEntry): The current file or directory entry being processed. 82 fullpath (str): The full path to the entry. 83 84 The function checks against filters such as file name, case sensitivity, file type, and size. If the entry matches 85 the filters, it will perform actions like printing the entry's details, downloading the entry, or listing the entry 86 based on the options provided in the command line arguments. 87 """ 88 89 # Match and print results 90 do_print_results = True 91 if self.options.mindepth is not None: 92 if depth < self.options.mindepth: 93 do_print_results = False 94 if self.options.maxdepth is not None: 95 if depth > self.options.maxdepth: 96 do_print_results = False 97 98 if do_print_results: 99 do_print_entry = False 100 # Print directory 101 if entry.is_directory(): 102 if (self.options.type == 'd' or self.options.type is None): 103 # No name filtering 104 if self.options.name is None and self.options.iname is None: 105 do_print_entry = True 106 107 # Filtering on names case sensitive 108 elif self.options.name is not None: 109 if '*' in self.options.name: 110 regex = self.options.name 111 regex = regex.replace('.', '\\.') 112 regex = regex.replace('*', '.*') 113 regex = '^' + regex + '$' 114 if re.match(regex, entry.get_longname()): 115 do_print_entry = True 116 else: 117 do_print_entry = False 118 else: 119 do_print_entry = (entry.get_longname().lower() == self.options.name.lower()) 120 121 # Filtering on names case insensitive 122 elif self.options.iname is not None: 123 if '*' in self.options.iname: 124 regex = self.options.iname 125 regex = regex.replace('.', '\\.') 126 regex = regex.replace('*', '.*') 127 regex = '^' + regex + '$' 128 if re.match(regex, entry.get_longname(), re.IGNORECASE): 129 do_print_entry = True 130 else: 131 do_print_entry = False 132 else: 133 do_print_entry = (entry.get_longname().lower() == self.options.iname.lower()) 134 135 # Print file 136 else: 137 if (self.options.type == 'f' or self.options.type is None): 138 # No name filtering 139 if self.options.name is None and self.options.iname is None: 140 do_print_entry = True 141 142 # Filtering on names case sensitive 143 elif self.options.name is not None: 144 if '*' in self.options.name: 145 regex = self.options.name 146 regex = regex.replace('.', '\\.') 147 regex = regex.replace('*', '.*') 148 regex = '^' + regex + '$' 149 if re.match(regex, entry.get_longname()): 150 do_print_entry = True 151 else: 152 do_print_entry = False 153 else: 154 do_print_entry = (entry.get_longname().lower() == self.options.name.lower()) 155 156 # Filtering on names case insensitive 157 elif self.options.iname is not None: 158 if '*' in self.options.iname: 159 regex = self.options.iname 160 regex = regex.replace('.', '\\.') 161 regex = regex.replace('*', '.*') 162 regex = '^' + regex + '$' 163 if re.match(regex, entry.get_longname(), re.IGNORECASE): 164 do_print_entry = True 165 else: 166 do_print_entry = False 167 else: 168 do_print_entry = (entry.get_longname().lower() == self.options.iname.lower()) 169 170 if do_print_entry: 171 # Actions on matches 172 if self.options.download: 173 if entry.is_directory(): 174 self.smbSession.get_file_recursively(path=fullpath) 175 else: 176 self.smbSession.get_file(path=fullpath, keepRemotePath=True) 177 # Output formats 178 if self.options.ls: 179 if entry.is_directory(): 180 windows_ls_entry(entry, fullpath) 181 else: 182 windows_ls_entry(entry, fullpath) 183 else: 184 if entry.is_directory(): 185 print("%s" % fullpath) 186 else: 187 print("%s" % fullpath) 188 189 return None 190 191 def run(self, arguments): 192 """ 193 This function recursively searches for files in a directory hierarchy and prints the results based on specified criteria. 194 195 Args: 196 base_dir (str): The base directory to start the search from. 197 paths (list): List of paths to search within the base directory. 198 depth (int): The current depth level in the directory hierarchy. 199 200 Returns: 201 None 202 """ 203 204 self.options = self.parseArgs(arguments=arguments) 205 206 if self.options is not None: 207 # Entrypoint 208 try: 209 next_directories_to_explore = [] 210 for path in list(set(self.options.paths)): 211 next_directories_to_explore.append(ntpath.normpath(path) + ntpath.sep) 212 next_directories_to_explore = sorted(list(set(next_directories_to_explore))) 213 214 self.smbSession.find( 215 paths=next_directories_to_explore, 216 callback=self.__find_callback 217 ) 218 219 except (BrokenPipeError, KeyboardInterrupt) as e: 220 print("[!] Interrupted.") 221 self.smbSession.close_smb_session() 222 self.smbSession.init_smb_session()
17class Find(Module): 18 """ 19 A class to search for files in a directory hierarchy. 20 21 This class provides functionality to search for files based on various criteria in a directory hierarchy. 22 """ 23 24 name = "find" 25 description = "Search for files in a directory hierarchy" 26 27 def parseArgs(self, arguments): 28 """ 29 Parses the command line arguments provided to the module. 30 31 This method initializes the argument parser with the module's name and description, and defines all the necessary arguments that the module accepts. It then parses the provided command line arguments based on these definitions. 32 33 Args: 34 arguments (str): A string of command line arguments. 35 36 Returns: 37 ModuleArgumentParser.Namespace | None: The parsed arguments as a Namespace object if successful, None if there are no arguments or help is requested. 38 """ 39 40 parser = ModuleArgumentParser(prog=self.name, description=self.description) 41 42 # Adding positional arguments 43 parser.add_argument("paths", metavar="PATH", type=str, nargs="*", default=[], help="The starting point(s) for the search.") 44 45 # Adding options for filtering 46 parser.add_argument("-name", type=str, help="Base of file name (the path with the leading directories removed).") 47 parser.add_argument("-iname", type=str, help="Like -name, but the match is case insensitive.") 48 parser.add_argument("-type", type=str, default=None, help="File type (e.g., f for regular file, d for directory).") 49 parser.add_argument("-size", type=str, help="File uses n units of space.") 50 # parser.add_argument("-mtime", type=str, help="File's data was last modified n*24 hours ago") 51 # parser.add_argument("-ctime", type=str, help="File's status was last changed n*24 hours ago") 52 # parser.add_argument("-atime", type=str, help="File was last accessed n*24 hours ago") 53 54 # Adding actions 55 parser.add_argument("-ls", action="store_true", default=False, help="List current file in ls -dils format on standard output.") 56 parser.add_argument("-download", action="store_true", default=False, help="List current file in ls -dils format on standard output.") 57 58 # Other options 59 parser.add_argument("-maxdepth", type=int, help="Descend at most levels (a non-negative integer) levels of directories below the command line arguments.") 60 parser.add_argument("-mindepth", type=int, help="Do not apply any tests or actions at levels less than levels (a non-negative integer).") 61 62 if len(arguments.strip()) == 0: 63 parser.print_help() 64 return None 65 else: 66 self.options = self.processArguments(parser, arguments) 67 68 if self.options is not None: 69 if len(self.options.paths) == 0: 70 parser.print_help() 71 self.options = None 72 73 return self.options 74 75 def __find_callback(self, entry, fullpath, depth): 76 # Documentation for __find_callback function 77 """ 78 This function serves as a callback for the find operation. It applies filters based on the command line arguments 79 and decides whether to print, download, or list the entry in 'ls -dils' format if it matches the specified filters. 80 81 Args: 82 entry (SMBEntry): The current file or directory entry being processed. 83 fullpath (str): The full path to the entry. 84 85 The function checks against filters such as file name, case sensitivity, file type, and size. If the entry matches 86 the filters, it will perform actions like printing the entry's details, downloading the entry, or listing the entry 87 based on the options provided in the command line arguments. 88 """ 89 90 # Match and print results 91 do_print_results = True 92 if self.options.mindepth is not None: 93 if depth < self.options.mindepth: 94 do_print_results = False 95 if self.options.maxdepth is not None: 96 if depth > self.options.maxdepth: 97 do_print_results = False 98 99 if do_print_results: 100 do_print_entry = False 101 # Print directory 102 if entry.is_directory(): 103 if (self.options.type == 'd' or self.options.type is None): 104 # No name filtering 105 if self.options.name is None and self.options.iname is None: 106 do_print_entry = True 107 108 # Filtering on names case sensitive 109 elif self.options.name is not None: 110 if '*' in self.options.name: 111 regex = self.options.name 112 regex = regex.replace('.', '\\.') 113 regex = regex.replace('*', '.*') 114 regex = '^' + regex + '$' 115 if re.match(regex, entry.get_longname()): 116 do_print_entry = True 117 else: 118 do_print_entry = False 119 else: 120 do_print_entry = (entry.get_longname().lower() == self.options.name.lower()) 121 122 # Filtering on names case insensitive 123 elif self.options.iname is not None: 124 if '*' in self.options.iname: 125 regex = self.options.iname 126 regex = regex.replace('.', '\\.') 127 regex = regex.replace('*', '.*') 128 regex = '^' + regex + '$' 129 if re.match(regex, entry.get_longname(), re.IGNORECASE): 130 do_print_entry = True 131 else: 132 do_print_entry = False 133 else: 134 do_print_entry = (entry.get_longname().lower() == self.options.iname.lower()) 135 136 # Print file 137 else: 138 if (self.options.type == 'f' or self.options.type is None): 139 # No name filtering 140 if self.options.name is None and self.options.iname is None: 141 do_print_entry = True 142 143 # Filtering on names case sensitive 144 elif self.options.name is not None: 145 if '*' in self.options.name: 146 regex = self.options.name 147 regex = regex.replace('.', '\\.') 148 regex = regex.replace('*', '.*') 149 regex = '^' + regex + '$' 150 if re.match(regex, entry.get_longname()): 151 do_print_entry = True 152 else: 153 do_print_entry = False 154 else: 155 do_print_entry = (entry.get_longname().lower() == self.options.name.lower()) 156 157 # Filtering on names case insensitive 158 elif self.options.iname is not None: 159 if '*' in self.options.iname: 160 regex = self.options.iname 161 regex = regex.replace('.', '\\.') 162 regex = regex.replace('*', '.*') 163 regex = '^' + regex + '$' 164 if re.match(regex, entry.get_longname(), re.IGNORECASE): 165 do_print_entry = True 166 else: 167 do_print_entry = False 168 else: 169 do_print_entry = (entry.get_longname().lower() == self.options.iname.lower()) 170 171 if do_print_entry: 172 # Actions on matches 173 if self.options.download: 174 if entry.is_directory(): 175 self.smbSession.get_file_recursively(path=fullpath) 176 else: 177 self.smbSession.get_file(path=fullpath, keepRemotePath=True) 178 # Output formats 179 if self.options.ls: 180 if entry.is_directory(): 181 windows_ls_entry(entry, fullpath) 182 else: 183 windows_ls_entry(entry, fullpath) 184 else: 185 if entry.is_directory(): 186 print("%s" % fullpath) 187 else: 188 print("%s" % fullpath) 189 190 return None 191 192 def run(self, arguments): 193 """ 194 This function recursively searches for files in a directory hierarchy and prints the results based on specified criteria. 195 196 Args: 197 base_dir (str): The base directory to start the search from. 198 paths (list): List of paths to search within the base directory. 199 depth (int): The current depth level in the directory hierarchy. 200 201 Returns: 202 None 203 """ 204 205 self.options = self.parseArgs(arguments=arguments) 206 207 if self.options is not None: 208 # Entrypoint 209 try: 210 next_directories_to_explore = [] 211 for path in list(set(self.options.paths)): 212 next_directories_to_explore.append(ntpath.normpath(path) + ntpath.sep) 213 next_directories_to_explore = sorted(list(set(next_directories_to_explore))) 214 215 self.smbSession.find( 216 paths=next_directories_to_explore, 217 callback=self.__find_callback 218 ) 219 220 except (BrokenPipeError, KeyboardInterrupt) as e: 221 print("[!] Interrupted.") 222 self.smbSession.close_smb_session() 223 self.smbSession.init_smb_session()
A class to search for files in a directory hierarchy.
This class provides functionality to search for files based on various criteria in a directory hierarchy.
27 def parseArgs(self, arguments): 28 """ 29 Parses the command line arguments provided to the module. 30 31 This method initializes the argument parser with the module's name and description, and defines all the necessary arguments that the module accepts. It then parses the provided command line arguments based on these definitions. 32 33 Args: 34 arguments (str): A string of command line arguments. 35 36 Returns: 37 ModuleArgumentParser.Namespace | None: The parsed arguments as a Namespace object if successful, None if there are no arguments or help is requested. 38 """ 39 40 parser = ModuleArgumentParser(prog=self.name, description=self.description) 41 42 # Adding positional arguments 43 parser.add_argument("paths", metavar="PATH", type=str, nargs="*", default=[], help="The starting point(s) for the search.") 44 45 # Adding options for filtering 46 parser.add_argument("-name", type=str, help="Base of file name (the path with the leading directories removed).") 47 parser.add_argument("-iname", type=str, help="Like -name, but the match is case insensitive.") 48 parser.add_argument("-type", type=str, default=None, help="File type (e.g., f for regular file, d for directory).") 49 parser.add_argument("-size", type=str, help="File uses n units of space.") 50 # parser.add_argument("-mtime", type=str, help="File's data was last modified n*24 hours ago") 51 # parser.add_argument("-ctime", type=str, help="File's status was last changed n*24 hours ago") 52 # parser.add_argument("-atime", type=str, help="File was last accessed n*24 hours ago") 53 54 # Adding actions 55 parser.add_argument("-ls", action="store_true", default=False, help="List current file in ls -dils format on standard output.") 56 parser.add_argument("-download", action="store_true", default=False, help="List current file in ls -dils format on standard output.") 57 58 # Other options 59 parser.add_argument("-maxdepth", type=int, help="Descend at most levels (a non-negative integer) levels of directories below the command line arguments.") 60 parser.add_argument("-mindepth", type=int, help="Do not apply any tests or actions at levels less than levels (a non-negative integer).") 61 62 if len(arguments.strip()) == 0: 63 parser.print_help() 64 return None 65 else: 66 self.options = self.processArguments(parser, arguments) 67 68 if self.options is not None: 69 if len(self.options.paths) == 0: 70 parser.print_help() 71 self.options = None 72 73 return self.options
Parses the command line arguments provided to the module.
This method initializes the argument parser with the module's name and description, and defines all the necessary arguments that the module accepts. It then parses the provided command line arguments based on these definitions.
Args: arguments (str): A string of command line arguments.
Returns: ModuleArgumentParser.Namespace | None: The parsed arguments as a Namespace object if successful, None if there are no arguments or help is requested.
192 def run(self, arguments): 193 """ 194 This function recursively searches for files in a directory hierarchy and prints the results based on specified criteria. 195 196 Args: 197 base_dir (str): The base directory to start the search from. 198 paths (list): List of paths to search within the base directory. 199 depth (int): The current depth level in the directory hierarchy. 200 201 Returns: 202 None 203 """ 204 205 self.options = self.parseArgs(arguments=arguments) 206 207 if self.options is not None: 208 # Entrypoint 209 try: 210 next_directories_to_explore = [] 211 for path in list(set(self.options.paths)): 212 next_directories_to_explore.append(ntpath.normpath(path) + ntpath.sep) 213 next_directories_to_explore = sorted(list(set(next_directories_to_explore))) 214 215 self.smbSession.find( 216 paths=next_directories_to_explore, 217 callback=self.__find_callback 218 ) 219 220 except (BrokenPipeError, KeyboardInterrupt) as e: 221 print("[!] Interrupted.") 222 self.smbSession.close_smb_session() 223 self.smbSession.init_smb_session()
This function recursively searches for files in a directory hierarchy and prints the results based on specified criteria.
Args: base_dir (str): The base directory to start the search from. paths (list): List of paths to search within the base directory. depth (int): The current depth level in the directory hierarchy.
Returns: None