smbclientng.core.utils

  1#!/usr/bin/env python3
  2# -*- coding: utf-8 -*-
  3# File name          : utils.py
  4# Author             : Podalirius (@podalirius_)
  5# Date created       : 23 may 2024
  6
  7
  8import datetime
  9import fnmatch
 10import ntpath
 11import os
 12import re
 13import socket
 14import stat
 15
 16
 17def parse_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
 38    lm_hash_value, nt_hash_value = "", ""
 39    if lm_nt_hashes_string is not None:
 40        matched = re.match("([0-9a-f]{32})?(:)?([0-9a-f]{32})?", lm_nt_hashes_string.strip().lower())
 41        m_lm_hash, m_sep, m_nt_hash = matched.groups()
 42        if m_lm_hash is None and m_sep is None and m_nt_hash is None:
 43            lm_hash_value, nt_hash_value = "", ""
 44        elif m_lm_hash is None and m_nt_hash is not None:
 45            lm_hash_value = "aad3b435b51404eeaad3b435b51404ee"
 46            nt_hash_value = m_nt_hash
 47        elif m_lm_hash is not None and m_nt_hash is None:
 48            lm_hash_value = m_lm_hash
 49            nt_hash_value = "31d6cfe0d16ae931b73c59d7e0c089c0"
 50    return lm_hash_value, nt_hash_value
 51
 52
 53def b_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
 68    units = ['B','kB','MB','GB','TB','PB']
 69    for k in range(len(units)):
 70        if l < (1024**(k+1)):
 71            break
 72    return "%4.2f %s" % (round(l/(1024**(k)),2), units[k])
 73
 74
 75def unix_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
 92    mode = os.lstat(entryname).st_mode
 93    permissions = []
 94
 95    permissions.append('d' if stat.S_ISDIR(mode) else '-')
 96
 97    permissions.append('r' if mode & stat.S_IRUSR else '-')
 98    permissions.append('w' if mode & stat.S_IWUSR else '-')
 99    permissions.append('x' if mode & stat.S_IXUSR else '-')
100
101    permissions.append('r' if mode & stat.S_IRGRP else '-')
102    permissions.append('w' if mode & stat.S_IWGRP else '-')
103    permissions.append('x' if mode & stat.S_IXGRP else '-')
104
105    permissions.append('r' if mode & stat.S_IROTH else '-')
106    permissions.append('w' if mode & stat.S_IWOTH else '-')
107    permissions.append('x' if mode & stat.S_IXOTH else '-')
108
109    return ''.join(permissions)
110
111
112def STYPE_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
126    known_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    }
148    flags = []
149    if (stype_value & 0b11) == known_flags["STYPE_DISKTREE"]:
150        flags.append("STYPE_DISKTREE")
151    elif (stype_value & 0b11) == known_flags["STYPE_PRINTQ"]:
152        flags.append("STYPE_PRINTQ")
153    elif (stype_value & 0b11) == known_flags["STYPE_DEVICE"]:
154        flags.append("STYPE_DEVICE")
155    elif (stype_value & 0b11) == known_flags["STYPE_IPC"]:
156        flags.append("STYPE_IPC")
157    if (stype_value & known_flags["STYPE_SPECIAL"]) == known_flags["STYPE_SPECIAL"]:
158        flags.append("STYPE_SPECIAL")
159    if (stype_value & known_flags["STYPE_TEMPORARY"]) == known_flags["STYPE_TEMPORARY"]:
160        flags.append("STYPE_TEMPORARY")
161    return flags
162
163
164def windows_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    
175    if pathToPrint is not None:
176        pathToPrint = pathToPrint + ntpath.sep + entry.get_longname()
177    else:
178        pathToPrint = entry.get_longname()
179
180    meta_string = ""
181    meta_string += ("d" if entry.is_directory() else "-")
182    meta_string += ("a" if entry.is_archive() else "-")
183    meta_string += ("c" if entry.is_compressed() else "-")
184    meta_string += ("h" if entry.is_hidden() else "-")
185    meta_string += ("n" if entry.is_normal() else "-")
186    meta_string += ("r" if entry.is_readonly() else "-")
187    meta_string += ("s" if entry.is_system() else "-")
188    meta_string += ("t" if entry.is_temporary() else "-")
189
190    size_str = b_filesize(entry.get_filesize())
191
192    date_str = datetime.datetime.fromtimestamp(entry.get_atime_epoch()).strftime("%Y-%m-%d %H:%M")
193    
194    if entry.is_directory():
195        if config.no_colors:
196            print("%s %10s  %s  %s\\" % (meta_string, size_str, date_str, pathToPrint))
197        else:
198            print("%s %10s  %s  \x1b[1;96m%s\x1b[0m\\" % (meta_string, size_str, date_str, pathToPrint))
199    else:
200        if config.no_colors:
201            print("%s %10s  %s  %s" % (meta_string, size_str, date_str, pathToPrint))
202        else:
203            print("%s %10s  %s  \x1b[1m%s\x1b[0m" % (meta_string, size_str, date_str, pathToPrint))
204
205
206def local_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
218    def recurse_action(base_dir="", path=[], prompt=[]):
219        bars = ["│   ", "├── ", "└── "]
220
221        local_path = os.path.normpath(base_dir + os.path.sep + os.path.sep.join(path) + os.path.sep)
222
223        entries = []
224        try:
225            entries = os.listdir(local_path)
226        except Exception as err:
227            if config.no_colors:
228                print("%s%s" % (''.join(prompt+[bars[2]]), err))
229            else:
230                print("%s\x1b[1;91m%s\x1b[0m" % (''.join(prompt+[bars[2]]), err))
231            return 
232
233        entries = sorted(entries)
234
235        # 
236        if len(entries) > 1:
237            index = 0
238            for entry in entries:
239                index += 1
240                # This is the first entry 
241                if index == 0:
242                    if os.path.isdir(local_path + os.path.sep + entry):
243                        if config.no_colors:
244                            print("%s%s%s" % (''.join(prompt+[bars[1]]), entry, os.path.sep))
245                        else:
246                            print("%s\x1b[1;96m%s\x1b[0m%s" % (''.join(prompt+[bars[1]]), entry, os.path.sep))
247                        recurse_action(
248                            base_dir=base_dir, 
249                            path=path+[entry],
250                            prompt=prompt+["│   "]
251                        )
252                    else:
253                        if config.no_colors:
254                            print("%s%s" % (''.join(prompt+[bars[1]]), entry))
255                        else:
256                            print("%s\x1b[1m%s\x1b[0m" % (''.join(prompt+[bars[1]]), entry))
257
258                # This is the last entry
259                elif index == len(entries):
260                    if os.path.isdir(local_path + os.path.sep + entry):
261                        if config.no_colors:
262                            print("%s%s%s" % (''.join(prompt+[bars[2]]), entry, os.path.sep))
263                        else:
264                            print("%s\x1b[1;96m%s\x1b[0m%s" % (''.join(prompt+[bars[2]]), entry, os.path.sep))
265                        recurse_action(
266                            base_dir=base_dir, 
267                            path=path+[entry],
268                            prompt=prompt+["    "]
269                        )
270                    else:
271                        if config.no_colors:
272                            print("%s%s" % (''.join(prompt+[bars[2]]), entry))
273                        else:
274                            print("%s\x1b[1m%s\x1b[0m" % (''.join(prompt+[bars[2]]), entry))
275                    
276                # These are entries in the middle
277                else:
278                    if os.path.isdir(local_path + os.path.sep + entry):
279                        if config.no_colors:
280                            print("%s%s%s" % (''.join(prompt+[bars[1]]), entry, os.path.sep))
281                        else:
282                            print("%s\x1b[1;96m%s\x1b[0m%s" % (''.join(prompt+[bars[1]]), entry, os.path.sep))
283                        recurse_action(
284                            base_dir=base_dir, 
285                            path=path+[entry],
286                            prompt=prompt+["│   "]
287                        )
288                    else:
289                        if config.no_colors:
290                            print("%s%s" % (''.join(prompt+[bars[1]]), entry))
291                        else:
292                            print("%s\x1b[1m%s\x1b[0m" % (''.join(prompt+[bars[1]]), entry))
293
294        # 
295        elif len(entries) == 1:
296            entry = entries[0]
297            if os.path.isdir(local_path + os.path.sep + entry):
298                if config.no_colors:
299                    print("%s%s%s" % (''.join(prompt+[bars[2]]), entry, os.path.sep))
300                else:
301                    print("%s\x1b[1;96m%s\x1b[0m%s" % (''.join(prompt+[bars[2]]), entry, os.path.sep))
302                recurse_action(
303                    base_dir=base_dir, 
304                    path=path+[entry],
305                    prompt=prompt+["    "]
306                )
307            else:
308                if config.no_colors:
309                    print("%s%s" % (''.join(prompt+[bars[2]]), entry))
310                else:
311                    print("%s\x1b[1m%s\x1b[0m" % (''.join(prompt+[bars[2]]), entry))
312
313    # Entrypoint
314    try:
315        if config.no_colors:
316            print("%s%s" % (path, os.path.sep))
317        else:
318            print("\x1b[1;96m%s\x1b[0m%s" % (path, os.path.sep))
319        recurse_action(
320            base_dir=os.getcwd(),
321            path=[path],
322            prompt=[""]
323        )
324    except (BrokenPipeError, KeyboardInterrupt) as e:
325        print("[!] Interrupted.")
326
327
328def resolve_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
343    resolved_files = []
344    for arg in arguments:
345        if '*' in arg:
346            try:
347                path = os.path.dirname(arg) or '.'
348                pattern = os.path.basename(arg)
349                for entry in os.listdir(path):
350                    if fnmatch.fnmatch(entry, pattern):
351                        resolved_files.append(os.path.join(path, entry))
352            except FileNotFoundError as err:
353                pass
354        else:
355            resolved_files.append(arg)
356    resolved_files = sorted(list(set(resolved_files)))
357    return resolved_files
358
359
360def resolve_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
376    resolved_files = []
377    for arg in arguments:
378        if '*' in arg:
379            if arg == '*':
380                path = smbSession.smb_cwd
381            elif arg.startswith(ntpath.sep):
382                path = ntpath.dirname(arg)
383            else:
384                path = ntpath.normpath(smbSession.smb_cwd + ntpath.sep + ntpath.dirname(arg))
385
386            try:
387                contents = smbSession.smbClient.listPath(
388                    shareName=smbSession.smb_share,
389                    path=path + ntpath.sep + '*'
390                )
391                contents = [e for e in contents if e.get_longname() not in ['.', '..']]
392
393                for entry in contents:
394                    if fnmatch.fnmatch(entry.get_longname(), ntpath.basename(arg)):
395                        resolved_files.append(ntpath.join(path, entry.get_longname()))
396
397            except Exception as err:
398                pass
399        else:
400            resolved_files.append(arg)
401    resolved_files = sorted(list(set(resolved_files)))
402    return resolved_files
403
404
405def is_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
421    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
422        s.settimeout(0.1)
423        # Non-existant domains cause a lot of errors, added error handling
424        try:
425            return s.connect_ex((target, port)) == 0
426        except Exception as e:
427            return False
def parse_lm_nt_hashes(lm_nt_hashes_string):
18def parse_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
39    lm_hash_value, nt_hash_value = "", ""
40    if lm_nt_hashes_string is not None:
41        matched = re.match("([0-9a-f]{32})?(:)?([0-9a-f]{32})?", lm_nt_hashes_string.strip().lower())
42        m_lm_hash, m_sep, m_nt_hash = matched.groups()
43        if m_lm_hash is None and m_sep is None and m_nt_hash is None:
44            lm_hash_value, nt_hash_value = "", ""
45        elif m_lm_hash is None and m_nt_hash is not None:
46            lm_hash_value = "aad3b435b51404eeaad3b435b51404ee"
47            nt_hash_value = m_nt_hash
48        elif m_lm_hash is not None and m_nt_hash is None:
49            lm_hash_value = m_lm_hash
50            nt_hash_value = "31d6cfe0d16ae931b73c59d7e0c089c0"
51    return lm_hash_value, nt_hash_value

Parse the input string containing LM and NT hash values and return them separately.

This function takes a string containing LM and NT hash values, typically separated by a colon (:). It returns the LM and NT hash values as separate strings. If only one hash value is provided, it is assumed to be the NT hash and the LM hash is set to its default value. If no valid hash values are found, both return values are empty strings.

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

Returns: tuple: A tuple containing two strings (lm_hash_value, nt_hash_value). - lm_hash_value: The LM hash value or its default if not provided. - nt_hash_value: The NT hash value or its default if not provided.

Extracted from p0dalirius/sectools library Src: https://github.com/p0dalirius/sectools/blob/7bb3f5cb7815ad4d4845713c8739e2e2b0ea4e75/sectools/windows/crypto.py#L11-L24

def b_filesize(l):
54def b_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
69    units = ['B','kB','MB','GB','TB','PB']
70    for k in range(len(units)):
71        if l < (1024**(k+1)):
72            break
73    return "%4.2f %s" % (round(l/(1024**(k)),2), units[k])

Convert a file size from bytes to a more readable format using the largest appropriate unit.

This function takes an integer representing a file size in bytes and converts it to a human-readable string using the largest appropriate unit from bytes (B) to petabytes (PB). The result is rounded to two decimal places.

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

Returns: str: A string representing the file size in a more readable format, including the appropriate unit.

def unix_permissions(entryname):
 76def unix_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
 93    mode = os.lstat(entryname).st_mode
 94    permissions = []
 95
 96    permissions.append('d' if stat.S_ISDIR(mode) else '-')
 97
 98    permissions.append('r' if mode & stat.S_IRUSR else '-')
 99    permissions.append('w' if mode & stat.S_IWUSR else '-')
100    permissions.append('x' if mode & stat.S_IXUSR else '-')
101
102    permissions.append('r' if mode & stat.S_IRGRP else '-')
103    permissions.append('w' if mode & stat.S_IWGRP else '-')
104    permissions.append('x' if mode & stat.S_IXGRP else '-')
105
106    permissions.append('r' if mode & stat.S_IROTH else '-')
107    permissions.append('w' if mode & stat.S_IWOTH else '-')
108    permissions.append('x' if mode & stat.S_IXOTH else '-')
109
110    return ''.join(permissions)

Generate a string representing the Unix-style permissions for a given file or directory.

This function uses the os.lstat() method to retrieve the status of the specified file or directory, then constructs a string that represents the Unix-style permissions based on the mode of the file.

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

Returns: str: A string of length 10 representing the Unix-style permissions (e.g., '-rwxr-xr--'). The first character is either 'd' (directory), '-' (not a directory), followed by three groups of 'r', 'w', 'x' (read, write, execute permissions) for owner, group, and others respectively.

def STYPE_MASK(stype_value):
113def STYPE_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
127    known_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    }
149    flags = []
150    if (stype_value & 0b11) == known_flags["STYPE_DISKTREE"]:
151        flags.append("STYPE_DISKTREE")
152    elif (stype_value & 0b11) == known_flags["STYPE_PRINTQ"]:
153        flags.append("STYPE_PRINTQ")
154    elif (stype_value & 0b11) == known_flags["STYPE_DEVICE"]:
155        flags.append("STYPE_DEVICE")
156    elif (stype_value & 0b11) == known_flags["STYPE_IPC"]:
157        flags.append("STYPE_IPC")
158    if (stype_value & known_flags["STYPE_SPECIAL"]) == known_flags["STYPE_SPECIAL"]:
159        flags.append("STYPE_SPECIAL")
160    if (stype_value & known_flags["STYPE_TEMPORARY"]) == known_flags["STYPE_TEMPORARY"]:
161        flags.append("STYPE_TEMPORARY")
162    return flags

Extracts the share type flags from a given share type value.

This function uses bitwise operations to determine which share type flags are set in the provided stype_value. It checks against known share type flags and returns a list of the flags that are set.

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

Returns: list: A list of strings, where each string represents a share type flag that is set in the input value.

def windows_ls_entry(entry, config, pathToPrint=None):
165def windows_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    
176    if pathToPrint is not None:
177        pathToPrint = pathToPrint + ntpath.sep + entry.get_longname()
178    else:
179        pathToPrint = entry.get_longname()
180
181    meta_string = ""
182    meta_string += ("d" if entry.is_directory() else "-")
183    meta_string += ("a" if entry.is_archive() else "-")
184    meta_string += ("c" if entry.is_compressed() else "-")
185    meta_string += ("h" if entry.is_hidden() else "-")
186    meta_string += ("n" if entry.is_normal() else "-")
187    meta_string += ("r" if entry.is_readonly() else "-")
188    meta_string += ("s" if entry.is_system() else "-")
189    meta_string += ("t" if entry.is_temporary() else "-")
190
191    size_str = b_filesize(entry.get_filesize())
192
193    date_str = datetime.datetime.fromtimestamp(entry.get_atime_epoch()).strftime("%Y-%m-%d %H:%M")
194    
195    if entry.is_directory():
196        if config.no_colors:
197            print("%s %10s  %s  %s\\" % (meta_string, size_str, date_str, pathToPrint))
198        else:
199            print("%s %10s  %s  \x1b[1;96m%s\x1b[0m\\" % (meta_string, size_str, date_str, pathToPrint))
200    else:
201        if config.no_colors:
202            print("%s %10s  %s  %s" % (meta_string, size_str, date_str, pathToPrint))
203        else:
204            print("%s %10s  %s  \x1b[1m%s\x1b[0m" % (meta_string, size_str, date_str, pathToPrint))

This function generates a metadata string based on the attributes of the provided entry object.

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

Returns: str: A string representing the metadata of the entry, including attributes like directory, archive, compressed, hidden, normal, readonly, system, and temporary.

def local_tree(path, config):
207def local_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
219    def recurse_action(base_dir="", path=[], prompt=[]):
220        bars = ["│   ", "├── ", "└── "]
221
222        local_path = os.path.normpath(base_dir + os.path.sep + os.path.sep.join(path) + os.path.sep)
223
224        entries = []
225        try:
226            entries = os.listdir(local_path)
227        except Exception as err:
228            if config.no_colors:
229                print("%s%s" % (''.join(prompt+[bars[2]]), err))
230            else:
231                print("%s\x1b[1;91m%s\x1b[0m" % (''.join(prompt+[bars[2]]), err))
232            return 
233
234        entries = sorted(entries)
235
236        # 
237        if len(entries) > 1:
238            index = 0
239            for entry in entries:
240                index += 1
241                # This is the first entry 
242                if index == 0:
243                    if os.path.isdir(local_path + os.path.sep + entry):
244                        if config.no_colors:
245                            print("%s%s%s" % (''.join(prompt+[bars[1]]), entry, os.path.sep))
246                        else:
247                            print("%s\x1b[1;96m%s\x1b[0m%s" % (''.join(prompt+[bars[1]]), entry, os.path.sep))
248                        recurse_action(
249                            base_dir=base_dir, 
250                            path=path+[entry],
251                            prompt=prompt+["│   "]
252                        )
253                    else:
254                        if config.no_colors:
255                            print("%s%s" % (''.join(prompt+[bars[1]]), entry))
256                        else:
257                            print("%s\x1b[1m%s\x1b[0m" % (''.join(prompt+[bars[1]]), entry))
258
259                # This is the last entry
260                elif index == len(entries):
261                    if os.path.isdir(local_path + os.path.sep + entry):
262                        if config.no_colors:
263                            print("%s%s%s" % (''.join(prompt+[bars[2]]), entry, os.path.sep))
264                        else:
265                            print("%s\x1b[1;96m%s\x1b[0m%s" % (''.join(prompt+[bars[2]]), entry, os.path.sep))
266                        recurse_action(
267                            base_dir=base_dir, 
268                            path=path+[entry],
269                            prompt=prompt+["    "]
270                        )
271                    else:
272                        if config.no_colors:
273                            print("%s%s" % (''.join(prompt+[bars[2]]), entry))
274                        else:
275                            print("%s\x1b[1m%s\x1b[0m" % (''.join(prompt+[bars[2]]), entry))
276                    
277                # These are entries in the middle
278                else:
279                    if os.path.isdir(local_path + os.path.sep + entry):
280                        if config.no_colors:
281                            print("%s%s%s" % (''.join(prompt+[bars[1]]), entry, os.path.sep))
282                        else:
283                            print("%s\x1b[1;96m%s\x1b[0m%s" % (''.join(prompt+[bars[1]]), entry, os.path.sep))
284                        recurse_action(
285                            base_dir=base_dir, 
286                            path=path+[entry],
287                            prompt=prompt+["│   "]
288                        )
289                    else:
290                        if config.no_colors:
291                            print("%s%s" % (''.join(prompt+[bars[1]]), entry))
292                        else:
293                            print("%s\x1b[1m%s\x1b[0m" % (''.join(prompt+[bars[1]]), entry))
294
295        # 
296        elif len(entries) == 1:
297            entry = entries[0]
298            if os.path.isdir(local_path + os.path.sep + entry):
299                if config.no_colors:
300                    print("%s%s%s" % (''.join(prompt+[bars[2]]), entry, os.path.sep))
301                else:
302                    print("%s\x1b[1;96m%s\x1b[0m%s" % (''.join(prompt+[bars[2]]), entry, os.path.sep))
303                recurse_action(
304                    base_dir=base_dir, 
305                    path=path+[entry],
306                    prompt=prompt+["    "]
307                )
308            else:
309                if config.no_colors:
310                    print("%s%s" % (''.join(prompt+[bars[2]]), entry))
311                else:
312                    print("%s\x1b[1m%s\x1b[0m" % (''.join(prompt+[bars[2]]), entry))
313
314    # Entrypoint
315    try:
316        if config.no_colors:
317            print("%s%s" % (path, os.path.sep))
318        else:
319            print("\x1b[1;96m%s\x1b[0m%s" % (path, os.path.sep))
320        recurse_action(
321            base_dir=os.getcwd(),
322            path=[path],
323            prompt=[""]
324        )
325    except (BrokenPipeError, KeyboardInterrupt) as e:
326        print("[!] Interrupted.")

This function recursively lists the contents of a directory in a tree-like format.

Parameters: path (str): The path to the directory to list. config (object): Configuration settings which may affect the output, such as whether to use colors.

Returns: None: This function does not return anything but prints the directory tree to the console.

def resolve_local_files(arguments):
329def resolve_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
344    resolved_files = []
345    for arg in arguments:
346        if '*' in arg:
347            try:
348                path = os.path.dirname(arg) or '.'
349                pattern = os.path.basename(arg)
350                for entry in os.listdir(path):
351                    if fnmatch.fnmatch(entry, pattern):
352                        resolved_files.append(os.path.join(path, entry))
353            except FileNotFoundError as err:
354                pass
355        else:
356            resolved_files.append(arg)
357    resolved_files = sorted(list(set(resolved_files)))
358    return resolved_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.

def resolve_remote_files(smbSession, arguments):
361def resolve_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
377    resolved_files = []
378    for arg in arguments:
379        if '*' in arg:
380            if arg == '*':
381                path = smbSession.smb_cwd
382            elif arg.startswith(ntpath.sep):
383                path = ntpath.dirname(arg)
384            else:
385                path = ntpath.normpath(smbSession.smb_cwd + ntpath.sep + ntpath.dirname(arg))
386
387            try:
388                contents = smbSession.smbClient.listPath(
389                    shareName=smbSession.smb_share,
390                    path=path + ntpath.sep + '*'
391                )
392                contents = [e for e in contents if e.get_longname() not in ['.', '..']]
393
394                for entry in contents:
395                    if fnmatch.fnmatch(entry.get_longname(), ntpath.basename(arg)):
396                        resolved_files.append(ntpath.join(path, entry.get_longname()))
397
398            except Exception as err:
399                pass
400        else:
401            resolved_files.append(arg)
402    resolved_files = sorted(list(set(resolved_files)))
403    return resolved_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.

def is_port_open(target, port) -> bool:
406def is_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
422    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
423        s.settimeout(0.1)
424        # Non-existant domains cause a lot of errors, added error handling
425        try:
426            return s.connect_ex((target, port)) == 0
427        except Exception as e:
428            return False

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.