Created
November 25, 2025 13:57
-
-
Save jakobfriedl/e33329cad0aac995e198ee406d96f75f to your computer and use it in GitHub Desktop.
Extract one hash per username from NTLMv1/NTLMv2 hashes collected by Responder.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env python3 | |
| """ | |
| NTLMv2 Hash Deduplicator | |
| Finds the first occurrence of each user from a file containing NTLMv2 hashes | |
| and writes unique entries to a new file. | |
| """ | |
| import sys | |
| import os | |
| from collections import OrderedDict | |
| def extract_username_from_hash(hash_line): | |
| """ | |
| Extract username from NTLMv2 hash line. | |
| NTLMv2 format: username::domain:challenge:response | |
| """ | |
| try: | |
| parts = hash_line.strip().split(':') | |
| if len(parts) >= 4: | |
| return parts[0].lower() # Username is the first part, normalize to lowercase | |
| except: | |
| pass | |
| return None | |
| def process_ntlmv2_file(input_file, output_file): | |
| """ | |
| Process NTLMv2 hash file and extract first occurrence of each user. | |
| """ | |
| seen_users = OrderedDict() | |
| processed_count = 0 | |
| duplicate_count = 0 | |
| try: | |
| with open(input_file, 'r', encoding='utf-8', errors='ignore') as infile: | |
| for line_num, line in enumerate(infile, 1): | |
| line = line.strip() | |
| # Skip empty lines | |
| if not line: | |
| continue | |
| username = extract_username_from_hash(line) | |
| if username: | |
| if username not in seen_users: | |
| seen_users[username] = line | |
| processed_count += 1 | |
| print(f"Found user: {username}") | |
| else: | |
| duplicate_count += 1 | |
| print(f"Duplicate found for user: {username} (line {line_num})") | |
| else: | |
| print(f"Warning: Could not parse line {line_num}: {line[:50]}...") | |
| # Write unique hashes to output file | |
| with open(output_file, 'w', encoding='utf-8') as outfile: | |
| for hash_line in seen_users.values(): | |
| outfile.write(hash_line + '\n') | |
| print(f"\nProcessing complete!") | |
| print(f"Unique users found: {processed_count}") | |
| print(f"Duplicates removed: {duplicate_count}") | |
| print(f"Output written to: {output_file}") | |
| except FileNotFoundError: | |
| print(f"Error: Input file '{input_file}' not found.") | |
| sys.exit(1) | |
| except Exception as e: | |
| print(f"Error processing file: {e}") | |
| sys.exit(1) | |
| def main(): | |
| if len(sys.argv) != 3: | |
| print("Usage: python ntlmv2_dedup.py <input_file> <output_file>") | |
| print("Example: python ntlmv2_dedup.py hashes.txt unique_hashes.txt") | |
| sys.exit(1) | |
| input_file = sys.argv[1] | |
| output_file = sys.argv[2] | |
| # Check if input file exists | |
| if not os.path.exists(input_file): | |
| print(f"Error: Input file '{input_file}' does not exist.") | |
| sys.exit(1) | |
| # Confirm overwrite if output file exists | |
| if os.path.exists(output_file): | |
| response = input(f"Output file '{output_file}' already exists. Overwrite? (y/N): ") | |
| if response.lower() != 'y': | |
| print("Operation cancelled.") | |
| sys.exit(0) | |
| process_ntlmv2_file(input_file, output_file) | |
| if __name__ == "__main__": | |
| main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment