mirror of
https://github.com/trustedsec/hate_crack.git
synced 2026-04-28 12:03:11 -07:00
Adds Permutation Attack (menu option 19) that generates all character permutations of each word in a targeted wordlist and pipes them to hashcat via permute.bin from hashcat-utils. - hcatPermute() in main.py: pipes permute.bin < wordlist | hashcat - permute_crack() in attacks.py: prompts for single wordlist file with factorial-growth warning, tab-autocomplete support - Menu option 19 wired in both main.py and hate_crack.py - hcatPermuteCount tracking alongside other count globals - Tests: test_permute_attack.py (handler behavior) and test_permute_wrapper.py (subprocess wiring) - README: added entry in menu listing and attack descriptions
163 lines
7.1 KiB
Python
163 lines
7.1 KiB
Python
import os
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
import pytest
|
|
|
|
|
|
@pytest.fixture
|
|
def main_module(hc_module):
|
|
"""Return the underlying hate_crack.main module for direct patching."""
|
|
return hc_module._main
|
|
|
|
|
|
class TestHcatPermute:
|
|
def test_uses_permute_bin(self, main_module, tmp_path):
|
|
wl = tmp_path / "words.txt"
|
|
wl.write_text("abc\n")
|
|
hash_file = str(tmp_path / "hashes.txt")
|
|
permute_bin_dir = tmp_path / "hashcat-utils" / "bin"
|
|
permute_bin_dir.mkdir(parents=True)
|
|
permute_bin = permute_bin_dir / "permute.bin"
|
|
permute_bin.touch()
|
|
|
|
mock_permute_proc = MagicMock()
|
|
mock_permute_proc.stdout = MagicMock()
|
|
mock_permute_proc.wait.return_value = None
|
|
|
|
mock_hashcat_proc = MagicMock()
|
|
mock_hashcat_proc.wait.return_value = None
|
|
mock_hashcat_proc.pid = 99
|
|
|
|
with patch.object(main_module, "hate_path", str(tmp_path)), \
|
|
patch.object(main_module, "hcatBin", "hashcat"), \
|
|
patch.object(main_module, "hcatTuning", ""), \
|
|
patch.object(main_module, "hcatPotfilePath", ""), \
|
|
patch.object(main_module, "generate_session_id", return_value="sess1"), \
|
|
patch.object(main_module, "lineCount", return_value=0), \
|
|
patch.object(main_module, "hcatHashCracked", 0, create=True), \
|
|
patch("hate_crack.main.subprocess.Popen") as mock_popen:
|
|
mock_popen.side_effect = [mock_permute_proc, mock_hashcat_proc]
|
|
main_module.hcatPermute("1000", hash_file, str(wl))
|
|
|
|
assert mock_popen.call_count == 2
|
|
first_call_args = mock_popen.call_args_list[0][0][0]
|
|
assert "permute.bin" in str(first_call_args)
|
|
|
|
def test_pipes_permute_stdout_to_hashcat_stdin(self, main_module, tmp_path):
|
|
wl = tmp_path / "words.txt"
|
|
wl.write_text("abc\n")
|
|
hash_file = str(tmp_path / "hashes.txt")
|
|
permute_bin_dir = tmp_path / "hashcat-utils" / "bin"
|
|
permute_bin_dir.mkdir(parents=True)
|
|
(permute_bin_dir / "permute.bin").touch()
|
|
|
|
mock_permute_proc = MagicMock()
|
|
mock_permute_proc.stdout = MagicMock()
|
|
mock_permute_proc.wait.return_value = None
|
|
|
|
mock_hashcat_proc = MagicMock()
|
|
mock_hashcat_proc.wait.return_value = None
|
|
mock_hashcat_proc.pid = 99
|
|
|
|
with patch.object(main_module, "hate_path", str(tmp_path)), \
|
|
patch.object(main_module, "hcatBin", "hashcat"), \
|
|
patch.object(main_module, "hcatTuning", ""), \
|
|
patch.object(main_module, "hcatPotfilePath", ""), \
|
|
patch.object(main_module, "generate_session_id", return_value="sess1"), \
|
|
patch.object(main_module, "lineCount", return_value=0), \
|
|
patch.object(main_module, "hcatHashCracked", 0, create=True), \
|
|
patch("hate_crack.main.subprocess.Popen") as mock_popen:
|
|
mock_popen.side_effect = [mock_permute_proc, mock_hashcat_proc]
|
|
main_module.hcatPermute("1000", hash_file, str(wl))
|
|
|
|
# Second call (hashcat) should use permute_proc.stdout as stdin
|
|
second_call_kwargs = mock_popen.call_args_list[1][1]
|
|
assert second_call_kwargs.get("stdin") == mock_permute_proc.stdout
|
|
|
|
def test_hashcat_cmd_includes_hash_type_and_file(self, main_module, tmp_path):
|
|
wl = tmp_path / "words.txt"
|
|
wl.write_text("abc\n")
|
|
hash_file = str(tmp_path / "hashes.txt")
|
|
permute_bin_dir = tmp_path / "hashcat-utils" / "bin"
|
|
permute_bin_dir.mkdir(parents=True)
|
|
(permute_bin_dir / "permute.bin").touch()
|
|
|
|
mock_permute_proc = MagicMock()
|
|
mock_permute_proc.stdout = MagicMock()
|
|
mock_permute_proc.wait.return_value = None
|
|
|
|
mock_hashcat_proc = MagicMock()
|
|
mock_hashcat_proc.wait.return_value = None
|
|
mock_hashcat_proc.pid = 99
|
|
|
|
with patch.object(main_module, "hate_path", str(tmp_path)), \
|
|
patch.object(main_module, "hcatBin", "hashcat"), \
|
|
patch.object(main_module, "hcatTuning", ""), \
|
|
patch.object(main_module, "hcatPotfilePath", ""), \
|
|
patch.object(main_module, "generate_session_id", return_value="sess1"), \
|
|
patch.object(main_module, "lineCount", return_value=0), \
|
|
patch.object(main_module, "hcatHashCracked", 0, create=True), \
|
|
patch("hate_crack.main.subprocess.Popen") as mock_popen:
|
|
mock_popen.side_effect = [mock_permute_proc, mock_hashcat_proc]
|
|
main_module.hcatPermute("1000", hash_file, str(wl))
|
|
|
|
hashcat_cmd = mock_popen.call_args_list[1][0][0]
|
|
assert "hashcat" in hashcat_cmd
|
|
assert "-m" in hashcat_cmd
|
|
assert "1000" in hashcat_cmd
|
|
assert hash_file in hashcat_cmd
|
|
|
|
def test_keyboard_interrupt_kills_both_processes(self, main_module, tmp_path):
|
|
wl = tmp_path / "words.txt"
|
|
wl.write_text("abc\n")
|
|
hash_file = str(tmp_path / "hashes.txt")
|
|
permute_bin_dir = tmp_path / "hashcat-utils" / "bin"
|
|
permute_bin_dir.mkdir(parents=True)
|
|
(permute_bin_dir / "permute.bin").touch()
|
|
|
|
mock_permute_proc = MagicMock()
|
|
mock_permute_proc.stdout = MagicMock()
|
|
mock_permute_proc.wait.return_value = None
|
|
|
|
mock_hashcat_proc = MagicMock()
|
|
mock_hashcat_proc.wait.side_effect = KeyboardInterrupt()
|
|
mock_hashcat_proc.pid = 99
|
|
|
|
with patch.object(main_module, "hate_path", str(tmp_path)), \
|
|
patch.object(main_module, "hcatBin", "hashcat"), \
|
|
patch.object(main_module, "hcatTuning", ""), \
|
|
patch.object(main_module, "hcatPotfilePath", ""), \
|
|
patch.object(main_module, "generate_session_id", return_value="sess1"), \
|
|
patch.object(main_module, "lineCount", return_value=0), \
|
|
patch.object(main_module, "hcatHashCracked", 0, create=True), \
|
|
patch("hate_crack.main.subprocess.Popen") as mock_popen:
|
|
mock_popen.side_effect = [mock_permute_proc, mock_hashcat_proc]
|
|
main_module.hcatPermute("1000", hash_file, str(wl))
|
|
|
|
mock_hashcat_proc.kill.assert_called_once()
|
|
mock_permute_proc.kill.assert_called_once()
|
|
|
|
def test_missing_permute_bin_prints_error(self, main_module, tmp_path, capsys):
|
|
wl = tmp_path / "words.txt"
|
|
wl.write_text("abc\n")
|
|
hash_file = str(tmp_path / "hashes.txt")
|
|
# No permute.bin created
|
|
|
|
with patch.object(main_module, "hate_path", str(tmp_path)):
|
|
main_module.hcatPermute("1000", hash_file, str(wl))
|
|
|
|
captured = capsys.readouterr()
|
|
assert "permute.bin" in captured.out
|
|
|
|
def test_missing_wordlist_prints_error(self, main_module, tmp_path, capsys):
|
|
hash_file = str(tmp_path / "hashes.txt")
|
|
permute_bin_dir = tmp_path / "hashcat-utils" / "bin"
|
|
permute_bin_dir.mkdir(parents=True)
|
|
(permute_bin_dir / "permute.bin").touch()
|
|
|
|
with patch.object(main_module, "hate_path", str(tmp_path)):
|
|
main_module.hcatPermute("1000", hash_file, "/nonexistent/words.txt")
|
|
|
|
captured = capsys.readouterr()
|
|
assert "not found" in captured.out.lower() or "error" in captured.out.lower()
|