From 57fb2d653ab596ee43e9917cd42e4bd204adbddc Mon Sep 17 00:00:00 2001 From: "Remi GASCOU (Podalirius)" <79218792+p0dalirius@users.noreply.github.com> Date: Fri, 7 Jun 2024 21:39:59 +0200 Subject: [PATCH 1/3] Created mount command, resolves #7 --- smbclientng/core/CommandCompleter.py | 7 ++++++ smbclientng/core/InteractiveShell.py | 33 ++++++++++++++++++++++++++++ smbclientng/core/SMBSession.py | 3 +++ 3 files changed, 43 insertions(+) diff --git a/smbclientng/core/CommandCompleter.py b/smbclientng/core/CommandCompleter.py index 82f1656..36558cd 100644 --- a/smbclientng/core/CommandCompleter.py +++ b/smbclientng/core/CommandCompleter.py @@ -201,6 +201,13 @@ class CommandCompleter(object): ], "subcommands": [] }, + "mount": { + "description": [ + "Creates a mount point of the remote share on the local machine.", + "Syntax: 'mount '" + ], + "subcommands": [] + }, "put": { "description": [ "Put a local file or directory in a remote directory.", diff --git a/smbclientng/core/InteractiveShell.py b/smbclientng/core/InteractiveShell.py index 61979bf..c31f446 100644 --- a/smbclientng/core/InteractiveShell.py +++ b/smbclientng/core/InteractiveShell.py @@ -222,6 +222,10 @@ def process_command(self, command, arguments=[]): elif command == "module": self.command_module(arguments, command) + # Creates a mount point of the remote share on the local machine + elif command == "mount": + self.command_mount(arguments, command) + # Reconnects the current SMB session elif command in ["connect", "reconnect"]: self.command_reconnect(arguments, command) @@ -640,6 +644,35 @@ def command_module(self, arguments, command): else: print("[!] Module '%s' does not exist." % module_name) + @command_arguments_required + @active_smb_connection_needed + @smb_share_is_set + def command_mount(self, arguments, command): + # Command arguments required : Yes + # Active SMB connection needed : Yes + # SMB share needed : Yes + + if len(arguments) == 2: + remote_path = arguments[0] + if not remote_path.startswith(ntpath.sep): + remote_path = self.smbSession.smb_cwd + ntpath.sep + remote_path + + local_mount_point = arguments[1] + + if self.config.debug: + print("[debug] Trying to mount remote '%s' onto local '%s'" % (remote_path, local_mount_point)) + + try: + self.smbSession.smbClient.createMountPoint( + self.smbSession.smb_tree_id, # Tree ID + remote_path, # Remote path + local_mount_point # Local path + ) + except (impacket.smbconnection.SessionError, impacket.smb3.SessionError) as e: + self.smbSession.smbClient.removeMountPoint(self.smbSession.smb_tree_id, remote_path) + else: + self.commandCompleterObject.print_help(command=command) + @command_arguments_required @active_smb_connection_needed @smb_share_is_set diff --git a/smbclientng/core/SMBSession.py b/smbclientng/core/SMBSession.py index f9944dd..5694bb1 100644 --- a/smbclientng/core/SMBSession.py +++ b/smbclientng/core/SMBSession.py @@ -65,6 +65,7 @@ def __init__(self, address, domain, username, password, lmhash, nthash, use_kerb self.available_shares = {} self.smb_share = None self.smb_cwd = "" + self.smb_tree_id = None self.list_shares() @@ -977,6 +978,8 @@ def set_share(self, shareName): if shareName.lower() in self.available_shares.keys(): # Doing this in order to keep the case of the share adevertised by the remote machine self.smb_share = self.available_shares[shareName.lower()]["name"] + # Connects the tree + self.smb_tree_id = self.smbClient.connectTree(self.smb_share) else: print("[!] Could not set share '%s', it does not exist remotely." % shareName) From b6054ef8e256fc125e3bf647f17e6e484e63d2a7 Mon Sep 17 00:00:00 2001 From: "Remi GASCOU (Podalirius)" <79218792+p0dalirius@users.noreply.github.com> Date: Fri, 7 Jun 2024 21:44:11 +0200 Subject: [PATCH 2/3] Created umount command, resolves #7 --- smbclientng/core/CommandCompleter.py | 19 +++++++++++++------ smbclientng/core/InteractiveShell.py | 17 +++++++++++++++++ 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/smbclientng/core/CommandCompleter.py b/smbclientng/core/CommandCompleter.py index 36558cd..5c154fb 100644 --- a/smbclientng/core/CommandCompleter.py +++ b/smbclientng/core/CommandCompleter.py @@ -257,17 +257,24 @@ class CommandCompleter(object): ], "subcommands": [] }, - "use": { + "tree": { "description": [ - "Use a SMB share.", - "Syntax: 'use '" + "Displays a tree view of the remote directories.", + "Syntax: 'tree [directory]'" ], "subcommands": [] }, - "tree": { + "umount": { "description": [ - "Displays a tree view of the remote directories.", - "Syntax: 'tree [directory]'" + "Removes a mount point of the remote share on the local machine.", + "Syntax: 'umount '" + ], + "subcommands": [] + }, + "use": { + "description": [ + "Use a SMB share.", + "Syntax: 'use '" ], "subcommands": [] }, diff --git a/smbclientng/core/InteractiveShell.py b/smbclientng/core/InteractiveShell.py index c31f446..b03c370 100644 --- a/smbclientng/core/InteractiveShell.py +++ b/smbclientng/core/InteractiveShell.py @@ -861,6 +861,23 @@ def command_tree(self, arguments, command): else: self.smbSession.tree(path=' '.join(arguments)) + @command_arguments_required + @active_smb_connection_needed + @smb_share_is_set + def command_umount(self, arguments, command): + # Command arguments required : Yes + # Active SMB connection needed : Yes + # SMB share needed : Yes + + remote_path = arguments[0] + if not remote_path.startswith(ntpath.sep): + remote_path = self.smbSession.smb_cwd + ntpath.sep + remote_path + + if self.config.debug: + print("[debug] Trying to unmount remote '%s'" % (remote_path)) + + self.smbSession.smbClient.removeMountPoint(self.smbSession.smb_tree_id, remote_path) + @command_arguments_required @active_smb_connection_needed def command_use(self, arguments, command): From df348b63b676fc2e7ef1685db1e01f6af0e0baaf Mon Sep 17 00:00:00 2001 From: "Remi GASCOU (Podalirius)" <79218792+p0dalirius@users.noreply.github.com> Date: Sat, 8 Jun 2024 12:41:52 +0200 Subject: [PATCH 3/3] Added mount and umount commands, resolves #7 --- smbclientng/core/InteractiveShell.py | 18 ++++------- smbclientng/core/SMBSession.py | 46 ++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 12 deletions(-) diff --git a/smbclientng/core/InteractiveShell.py b/smbclientng/core/InteractiveShell.py index b03c370..3a4e399 100644 --- a/smbclientng/core/InteractiveShell.py +++ b/smbclientng/core/InteractiveShell.py @@ -663,13 +663,9 @@ def command_mount(self, arguments, command): print("[debug] Trying to mount remote '%s' onto local '%s'" % (remote_path, local_mount_point)) try: - self.smbSession.smbClient.createMountPoint( - self.smbSession.smb_tree_id, # Tree ID - remote_path, # Remote path - local_mount_point # Local path - ) + self.smbSession.mount(local_mount_point, remote_path) except (impacket.smbconnection.SessionError, impacket.smb3.SessionError) as e: - self.smbSession.smbClient.removeMountPoint(self.smbSession.smb_tree_id, remote_path) + self.smbSession.umount(local_mount_point) else: self.commandCompleterObject.print_help(command=command) @@ -869,15 +865,13 @@ def command_umount(self, arguments, command): # Active SMB connection needed : Yes # SMB share needed : Yes - remote_path = arguments[0] - if not remote_path.startswith(ntpath.sep): - remote_path = self.smbSession.smb_cwd + ntpath.sep + remote_path + local_mount_point = arguments[0] if self.config.debug: - print("[debug] Trying to unmount remote '%s'" % (remote_path)) + print("[debug] Trying to unmount local mount point '%s'" % (local_mount_point)) + + self.smbSession.mount(local_mount_point) - self.smbSession.smbClient.removeMountPoint(self.smbSession.smb_tree_id, remote_path) - @command_arguments_required @active_smb_connection_needed def command_use(self, arguments, command): diff --git a/smbclientng/core/SMBSession.py b/smbclientng/core/SMBSession.py index 5694bb1..2661960 100644 --- a/smbclientng/core/SMBSession.py +++ b/smbclientng/core/SMBSession.py @@ -10,6 +10,7 @@ import ntpath import os import re +import sys import traceback from smbclientng.core.LocalFileIO import LocalFileIO from smbclientng.core.utils import b_filesize, STYPE_MASK @@ -566,6 +567,32 @@ def mkdir(self, path=None): else: pass + def mount(self, local_mount_point, remote_path): + + if not os.path.exists(local_mount_point): + pass + + if sys.platform.startswith('win'): + remote_path = remote_path.replace('/',ntpath.sep) + command = f"net use {local_mount_point} \\\\{self.address}\\{self.smb_share}\\{remote_path}" + + elif sys.platform.startswith('linux'): + remote_path = remote_path.replace(ntpath.sep,'/') + command = f"mount -t cifs //{self.address}/{self.smb_share}/{remote_path} {local_mount_point} -o username={self.username},password={self.password}" + + elif sys.platform.startswith('darwin'): + remote_path = remote_path.replace(ntpath.sep,'/') + command = f"mount_smbfs //{self.username}:{self.password}@{self.address}/{self.smb_share}/{remote_path} {local_mount_point}" + + else: + command = None + print("[!] Unsupported platform for mounting SMB share.") + + if command is not None: + if self.config.debug: + print("[debug] Executing: %s" % command) + os.system(command) + def path_exists(self, path=None): """ Checks if the specified path exists on the SMB share. @@ -957,6 +984,25 @@ def recurse_action(base_dir="", path=[], prompt=[]): self.close_smb_session() self.init_smb_session() + def umount(self, local_mount_point): + if os.path.exists(local_mount_point): + if sys.platform.startswith('win'): + command = f"net use {local_mount_point} /delete" + + elif sys.platform.startswith('linux') or sys.platform.startswith('darwin'): + command = f"umount {local_mount_point}" + + else: + command = None + print("[!] Unsupported platform for unmounting SMB share.") + + if command is not None: + if self.config.debug: + print("[debug] Executing: %s" % command) + os.system(command) + else: + print("[!] Cannot unmount a non existing path.") + # Setter / Getter def set_share(self, shareName):