Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Module extract #73

Merged
merged 2 commits into from
Jun 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ python = "^3.7"
impacket = "^0.10.0"
rich = "^13.0.0"
charset-normalizer = "^3.3.2"
pefile = "2023.2.7"

[tool.poetry.dev-dependencies]

Expand Down
3 changes: 2 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
impacket
rich
charset-normalizer
charset-normalizer
pefile
4 changes: 3 additions & 1 deletion smbclientng/core/InteractiveShell.py
Original file line number Diff line number Diff line change
Expand Up @@ -1068,8 +1068,10 @@ def __load_modules(self):
module_file = import_module('smbclientng.modules.%s' % (file[:-3]))
module = module_file.__getattribute__(file[:-3])
self.modules[module.name.lower()] = module
except AttributeError as e:
except AttributeError as err:
pass
except ImportError as err:
self.logger.debug("Could not load module '%s': %s" % ((file[:-3]), err))

if self.config.debug:
if len(self.modules.keys()) == 0:
Expand Down
14 changes: 7 additions & 7 deletions smbclientng/core/LocalFileIO.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,23 +32,23 @@ def __init__(self, mode, path=None, expected_size=None, keepRemotePath=False, lo
self.logger = logger
self.mode = mode
# Convert remote path format to local operating system path format
self.path = path.replace(ntpath.sep, os.path.sep)
self.path = os.path.normpath(path.replace(ntpath.sep, os.path.sep))
self.dir = None
self.debug = False
self.expected_size = expected_size
self.keepRemotePath = keepRemotePath

# Write to local (read remote)
if self.mode in ["wb"]:
self.dir = '.' + os.path.sep
if keepRemotePath:
self.dir += os.path.dirname(self.path)
self.dir = os.path.dirname(self.path)
else:
self.dir = '.' + os.path.sep

if not os.path.exists(self.dir):
self.logger.debug("[debug] Creating local directory '%s'" % self.dir)
self.logger.debug("Creating local directory '%s'" % self.dir)
os.makedirs(self.dir)

self.logger.debug("[debug] Openning local '%s' with mode '%s'" % (self.path, self.mode))
self.logger.debug("Openning local '%s' with mode '%s'" % (self.path, self.mode))

try:
self.fd = open(self.dir + os.path.sep + os.path.basename(self.path), self.mode)
Expand All @@ -60,7 +60,7 @@ def __init__(self, mode, path=None, expected_size=None, keepRemotePath=False, lo
if ntpath.sep in self.path:
self.dir = os.path.dirname(self.path)

self.logger.debug("[debug] Openning local '%s' with mode '%s'" % (self.path, self.mode))
self.logger.debug("Openning local '%s' with mode '%s'" % (self.path, self.mode))

try:
self.fd = open(self.path, self.mode)
Expand Down
39 changes: 22 additions & 17 deletions smbclientng/core/SMBSession.py
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ def recurse_action(paths=[], depth=0, callback=None):
else:
self.logger.error("SMBSession.find(), callback function cannot be None.")

def get_file(self, path=None, keepRemotePath=False):
def get_file(self, path=None, keepRemotePath=False, localDownloadDir="./"):
"""
Retrieves a file from the specified path on the SMB share.

Expand Down Expand Up @@ -312,14 +312,14 @@ def get_file(self, path=None, keepRemotePath=False):
else:
try:
if ntpath.sep in path:
outputfile = ntpath.dirname(path) + ntpath.sep + entry.get_longname()
outputfile = localDownloadDir + os.path.sep + ntpath.dirname(path) + os.path.sep + entry.get_longname()
else:
outputfile = entry.get_longname()
outputfile = localDownloadDir + os.path.sep + entry.get_longname()
f = LocalFileIO(
mode="wb",
path=outputfile,
expected_size=entry.get_filesize(),
debug=self.config.debug,
logger=self.logger,
keepRemotePath=keepRemotePath
)
self.smbClient.getFile(
Expand All @@ -336,7 +336,7 @@ def get_file(self, path=None, keepRemotePath=False):

return None

def get_file_recursively(self, path=None):
def get_file_recursively(self, path=None, localDownloadDir="./"):
"""
Recursively retrieves files from a specified path on the SMB share.

Expand All @@ -350,37 +350,40 @@ def get_file_recursively(self, path=None):
If None, it starts from the root of the configured SMB share.
"""

def recurse_action(base_dir="", path=[]):
def recurse_action(base_dir="", path=[], localDownloadDir="./"):
if len(base_dir) == 0:
remote_smb_path = ntpath.sep.join(path)
remote_smb_fullpath = ntpath.sep.join(path)
else:
remote_smb_path = base_dir + ntpath.sep + ntpath.sep.join(path)
remote_smb_path = ntpath.normpath(remote_smb_path)
remote_smb_fullpath = base_dir + ntpath.sep + ntpath.sep.join(path)
remote_smb_fullpath = ntpath.normpath(remote_smb_fullpath)

remote_smb_relativepath = ntpath.normpath(ntpath.sep.join(path))

entries = self.smbClient.listPath(
shareName=self.smb_share,
path=remote_smb_path + '\\*'
path=remote_smb_fullpath + ntpath.sep + '*'
)
if len(entries) != 0:
files = [entry for entry in entries if not entry.is_directory()]
directories = [entry for entry in entries if entry.is_directory() and entry.get_longname() not in [".", ".."]]

# Files
if len(files) != 0:
self.logger.print("[>] Retrieving files of '%s'" % remote_smb_path)
self.logger.print("[>] Retrieving files of '%s'" % remote_smb_relativepath)
for entry_file in files:
if not entry_file.is_directory():
downloadToPath = localDownloadDir + os.path.sep + remote_smb_relativepath + os.path.sep + entry_file.get_longname()
f = LocalFileIO(
mode="wb",
path=remote_smb_path + ntpath.sep + entry_file.get_longname(),
path=downloadToPath,
expected_size=entry_file.get_filesize(),
keepRemotePath=True,
debug=self.config.debug
logger=self.logger
)
try:
self.smbClient.getFile(
shareName=self.smb_share,
pathName=remote_smb_path + ntpath.sep + entry_file.get_longname(),
pathName=(remote_smb_fullpath + ntpath.sep + entry_file.get_longname()),
callback=f.write
)
f.close()
Expand All @@ -396,14 +399,16 @@ def recurse_action(base_dir="", path=[]):
for entry_directory in directories:
if entry_directory.is_directory():
recurse_action(
base_dir=self.smb_cwd,
path=path+[entry_directory.get_longname()]
base_dir=self.smb_cwd,
path=path+[entry_directory.get_longname()],
localDownloadDir=localDownloadDir
)
# Entrypoint
try:
recurse_action(
base_dir=self.smb_cwd,
path=[path]
path=[path],
localDownloadDir=localDownloadDir
)
except (BrokenPipeError, KeyboardInterrupt) as e:
print("\x1b[v\x1b[o\r[!] Interrupted.")
Expand Down
126 changes: 126 additions & 0 deletions smbclientng/modules/Extract.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# File name : Extract.py
# Author : Podalirius (@podalirius_)
# Date created : 23 may 2024

import os
import pefile
import shutil
import tempfile
import zipfile
from smbclientng.core.Module import Module
from smbclientng.core.ModuleArgumentParser import ModuleArgumentParser


def pe_get_version(pathtopefile):
data = {"FileVersion": "", "ProductVersion": ""}
p = pefile.PE(pathtopefile)
data["FileVersion"] = "%d.%d.%d.%d" % (
(p.VS_FIXEDFILEINFO[0].FileVersionMS >> 16) & 0xffff,
(p.VS_FIXEDFILEINFO[0].FileVersionMS >> 0) & 0xffff,
(p.VS_FIXEDFILEINFO[0].FileVersionLS >> 16) & 0xffff,
(p.VS_FIXEDFILEINFO[0].FileVersionLS >> 0) & 0xffff
)
data["ProductVersion"] = "%d.%d.%d.%d" % (
(p.VS_FIXEDFILEINFO[0].ProductVersionMS >> 16) & 0xffff,
(p.VS_FIXEDFILEINFO[0].ProductVersionMS >> 0) & 0xff,
(p.VS_FIXEDFILEINFO[0].ProductVersionLS >> 16) & 0xffff,
(p.VS_FIXEDFILEINFO[0].ProductVersionLS >> 0) & 0xffff
)
return data


class Extract(Module):

name = "extract"
description = "Extracts interesting files of a remote system."

def parseArgs(self, arguments):
"""
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.
"""

parser = ModuleArgumentParser(prog=self.name, description=self.description)

parser.add_argument("targets", metavar="target", type=str, nargs="*", default=[], help="The path(s) to the file(s) to extract.")

parser.add_argument("-v", "--verbose", dest="verbose", action="store_true", default=False, help="Verbose mode.")
parser.add_argument("-o", "--outputdir", dest="outputdir", default=os.getcwd(), help="Output directory.")

self.options = self.processArguments(parser, arguments)

if self.options is not None:
if len(self.options.targets) == 0:
parser.print_help()
self.options = None

return self.options

def saveSpooler(self):
files = [
r".\spoolss.dll",
r".\spoolsv.exe",
r".\winspool.drv",
r".\en-US\spoolsv.exe.mui",
r".\en-US\winspool.drv.mui"
]

# Save old share
old_share = self.smbSession.smb_share
old_pwd = self.smbSession.smb_cwd

temp_dir = tempfile.mkdtemp()
self.logger.debug("Using temporary local directory '%s'" % temp_dir)
self.smbSession.set_share('C$')
if self.smbSession.path_isdir("/Windows/System32/"):
self.smbSession.set_cwd("/Windows/System32/")
for f in files:
self.smbSession.get_file(path=f, keepRemotePath=True, localDownloadDir=temp_dir)
self.smbSession.get_file_recursively(path="spool/", localDownloadDir=temp_dir)

# Create a zipfile of the temp_dir
pev = pe_get_version(temp_dir + os.path.sep + "spoolsv.exe")
outputfile = '%s-spooler.zip' % pev["FileVersion"]
zip_file_path = os.path.join(self.options.outputdir, outputfile)
self.logger.info("Zipping files downloaded in '%s'" % temp_dir)
with zipfile.ZipFile(zip_file_path, 'w', zipfile.ZIP_DEFLATED) as zipf:
for root, dirs, files in os.walk(temp_dir):
for file in files:
self.logger.print(os.path.join(root, file).replace(temp_dir+os.path.sep, "├──> ", 1))
zipf.write(os.path.join(root, file), os.path.relpath(os.path.join(root, file), temp_dir))
self.logger.info(f"Backup saved to {zip_file_path}")

if os.path.exists(temp_dir):
shutil.rmtree(temp_dir)

# Restore old share
self.smbSession.set_share(old_share)
self.smbSession.set_cwd(old_pwd)

#=[Run]====================================================================

def run(self, arguments):
self.options = self.parseArgs(arguments=arguments)

if self.options is not None:
# Entrypoint
try:
for t in self.options.targets:
if t == "spooler":
self.saveSpooler()
except (BrokenPipeError, KeyboardInterrupt) as e:
print("[!] Interrupted.")
self.smbSession.close_smb_session()
self.smbSession.init_smb_session()



3 changes: 0 additions & 3 deletions smbclientng/modules/Users.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,8 @@
# Date created : 23 may 2024


import ntpath
import re
from smbclientng.core.Module import Module
from smbclientng.core.ModuleArgumentParser import ModuleArgumentParser
from smbclientng.core.utils import windows_ls_entry


class Users(Module):
Expand Down
Loading