diff --git a/nxc/connection.py b/nxc/connection.py index 10e95cfe..96fda170 100755 --- a/nxc/connection.py +++ b/nxc/connection.py @@ -14,7 +14,7 @@ from socket import AF_UNSPEC, SOCK_DGRAM, IPPROTO_IP, AI_CANONNAME, getaddrinfo from nxc.config import pwned_label from nxc.helpers.logger import highlight -from nxc.loaders.moduleloader import ModuleLoader +from nxc.loaders.moduleloader import ModuleLoader, ModuleOptionsError from nxc.logger import nxc_logger, NXCAdapter from nxc.context import Context from nxc.paths import NXC_PATH @@ -604,5 +604,9 @@ class connection: self.modules = [] for module_path in self.module_paths: - module = loader.init_module(module_path) + try: + module = loader.init_module(module_path) + except ModuleOptionsError as e: + self.logger.debug(f"Skipping module: {e}") + continue self.modules.append(module) diff --git a/nxc/loaders/moduleloader.py b/nxc/loaders/moduleloader.py index c393e7b1..c45e1584 100755 --- a/nxc/loaders/moduleloader.py +++ b/nxc/loaders/moduleloader.py @@ -13,6 +13,10 @@ from nxc.logger import NXCAdapter from nxc.paths import NXC_PATH +class ModuleOptionsError(Exception): + """Raised when a module aborts the run from options() (returned False).""" + + class ModuleLoader: def __init__(self, args, db, logger): self.args = args @@ -100,7 +104,8 @@ class ModuleLoader: key, value = option.split("=", 1) module_options[str(key).upper()] = value - module.options(context, module_options) + if module.options(context, module_options) is False: + raise ModuleOptionsError(f"Module {module.name} aborted the run from options()") return module else: self.logger.fail(f"Module {module.name.upper()} is not supported for protocol {self.args.protocol}") diff --git a/nxc/modules/daclread.py b/nxc/modules/daclread.py index d591378b..950b143c 100644 --- a/nxc/modules/daclread.py +++ b/nxc/modules/daclread.py @@ -249,10 +249,10 @@ class NXCModule: context.log.debug("There is a target specified!") if isfile(module_options[option]): try: - target_file = open(module_options[option]) # ruff: ignore[open-file-with-context-handler] - for line in target_file: - context.log.debug(f"Adding target from file: {line}") - self.targets.append((line.strip(), SEARCH_FILTERS[option])) + with open(module_options[option]) as target_file: + for line in target_file: + context.log.debug(f"Adding target from file: {line}") + self.targets.append((line.strip(), SEARCH_FILTERS[option])) except Exception: context.log.fail("The file doesn't exist or cannot be opened.") else: diff --git a/nxc/modules/example_module.py b/nxc/modules/example_module.py index 7c3d9479..f7adfdfb 100644 --- a/nxc/modules/example_module.py +++ b/nxc/modules/example_module.py @@ -22,6 +22,7 @@ class NXCModule: Module options get parsed here. Additionally, put the modules usage here as well """ # Put "No options available" in the docstring if there are no options for the module + # Return False to abort the run if options are invalid (log with context.log.fail first). def on_login(self, context, connection): """Concurrent. diff --git a/nxc/modules/handlekatz.py b/nxc/modules/handlekatz.py index 4fa5c44e..5455c7b8 100644 --- a/nxc/modules/handlekatz.py +++ b/nxc/modules/handlekatz.py @@ -126,20 +126,17 @@ class NXCModule: except Exception as e: context.log.fail(f"[OPSEC] Error deleting lsass.dmp file on share {self.share}: {e}") - h_in = open(self.dir_result + machine_name, "rb") # ruff: ignore[open-file-with-context-handler] - h_out = open(self.dir_result + machine_name + ".decode", "wb") # ruff: ignore[open-file-with-context-handler] + with open(self.dir_result + machine_name, "rb") as h_in, open(self.dir_result + machine_name + ".decode", "wb") as h_out: + bytes_in = bytearray(h_in.read()) - bytes_in = bytearray(h_in.read()) - bytes_in_len = len(bytes_in) + context.log.display(f"Deobfuscating, this might take a while (size: {len(bytes_in)} bytes)") - context.log.display(f"Deobfuscating, this might take a while (size: {bytes_in_len} bytes)") + chunks = [bytes_in[i: i + 1000000] for i in range(0, len(bytes_in), 1000000)] + for chunk in chunks: + for i in range(len(chunk)): + chunk[i] ^= 0x41 - chunks = [bytes_in[i: i + 1000000] for i in range(0, bytes_in_len, 1000000)] - for chunk in chunks: - for i in range(len(chunk)): - chunk[i] ^= 0x41 - - h_out.write(bytes(chunk)) + h_out.write(bytes(chunk)) with open(self.dir_result + machine_name + ".decode", "rb") as dump: try: diff --git a/nxc/netexec.py b/nxc/netexec.py index c1f7e3fc..ed379157 100644 --- a/nxc/netexec.py +++ b/nxc/netexec.py @@ -8,7 +8,7 @@ from nxc.parsers.nmap import parse_nmap_xml from nxc.parsers.nessus import parse_nessus_file from nxc.cli import gen_cli_args from nxc.loaders.protocolloader import ProtocolLoader -from nxc.loaders.moduleloader import ModuleLoader +from nxc.loaders.moduleloader import ModuleLoader, ModuleOptionsError from nxc.first_run import first_run_setup from nxc.paths import NXC_PATH, WORKSPACE_DIR from nxc.console import nxc_console @@ -207,7 +207,11 @@ def main(): exit(1) nxc_logger.debug(f"Loading module for sanity check {m} at path {modules[m]['path']}") - module = loader.init_module(modules[m]["path"]) + try: + loader.init_module(modules[m]["path"]) + except ModuleOptionsError as e: + nxc_logger.debug(f"Aborting run: {e}") + exit(1) # Add modules paths to the protocol object so it can load them itself proto_module_paths.append(modules[m]["path"]) diff --git a/nxc/protocols/ftp.py b/nxc/protocols/ftp.py index becbb2e7..39c27f9e 100644 --- a/nxc/protocols/ftp.py +++ b/nxc/protocols/ftp.py @@ -128,7 +128,8 @@ class ftp(connection): # Check if the file exists self.conn.size(filename) # Attempt to download the file - self.conn.retrbinary(f"RETR {filename}", open(downloaded_file, "wb").write) # ruff: ignore[open-file-with-context-handler] + with open(downloaded_file, "wb") as f: + self.conn.retrbinary(f"RETR {filename}", f.write) except error_perm as error_message: self.logger.fail(f"Failed to download the file. Response: ({error_message})") self.conn.close() @@ -146,7 +147,8 @@ class ftp(connection): def put_file(self, local_file, remote_file): try: # Attempt to upload the file - self.conn.storbinary(f"STOR {remote_file}", open(local_file, "rb")) # ruff: ignore[open-file-with-context-handler] + with open(local_file, "rb") as f: + self.conn.storbinary(f"STOR {remote_file}", f.read) except error_perm as error_message: self.logger.fail(f"Failed to upload file. Response: ({error_message})") return False diff --git a/nxc/protocols/smb.py b/nxc/protocols/smb.py index 40773269..19c96d1b 100755 --- a/nxc/protocols/smb.py +++ b/nxc/protocols/smb.py @@ -2314,7 +2314,8 @@ class smb(connection): if self.args.pvk is not None: try: - self.pvkbytes = open(self.args.pvk, "rb").read() # ruff: ignore[open-file-with-context-handler] + with open(self.args.pvk, "rb") as f: + self.pvkbytes = f.read() self.logger.success(f"Loading domain backupkey from {self.args.pvk}") except Exception as e: self.logger.fail(str(e))