#!/usr/bin/env python3 import argparse import itertools import shutil import string import subprocess import sys import time from pathlib import Path sys.path.insert(0, str(Path(__file__).parent)) from config import BIN_DIR CHARSET = string.ascii_letters + string.digits + "!@#$%^&*()" FIRST_CHARS = string.ascii_letters MAX_HOURS = 8 def get_access_cmd() -> list[str]: """Resolve path to access utility (confaccess).""" # setup.sh installs as confaccess; pyproject exposes as access for name in ("confaccess", "access"): path = shutil.which(name) if path: return [path] # Fallback: same directory as bruteforce (development) script_dir = Path(__file__).resolve().parent access_script = script_dir / "access.py" if access_script.exists(): return [sys.executable, str(access_script)] # Try bin dir from config (e.g. /usr/local/practice2/bin/confaccess) for name in ("confaccess", "access"): bin_cmd = BIN_DIR / name if bin_cmd.exists(): return [str(bin_cmd)] return ["confaccess"] # hope it's in PATH 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( login: str, length: int, proc: subprocess.Popen[str] ) -> tuple[str, int, float] | None: """Try all passwords of given length via batch process. proc = access --check.""" count = 0 start = time.perf_counter() stdin = proc.stdin stdout = proc.stdout assert stdin is not None and stdout is not None def check(password: str) -> bool: nonlocal count count += 1 stdin.write(password + "\n") stdin.flush() line = stdout.readline() if not line: return False return line.strip() == "1" if length == 1: for first in FIRST_CHARS: if time.perf_counter() - start > MAX_HOURS * 3600: return None if check(first): 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 password = first + "".join(rest) if check(password): return password, count, time.perf_counter() - start return None def main() -> None: parser = argparse.ArgumentParser( description="Brute force password cracker (via access utility)" ) 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() cmd = get_access_cmd() + ["--check", args.login] proc = subprocess.Popen( cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, text=True, bufsize=1, ) try: print(f"Target: {args.login}") print(f"Charset size: {len(CHARSET)} ({len(FIRST_CHARS)} valid for first char)") print() total_start = time.perf_counter() 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(args.login, length, proc) if result is not None: password, count, elapsed = result total_elapsed = time.perf_counter() - total_start print(f" >>> FOUND: '{password}'") print(f" Iterations: {count:,}") print(f" Time (len): {elapsed:.4f}s") print(f" Time (total): {total_elapsed:.4f}s") print(f" Speed: {count / elapsed:,.0f} attempts/s") return else: print(f" Not found at length {length} (timeout or exhausted)") print(f"\nPassword not found within length {args.max_length}.") finally: if proc.stdin: proc.stdin.close() if proc.poll() is None: proc.terminate() proc.wait() if __name__ == "__main__": main()