diff --git a/nxc/database.py b/nxc/database.py index 382c12677c..3c86ce357e 100644 --- a/nxc/database.py +++ b/nxc/database.py @@ -204,10 +204,9 @@ def clear_database(self): self.db_execute(table.delete()) def db_execute(self, *args): - self.lock.acquire() - res = self.sess.execute(*args) - self.lock.release() - return res + with self.lock: + res = self.sess.execute(*args) + return res.freeze()() if res.returns_rows else res def get_keys(self, key_id=None, cred_id=None): """Return an empty list by default. Protocols that support keys (e.g. SSH) override this.""" diff --git a/nxc/modules/pre2k.py b/nxc/modules/pre2k.py index 9e8087bd7e..ecb78a6df4 100644 --- a/nxc/modules/pre2k.py +++ b/nxc/modules/pre2k.py @@ -53,8 +53,13 @@ def on_login(self, context, connection): computers[computer["sAMAccountName"]] = computer["userAccountControl"] context.log.debug(f"Added computer: {computer['sAMAccountName']}") - # Save computers to file - domain_dir = os.path.join(f"{NXC_PATH}/modules/pre2k", connection.domain) + import re + + # Sanitize domain to prevent directory traversal (Issue #1261) + safe_domain = re.sub(r'[^a-zA-Z0-9.-]', '_', connection.domain) + safe_domain = safe_domain.replace('..', '_') + + domain_dir = os.path.join(f"{NXC_PATH}/modules/pre2k", safe_domain) output_file_pre2k = os.path.join(domain_dir, "precreated_computers.txt") output_file_non_pre2k = os.path.join(domain_dir, "non_precreated_computers.txt") @@ -63,6 +68,12 @@ def on_login(self, context, connection): with open(output_file_pre2k, "w") as pre2k_file, open(output_file_non_pre2k, "w") as non_pre2k_file: for computer, uac in computers.items(): + # Validate sAMAccountName to prevent arbitrary file writes (Issue #1261) + invalid_chars = '"[]:;|=+*?<>/\\\\,' + if any(c in computer for c in invalid_chars): + context.log.debug(f"Skipping invalid sAMAccountName: {computer}") + continue + if int(uac) == 4128: pre2k_file.write(f"{computer}\n") else: