107 lines
3.0 KiB
Python
107 lines
3.0 KiB
Python
#!/usr/bin/env python3
|
|
import argparse
|
|
import hashlib
|
|
import itertools
|
|
import string
|
|
import sys
|
|
import time
|
|
from pathlib import Path
|
|
|
|
sys.path.insert(0, str(Path(__file__).parent))
|
|
|
|
from config import PASSWD_FILE
|
|
|
|
CHARSET = string.ascii_letters + string.digits + "!@#$%^&*()"
|
|
FIRST_CHARS = string.ascii_letters
|
|
|
|
MAX_HOURS = 8
|
|
|
|
|
|
def hash_password(password: str) -> str:
|
|
return hashlib.sha256(password.encode("ascii")).hexdigest()
|
|
|
|
|
|
def get_target_hash(login: str) -> str | None:
|
|
if not PASSWD_FILE.exists():
|
|
return None
|
|
with open(PASSWD_FILE) as f:
|
|
for line in f:
|
|
parts = line.strip().split(":", 4)
|
|
if len(parts) == 5 and parts[0] == login:
|
|
return parts[1]
|
|
return None
|
|
|
|
|
|
def max_combinations(length: int) -> int:
|
|
if length == 1:
|
|
return len(FIRST_CHARS)
|
|
return len(FIRST_CHARS) * (len(CHARSET) ** (length - 1))
|
|
|
|
|
|
def brute_force_length(target_hash: str, length: int) -> tuple[str, int, float] | None:
|
|
count = 0
|
|
start = time.perf_counter()
|
|
|
|
if length == 1:
|
|
for first in FIRST_CHARS:
|
|
count += 1
|
|
if hash_password(first) == target_hash:
|
|
return first, count, time.perf_counter() - start
|
|
return None
|
|
|
|
for first in FIRST_CHARS:
|
|
for rest in itertools.product(CHARSET, repeat=length - 1):
|
|
if time.perf_counter() - start > MAX_HOURS * 3600:
|
|
return None
|
|
count += 1
|
|
password = first + "".join(rest)
|
|
if hash_password(password) == target_hash:
|
|
return password, count, time.perf_counter() - start
|
|
|
|
return None
|
|
|
|
|
|
def main() -> None:
|
|
parser = argparse.ArgumentParser(description="Brute force password cracker (SHA-256)")
|
|
parser.add_argument("login", help="Target username")
|
|
parser.add_argument(
|
|
"--max-length",
|
|
type=int,
|
|
default=6,
|
|
help="Maximum password length to try (default: 6)",
|
|
)
|
|
args = parser.parse_args()
|
|
|
|
target_hash = get_target_hash(args.login)
|
|
if not target_hash:
|
|
print(f"User '{args.login}' not found in passwd file.")
|
|
return
|
|
|
|
print(f"Target: {args.login}")
|
|
print(f"Hash: {target_hash}")
|
|
print(f"Charset size: {len(CHARSET)} ({len(FIRST_CHARS)} valid for first char)")
|
|
print(f"Algorithm: SHA-256")
|
|
print()
|
|
|
|
for length in range(1, args.max_length + 1):
|
|
total = max_combinations(length)
|
|
print(f"Length {length}: max {total:>15,} combinations")
|
|
|
|
result = brute_force_length(target_hash, length)
|
|
|
|
if result is not None:
|
|
password, count, elapsed = result
|
|
print(f" >>> FOUND: '{password}'")
|
|
print(f" Iterations: {count:,}")
|
|
print(f" Time: {elapsed:.4f}s")
|
|
print(f" Speed: {count / elapsed:,.0f} hashes/s")
|
|
return
|
|
else:
|
|
print(f" Not found at length {length} (timeout or exhausted)")
|
|
|
|
print(f"\nPassword not found within length {args.max_length}.")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|