Repository: p0dalirius/DumpSMBShare Branch: master Commit: 1e5ceb1690d0 Files: 4 Total size: 14.0 KB Directory structure: gitextract_hi7mzzpz/ ├── .github/ │ └── FUNDING.yml ├── DumpSMBShare.py ├── README.md └── requirements.txt ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/FUNDING.yml ================================================ # These are supported funding model platforms github: p0dalirius patreon: Podalirius ================================================ FILE: DumpSMBShare.py ================================================ #!/usr/bin/env python3 # -*- coding: utf-8 -*- # File name : DumpSMBShare.py # Author : Podalirius (@podalirius_) # Date created : 6 Jul 2022 import argparse import os import sys import traceback from impacket import version from impacket.examples import logger, utils from impacket.smbconnection import SMBConnection, SMB2_DIALECT_002, SMB2_DIALECT_21, SMB_DIALECT, SessionError class BFSDumpShare(object): """docstring for BFSDumpShare.""" def __init__(self, smb, share, base_dir="", dump_dir=".", quiet=False, debug=False, only_list_files=False): super(BFSDumpShare, self).__init__() self.quiet = quiet self.debug = debug self.smb = smb self.share = share self.dump_dir = dump_dir self.base_dir = base_dir self.only_list_files = only_list_files if not os.path.exists(self.dump_dir): os.makedirs(self.dump_dir, exist_ok=True) def list_shares(self): print("[>] Listing shares ...") resp = self.smb.listShares() shares = [] for k in range(len(resp)): shares.append(resp[k]["shi1_netname"][:-1]) return shares def dump_share(self, targetfile="", extensions=[], base_dir=None): if base_dir is not None: self.base_dir = base_dir print("[>] Dumping files with extensions %s ... " % extensions) # Breadth-first search algorithm to recursively find .extension files files = [] searchdirs = [self.base_dir + "/"] while len(searchdirs) != 0: next_dirs = [] for sdir in searchdirs: if self.debug: print("[>] Searching in %s " % sdir) try: for sharedfile in self.smb.listPath(self.share, sdir + "*", password=None): if sharedfile.get_longname() not in [".", ".."]: if sharedfile.is_directory(): if self.debug: print("[>] Found directory %s/" % sharedfile.get_longname()) next_dirs.append(sdir + sharedfile.get_longname() + "/") else: if len(extensions) == 0 or any([sharedfile.get_longname().endswith("." + e) for e in extensions]) or sharedfile.get_longname() == targetfile: if self.debug or not self.quiet or self.only_list_files: print("[>] Found matching file %s" % (sdir + sharedfile.get_longname())) full_path = sdir + sharedfile.get_longname() files.append(full_path) if not self.only_list_files: self.dump_file(full_path) else: if self.debug: print("[>] Found file %s" % sharedfile.get_longname()) except SessionError as e: if self.debug: print("[error] %s " % e) searchdirs = next_dirs if self.debug: print("[>] Next iteration with %d folders." % len(next_dirs)) return files def dump_file(self, filepath, only_file=False): # Sanitize dir filepath = filepath.replace("\\", "/") _dir, _file = os.path.dirname(filepath), os.path.basename(filepath) if _dir.startswith("//"): _dir = _dir[2:] try: if only_file: if not os.path.exists(self.dump_dir): os.makedirs(self.dump_dir, exist_ok=True) path = self.dump_dir + "/" + _file else: # Create directory if _dir.startswith(self.base_dir.rstrip('/')): _dir = _dir[len(self.base_dir.rstrip('/')):].lstrip('/') if not os.path.exists(self.dump_dir + "/" + _dir + "/"): os.makedirs(self.dump_dir + "/" + _dir + "/", exist_ok=True) path = self.dump_dir + "/" + _dir + "/" + _file # Write file f = open(path, "wb") self.smb.getFile(self.share, filepath, f.write) f.close() return True except SessionError as e: if self.debug: print("[error] %s" % e) return False except Exception as e: raise def parse_args(): print("DumpSMBShare v1.3 - by Remi GASCOU (Podalirius)\n") parser = argparse.ArgumentParser(add_help=True, description="A script to dump files and folders remotely from a Windows SMB share.") parser.add_argument("target", action="store", help="[[domain/]username[:password]@]") group = parser.add_mutually_exclusive_group(required=True) group.add_argument("-s", "--share", type=str, default=None, help="SMB Share to dump") group.add_argument("-l", "--list-shares", default=False, action="store_true", help="Lists SMB shares on the remote machine.") parser.add_argument("-L", "--list-files", default=False, action="store_true", help="Lists all the files present in the SMB share.") parser.add_argument("-e", "--extensions", type=str, required=False, default="", help="Extensions") parser.add_argument("-D", "--dump-dir", type=str, required=False, default=None, help="Dump directory") parser.add_argument("-f", "--file", type=str, default=None, help="SMB file to dump") parser.add_argument("-B", "--base-dir", type=str, required=False, default="", help="Directory to search in (Default: /)") parser.add_argument("--debug", action="store_true", help="Turn on debug output. (Default: False)") parser.add_argument("-q", "--quiet", action="store_true", default=False, help="Turn DEBUG output ON") group = parser.add_argument_group("authentication") group.add_argument("-H", "--hashes", action="store", metavar="LMHASH:NTHASH", help="NTLM hashes, format is LMHASH:NTHASH") group.add_argument("--no-pass", action="store_true", help="Don't ask for password (useful for -k)") group.add_argument("-k", "--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") group.add_argument("-A", "--aesKey", action="store", metavar="hex key", help="AES key to use for Kerberos Authentication (128 or 256 bits)") group = parser.add_argument_group("connection") group.add_argument("--dc-ip", action="store", metavar="ip address", help="IP Address of the domain controller. If omitted it will use the domain part (FQDN) specified in the target parameter") group.add_argument("-I", "--target-ip", action="store", metavar="ip address", help="IP Address of the target machine. If omitted it will use whatever was specified as target. This is useful when target is the NetBIOS name and you cannot resolve it") group.add_argument("-P", "--port", choices=["139", "445"], nargs="?", default="445", metavar="destination port", help="Destination port to connect to SMB Server") if len(sys.argv) == 1: parser.print_help() sys.exit(1) return parser.parse_args() def parse_target(args): domain, username, password, address = utils.parse_target(args.target) if args.target_ip is None: args.target_ip = address if domain is None: domain = "" if password == "" and username != "" and args.hashes is None and args.no_pass is False and args.aesKey is None: from getpass import getpass password = getpass("Password:") if args.aesKey is not None: args.k = True if args.hashes is not None: lmhash, nthash = args.hashes.split(":") else: lmhash = "" nthash = "" return domain, username, password, address, lmhash, nthash def init_smb_session(args, domain, username, password, address, lmhash, nthash): smbClient = SMBConnection(address, args.target_ip, sess_port=int(args.port)) dialect = smbClient.getDialect() if dialect == SMB_DIALECT: if args.debug: print("[>] SMBv1 dialect used") elif dialect == SMB2_DIALECT_002: if args.debug: print("[>] SMBv2.0 dialect used") elif dialect == SMB2_DIALECT_21: if args.debug: print("[>] SMBv2.1 dialect used") else: if args.debug: print("[>] SMBv3.0 dialect used") if args.kerberos is True: smbClient.kerberosLogin(username, password, domain, lmhash, nthash, args.aesKey, args.dc_ip) else: smbClient.login(username, password, domain, lmhash, nthash) if smbClient.isGuestSession() > 0: if args.debug: print("[>] GUEST Session Granted") else: if args.debug: print("[>] USER Session Granted") return smbClient if __name__ == "__main__": args = parse_args() args.extensions = [e.strip() for e in args.extensions.strip().split(",") if len(e.strip()) != 0] domain, username, password, address, lmhash, nthash = parse_target(args) try: smbClient = init_smb_session(args, domain, username, password, address, lmhash, nthash) if args.list_shares: if args.dump_dir is None: g = BFSDumpShare(smbClient, args.share) else: g = BFSDumpShare(smbClient, args.share, dump_dir=args.dump_dir) shares = g.list_shares() for s in shares: print(" - %s" % s) print() else: if args.dump_dir is None: args.dump_dir = "./%s/%s/" % (address, args.share) g = BFSDumpShare(smbClient, args.share, base_dir=args.base_dir, dump_dir=args.dump_dir, quiet=args.quiet, debug=args.debug, only_list_files=args.list_files) if args.share in g.list_shares(): if args.file is not None: print("[+] Dumping file '%s' from share '%s'" % (args.file, args.share)) g.base_dir = os.path.basename(args.base_dir) g.dump_file(args.file, only_file=True) else: dumped_files = g.dump_share(extensions=args.extensions) if not args.list_files: print("[+] Dumped %d files from share '%s'" % (len(dumped_files), args.share)) else: print("[>] Cannot find share '%s'" % args.share) except Exception as e: raise e ================================================ FILE: README.md ================================================ ![](./.github/banner.png)

A python script to dump files and folders remotely from a Windows SMB share.
GitHub release (latest by date) YouTube Channel Subscribers

## Features - [x] Only list shares with `--list-shares`. - [x] Select only files with given extensions (with `--extensions`) or all files. - [x] Choose the local folder to dump to with `--dump-dir`. - [x] Select base folder to search from in the share with `--base-dir`. ## Usage ``` $ ./DumpSMBShare.py -h DumpSMBShare v1.3 - by Remi GASCOU (Podalirius) usage: Dump.py [-h] (-s SHARE | -l) [-e EXTENSIONS] [-D DUMP_DIR] [-f FILE] [-B BASE_DIR] [--debug] [-q] [-H LMHASH:NTHASH] [--no-pass] [-k] [-A hex key] [--dc-ip ip address] [-I ip address] [-P [destination port]] target positional arguments: target [[domain/]username[:password]@] optional arguments: -h, --help show this help message and exit -s SHARE, --share SHARE SMB Share to dump -l, --list-shares Lists SMB shares on the remote machine. -L, --list-files Lists all the files present in the SMB share. -e EXTENSIONS, --extensions EXTENSIONS Extensions -D DUMP_DIR, --dump-dir DUMP_DIR Dump directory -f FILE, --file FILE SMB file to dump -B BASE_DIR, --base-dir BASE_DIR Directory to search in (Default: /) --debug Turn on debug output. (Default: False) -q, --quiet Turn DEBUG output ON authentication: -H LMHASH:NTHASH, --hashes LMHASH:NTHASH NTLM hashes, format is LMHASH:NTHASH --no-pass Don't ask for password (useful for -k) -k, --kerberos 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 -A hex key, --aesKey hex key AES key to use for Kerberos Authentication (128 or 256 bits) connection: --dc-ip ip address IP Address of the domain controller. If omitted it will use the domain part (FQDN) specified in the target parameter -I ip address, --target-ip ip address IP Address of the target machine. If omitted it will use whatever was specified as target. This is useful when target is the NetBIOS name and you cannot resolve it -P [destination port], --port [destination port] Destination port to connect to SMB Server ``` ## Example + Dump all files from the `SYSVOL` share: ``` ./DumpSMBShare.py 'LAB.local/user2:Admin123@192.168.2.1' --debug ``` ![](./.github/example_verbose.png) ![](./.github/example.png) ## Contributing Pull requests are welcome. Feel free to open an issue if you want to add other features. ================================================ FILE: requirements.txt ================================================ impacket