lab2 доработки

This commit is contained in:
2026-03-16 12:08:42 +03:00
parent b6b5e16d63
commit 37eff0ed9b
6 changed files with 164 additions and 63 deletions

View File

@@ -1,15 +1,16 @@
#!/usr/bin/env python3
import argparse
import hashlib
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 PASSWD_FILE
from config import BIN_DIR
CHARSET = string.ascii_letters + string.digits + "!@#$%^&*()"
FIRST_CHARS = string.ascii_letters
@@ -17,19 +18,24 @@ 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 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:
@@ -38,14 +44,31 @@ def max_combinations(length: int) -> int:
return len(FIRST_CHARS) * (len(CHARSET) ** (length - 1))
def brute_force_length(target_hash: str, length: int) -> tuple[str, int, float] | None:
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:
count += 1
if hash_password(first) == target_hash:
if time.perf_counter() - start > MAX_HOURS * 3600:
return None
if check(first):
return first, count, time.perf_counter() - start
return None
@@ -53,16 +76,17 @@ def brute_force_length(target_hash: str, length: int) -> tuple[str, int, float]
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:
if check(password):
return password, count, time.perf_counter() - start
return None
def main() -> None:
parser = argparse.ArgumentParser(description="Brute force password cracker (SHA-256)")
parser = argparse.ArgumentParser(
description="Brute force password cracker (via access utility)"
)
parser.add_argument("login", help="Target username")
parser.add_argument(
"--max-length",
@@ -72,34 +96,45 @@ def main() -> None:
)
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
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()
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()
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")
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)
result = brute_force_length(target_hash, length)
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)")
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}.")
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__":