175 lines
5.8 KiB
Python
175 lines
5.8 KiB
Python
import hashlib
|
|
import itertools
|
|
import time
|
|
import string
|
|
|
|
# Define character set: 0-9, a-z, A-Z, and special symbols
|
|
CHARACTERS = string.digits + string.ascii_lowercase + string.ascii_uppercase + '#$%^&*'
|
|
|
|
|
|
def generate_salted_sha256_hash(password, salt_bytes):
|
|
"""
|
|
Generate SHA256 hash of a password concatenated with salt.
|
|
|
|
Args:
|
|
password: The password string
|
|
salt_bytes: The salt as bytes
|
|
|
|
Returns:
|
|
SHA256 hash as hexadecimal string
|
|
"""
|
|
# Concatenate password bytes and salt bytes, then hash
|
|
combined = password.encode() + salt_bytes
|
|
return hashlib.sha256(combined).hexdigest()
|
|
|
|
|
|
def read_salted_password_database(filename):
|
|
"""
|
|
Read salted password database from a text file.
|
|
|
|
Args:
|
|
filename: Path to the file containing [username, salt, hash] entries
|
|
|
|
Returns:
|
|
List of [username, salt, hash] entries
|
|
"""
|
|
database = []
|
|
|
|
try:
|
|
with open(filename, 'r') as f:
|
|
for line in f:
|
|
line = line.strip()
|
|
if line and line.startswith('[') and line.endswith(']'):
|
|
# Remove brackets and split by comma
|
|
content = line[1:-1]
|
|
parts = content.split(',', 2) # Split into max 3 parts
|
|
if len(parts) == 3:
|
|
username = parts[0].strip()
|
|
salt_hex = parts[1].strip()
|
|
password_hash = parts[2].strip()
|
|
# Convert salt from hex string to bytes
|
|
salt_bytes = bytes.fromhex(salt_hex)
|
|
database.append([username, salt_bytes, password_hash])
|
|
|
|
print(f"Loaded {len(database)} accounts from {filename}")
|
|
return database
|
|
|
|
except FileNotFoundError:
|
|
print(f"Error: File '{filename}' not found.")
|
|
return []
|
|
except Exception as e:
|
|
print(f"Error reading file: {e}")
|
|
return []
|
|
|
|
|
|
def dictionary_attack_salted(password_database, N):
|
|
"""
|
|
Perform dictionary attack on salted password database.
|
|
|
|
Args:
|
|
password_database: List of [username, salt, hash] entries
|
|
N: Length of passwords to try
|
|
|
|
Returns:
|
|
Dictionary mapping usernames to cracked passwords
|
|
Time taken for the attack
|
|
"""
|
|
print(f"\n{'='*60}")
|
|
print(f"Starting Dictionary Attack on Salted Hashes")
|
|
print(f"Password length: {N}")
|
|
print(f"Character set size: {len(CHARACTERS)}")
|
|
print(f"Total possible passwords: {len(CHARACTERS)**N}")
|
|
print(f"Number of accounts to crack: {len(password_database)}")
|
|
print(f"{'='*60}\n")
|
|
|
|
cracked_passwords = {}
|
|
start_time = time.time()
|
|
attempts = 0
|
|
|
|
# For each user, we need to try all possible passwords with their specific salt
|
|
for username, salt_bytes, stored_hash in password_database:
|
|
print(f"Attacking {username} (salt: {salt_bytes.hex()})...")
|
|
user_cracked = False
|
|
user_attempts = 0
|
|
|
|
# Generate all possible passwords of length N
|
|
for password_tuple in itertools.product(CHARACTERS, repeat=N):
|
|
password = ''.join(password_tuple)
|
|
user_attempts += 1
|
|
attempts += 1
|
|
|
|
# Hash the password with this user's salt
|
|
password_hash = generate_salted_sha256_hash(password, salt_bytes)
|
|
|
|
# Check if this hash matches the stored hash
|
|
if password_hash == stored_hash:
|
|
cracked_passwords[username] = password
|
|
print(f"[+] Cracked! {username}: {password} (after {user_attempts} attempts)")
|
|
user_cracked = True
|
|
break
|
|
|
|
# Progress indicator for this user
|
|
if user_attempts % 10000 == 0:
|
|
print(f" Tried {user_attempts} passwords for {username}...")
|
|
|
|
if not user_cracked:
|
|
print(f"[-] Failed to crack {username}")
|
|
|
|
end_time = time.time()
|
|
time_taken = end_time - start_time
|
|
|
|
print(f"\n{'='*60}")
|
|
print(f"Attack Complete!")
|
|
print(f"Time taken: {time_taken:.4f} seconds")
|
|
print(f"Total attempts: {attempts}")
|
|
print(f"Passwords cracked: {len(cracked_passwords)}/{len(password_database)}")
|
|
print(f"{'='*60}\n")
|
|
|
|
return cracked_passwords, time_taken
|
|
|
|
|
|
def main():
|
|
"""Main function to run the dictionary attack on salted passwords."""
|
|
print("Dictionary Attack on Salted SHA256 Password Hashes")
|
|
print("=" * 60)
|
|
|
|
# Get input file and password length from user
|
|
filename = input("Enter password database filename: ")
|
|
N = int(input("Enter password length N: "))
|
|
|
|
# Warn if N is too large
|
|
if N > 4:
|
|
total_passwords = len(CHARACTERS) ** N
|
|
print(f"\nWarning: With N={N}, there are {total_passwords:,} possible passwords.")
|
|
print("With salting, each user requires a separate full search!")
|
|
confirm = input("Continue? (yes/no): ")
|
|
if confirm.lower() != 'yes':
|
|
print("Attack cancelled.")
|
|
return
|
|
|
|
# Read password database from file
|
|
print("\nReading password database...")
|
|
password_database = read_salted_password_database(filename)
|
|
|
|
if not password_database:
|
|
print("No valid data found. Exiting.")
|
|
return
|
|
|
|
print("\nPassword Database:")
|
|
for username, salt_bytes, password_hash in password_database:
|
|
print(f"[{username}, {salt_bytes.hex()}, {password_hash}]")
|
|
|
|
# Perform dictionary attack
|
|
cracked_passwords, time_taken = dictionary_attack_salted(password_database, N)
|
|
|
|
# Display results
|
|
print("\nRecovered Passwords:")
|
|
for username, password in cracked_passwords.items():
|
|
print(f"{username}: {password}")
|
|
|
|
print(f"\nTotal time needed: {time_taken:.4f} seconds")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|