· 10 months ago · Feb 27, 2025, 04:55 PM
1#!/usr/bin/env python3
2
3import hashlib
4import binascii
5import sys
6import time
7import select
8
9def derive_pbkdf2(password: bytes, salt: bytes, rounds: int, length: int) -> bytes:
10 return hashlib.pbkdf2_hmac(
11 hash_name='sha256',
12 password=password,
13 salt=salt,
14 iterations=rounds,
15 dklen=length
16 )
17
18# All "pbkdf2$50000$50" users
19hashes = [
20
21# Enter in username, passwd_hex, and salt_hex
22 {
23 "username": "admin",
24 "passwd_hex": "86a6137c28fda328c8e6abbcb53e4ac534dc9c010bb40468ce4b8f4bf882c0c1c1d2396d45e7ee16bc592260e74c8bbdd6f2",
25 "salt_hex": "5d3f4c5b71ae98ebdc2520449f6c56f8",
26 "iterations": 50000,
27 "dklen": 50
28 },
29 {
30 "username": "user1",
31 "passwd_hex": "7f2acfe2bdc0afe9cbb54aac932230de2a1032e8bfcd5706308cabd41f75151b51278207c2c6141321655612c1c54ad24fac",
32 "salt_hex": "e9facc6971e7440b5e04f10765e833d1",
33 "iterations": 50000,
34 "dklen": 50
35 }
36
37# Add others as needed...
38]
39
40# Convert from hex to bytes
41for h in hashes:
42 h["passwd_bytes"] = binascii.unhexlify(h["passwd_hex"])
43 h["salt_bytes"] = binascii.unhexlify(h["salt_hex"])
44 h["cracked"] = False
45
46wordlist = "/usr/share/wordlists/rockyou.txt"
47time_limit_seconds = 600 # 10 minutes per user
48
49print(f"[+] Will spend up to {time_limit_seconds}s (={time_limit_seconds/60:.1f} min) per hash.")
50print("[+] Press 's' + Enter at any time to skip the current hash.\n")
51
52def crack_one_hash(user_hash, wordlist_path, time_limit):
53 """
54 Attempt to crack a single user's PBKDF2-HMAC-SHA256 within 'time_limit' seconds.
55 Returns the plaintext password or None if not found / skipped / timed out.
56 """
57 user = user_hash["username"]
58 target = user_hash["passwd_bytes"]
59 start_time = time.time()
60
61 print(f"[*] Cracking {user} for up to {time_limit} seconds... (Press 's' + Enter to skip)")
62
63 try:
64 with open(wordlist_path, "r", encoding="utf-8", errors="ignore") as f:
65 for line in f:
66 # 1) Check if user wants to skip
67 if sys.stdin in select.select([sys.stdin], [], [], 0)[0]:
68 key_press = sys.stdin.read(1)
69 if key_press.lower() == 's':
70 print(f"[-] User pressed 's' to skip {user}. Moving on.\n")
71 return None
72
73 # 2) Check time limit
74 if (time.time() - start_time) > time_limit:
75 print(f"[-] Time limit reached for {user}. Moving on.\n")
76 return None
77
78 pwd_str = line.strip()
79 if not pwd_str:
80 continue
81
82 pwd_bytes = pwd_str.encode('utf-8')
83 derived = derive_pbkdf2(
84 password=pwd_bytes,
85 salt=user_hash["salt_bytes"],
86 rounds=user_hash["iterations"],
87 length=user_hash["dklen"]
88 )
89 if derived == target:
90 print(f"[!] SUCCESS: {user}'s password is '{pwd_str}'\n")
91 return pwd_str
92
93 except FileNotFoundError:
94 print(f"[!] Wordlist not found: {wordlist_path}")
95 return None
96
97 # If finished dictionary with no match
98 print(f"[!] Dictionary exhausted for {user}, no match.\n")
99 return None
100
101# ------------------------------------------------
102# Main Loop: process each user sequentially
103# ------------------------------------------------
104for user_hash in hashes:
105 if user_hash["cracked"]:
106 continue
107
108 found_pass = crack_one_hash(user_hash, wordlist, time_limit_seconds)
109 if found_pass:
110 user_hash["cracked"] = True
111
112print("[+] Done. Any user not shown as cracked wasn't found within time or dictionary.")
113
114