mirror of
https://github.com/trustedsec/hate_crack.git
synced 2026-07-28 14:47:22 -07:00
feat(pcfg): add hcatPCFG attack (mode A — pcfg_guesser piped to hashcat)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
ca124189e4
commit
61ee3b50d7
+45
-2
@@ -914,14 +914,17 @@ def _add_debug_mode_for_rules(cmd):
|
||||
|
||||
|
||||
# Sanitize filename for use as hashcat session name
|
||||
def generate_session_id():
|
||||
def generate_session_id(hash_file=None):
|
||||
"""Sanitize the hashfile name for use as a hashcat session name
|
||||
|
||||
Hashcat session names can only contain alphanumeric characters, hyphens, and underscores.
|
||||
This function removes the file extension and replaces problematic characters.
|
||||
|
||||
Args:
|
||||
hash_file: Optional explicit path; falls back to the ``hcatHashFile`` global when omitted.
|
||||
"""
|
||||
# Get just the filename without path
|
||||
filename = os.path.basename(hcatHashFile)
|
||||
filename = os.path.basename(hash_file if hash_file is not None else hcatHashFile)
|
||||
# Remove extension
|
||||
name_without_ext = os.path.splitext(filename)[0]
|
||||
# Replace any non-alphanumeric chars (except - and _) with underscore
|
||||
@@ -2614,6 +2617,46 @@ def hcatPrince(hcatHashType, hcatHashFile):
|
||||
prince_proc.stdout.close()
|
||||
|
||||
|
||||
def hcatPCFG(hcatHashType, hcatHashFile):
|
||||
"""Mode A: pipe pcfg_guesser.py output into hashcat in stdin mode."""
|
||||
pcfg_guesser_script = os.path.join(hate_path, "pcfg_cracker", "pcfg_guesser.py")
|
||||
if not os.path.isfile(pcfg_guesser_script):
|
||||
print(f"pcfg_guesser.py not found at {pcfg_guesser_script}")
|
||||
return
|
||||
pcfg_cmd = [
|
||||
"python3",
|
||||
pcfg_guesser_script,
|
||||
"--rule",
|
||||
pcfgRuleset,
|
||||
"--limit",
|
||||
str(pcfgMaxCandidates),
|
||||
]
|
||||
hashcat_cmd = [
|
||||
hcatBin,
|
||||
"-m",
|
||||
hcatHashType,
|
||||
hcatHashFile,
|
||||
"--session",
|
||||
generate_session_id(hcatHashFile),
|
||||
"-o",
|
||||
f"{hcatHashFile}.out",
|
||||
]
|
||||
if _should_use_optimized_kernel("hcatPCFG"):
|
||||
_insert_optimized_flag(hashcat_cmd)
|
||||
hashcat_cmd.extend(shlex.split(hcatTuning))
|
||||
_append_potfile_arg(hashcat_cmd)
|
||||
pcfg_proc = subprocess.Popen(pcfg_cmd, stdout=subprocess.PIPE)
|
||||
_run_hcat_cmd(
|
||||
hashcat_cmd,
|
||||
attack_name="PCFG",
|
||||
hash_file=hcatHashFile,
|
||||
stdin=pcfg_proc.stdout,
|
||||
companion_procs=[pcfg_proc],
|
||||
)
|
||||
if pcfg_proc.stdout:
|
||||
pcfg_proc.stdout.close()
|
||||
|
||||
|
||||
def hcatPermute(hcatHashType, hcatHashFile, wordlist):
|
||||
global hcatProcess, hcatPermuteCount
|
||||
permute_path = os.path.join(hate_path, "hashcat-utils", "bin", "permute.bin")
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
"""Tests for PCFG attack subprocess construction in hate_crack.main."""
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
PROJECT_ROOT = Path(__file__).resolve().parents[1]
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def hc_main(monkeypatch):
|
||||
"""Load hate_crack.main with SKIP_INIT and stub external bits."""
|
||||
monkeypatch.setenv("HATE_CRACK_SKIP_INIT", "1")
|
||||
if "hate_crack.main" in sys.modules:
|
||||
del sys.modules["hate_crack.main"]
|
||||
import hate_crack.main as m
|
||||
return m
|
||||
|
||||
|
||||
class TestHcatPCFG:
|
||||
def test_builds_expected_subprocess(self, hc_main, tmp_path):
|
||||
hash_file = str(tmp_path / "hashes.txt")
|
||||
Path(hash_file).write_text("dummy")
|
||||
|
||||
captured_calls = []
|
||||
|
||||
class FakeProc:
|
||||
def __init__(self, *args, **kwargs):
|
||||
captured_calls.append((args, kwargs))
|
||||
self.stdout = MagicMock()
|
||||
self.stdout.close = MagicMock()
|
||||
|
||||
with patch("hate_crack.main.subprocess.Popen", side_effect=FakeProc), \
|
||||
patch("hate_crack.main._run_hcat_cmd") as mock_run:
|
||||
hc_main.hcatPCFG("0", hash_file)
|
||||
|
||||
# First Popen call is the pcfg_guesser producer
|
||||
producer_args, producer_kwargs = captured_calls[0]
|
||||
producer_cmd = producer_args[0]
|
||||
assert "python3" in producer_cmd[0] or producer_cmd[0].endswith("python3")
|
||||
assert any("pcfg_guesser.py" in part for part in producer_cmd)
|
||||
assert "--rule" in producer_cmd
|
||||
assert producer_cmd[producer_cmd.index("--rule") + 1] == hc_main.pcfgRuleset
|
||||
assert "--limit" in producer_cmd
|
||||
assert producer_cmd[producer_cmd.index("--limit") + 1] == str(hc_main.pcfgMaxCandidates)
|
||||
|
||||
# _run_hcat_cmd was called with attack_name='PCFG' and the hashcat command
|
||||
assert mock_run.called
|
||||
kwargs = mock_run.call_args.kwargs
|
||||
hashcat_cmd = mock_run.call_args.args[0]
|
||||
assert kwargs["attack_name"] == "PCFG"
|
||||
assert kwargs["hash_file"] == hash_file
|
||||
# Hashcat does NOT carry --limit (cap is producer-side)
|
||||
assert "--limit" not in hashcat_cmd
|
||||
# Hashcat is in stdin mode (no -a flag)
|
||||
assert "-a" not in hashcat_cmd
|
||||
assert "-m" in hashcat_cmd
|
||||
assert hashcat_cmd[hashcat_cmd.index("-m") + 1] == "0"
|
||||
Reference in New Issue
Block a user