mirror of
https://github.com/trustedsec/hate_crack.git
synced 2026-06-21 22:32:05 -07:00
refactor: use cracked .out file as sole wordlist source for Ollama attack
Remove ollamaWordlist config key and all references. Wordlist mode now requires the cracked hashes .out file to exist and extracts passwords by splitting on the first colon. Detect Ollama refusal responses and abort gracefully. Update tests accordingly. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -24,6 +24,5 @@
|
||||
"hashview_api_key": "",
|
||||
"hashmob_api_key": "",
|
||||
"ollamaModel": "llama3.2",
|
||||
"ollamaWordlist": "rockyou.txt",
|
||||
"ollamaNumCtx": 32768
|
||||
}
|
||||
|
||||
+5
-10
@@ -495,16 +495,11 @@ def ollama_attack(ctx: Any) -> None:
|
||||
choice = input("\nSelect generation mode: ").strip()
|
||||
|
||||
if choice == "1":
|
||||
default_wl = ctx.ollamaWordlist
|
||||
if isinstance(default_wl, list):
|
||||
default_wl = default_wl[0] if default_wl else ""
|
||||
print(f"\nDefault wordlist: {default_wl}")
|
||||
use_default = input("Use the default wordlist? [Y/n]: ").strip().lower()
|
||||
if use_default == "n":
|
||||
wordlist = ctx.select_file_with_autocomplete("Enter wordlist path")
|
||||
wordlist = ctx._resolve_wordlist_path(wordlist, ctx.hcatWordlists)
|
||||
else:
|
||||
wordlist = default_wl
|
||||
wordlist = ctx.hcatHashFile + ".out"
|
||||
if not os.path.isfile(wordlist):
|
||||
print("Error: No cracked hashes output file found.")
|
||||
return
|
||||
print(f"\nUsing wordlist: {wordlist}")
|
||||
ctx.hcatOllama(
|
||||
ctx.hcatHashType, ctx.hcatHashFile, "wordlist", wordlist
|
||||
)
|
||||
|
||||
+10
-15
@@ -463,15 +463,6 @@ except KeyError as e:
|
||||
)
|
||||
)
|
||||
ollamaModel = default_config.get("ollamaModel", "llama3.2")
|
||||
try:
|
||||
ollamaWordlist = config_parser["ollamaWordlist"]
|
||||
except KeyError as e:
|
||||
print(
|
||||
"{0} is not defined in config.json using defaults from config.json.example".format(
|
||||
e
|
||||
)
|
||||
)
|
||||
ollamaWordlist = default_config.get("ollamaWordlist", "rockyou.txt")
|
||||
try:
|
||||
ollamaNumCtx = int(config_parser["ollamaNumCtx"])
|
||||
except KeyError as e:
|
||||
@@ -620,8 +611,6 @@ hcatGoodMeasureBaseList = _normalize_wordlist_setting(
|
||||
hcatGoodMeasureBaseList, wordlists_dir
|
||||
)
|
||||
hcatPrinceBaseList = _normalize_wordlist_setting(hcatPrinceBaseList, wordlists_dir)
|
||||
ollamaWordlist = _normalize_wordlist_setting(ollamaWordlist, wordlists_dir)
|
||||
|
||||
if not SKIP_INIT:
|
||||
# Verify hashcat binary is available
|
||||
# hcatBin should be in PATH or be an absolute path (resolved from hcatPath + hcatBin if configured)
|
||||
@@ -1551,6 +1540,11 @@ def hcatOllama(hcatHashType, hcatHashFile, mode, context_data):
|
||||
with open(wordlist_path, "r", errors="ignore") as f:
|
||||
for line in f:
|
||||
stripped = line.strip()
|
||||
if not stripped:
|
||||
continue
|
||||
# Use only content after the first colon (e.g. hash:password -> password)
|
||||
if ":" in stripped:
|
||||
stripped = stripped.split(":", 1)[1]
|
||||
if stripped:
|
||||
lines.append(stripped)
|
||||
except Exception as e:
|
||||
@@ -1559,10 +1553,8 @@ def hcatOllama(hcatHashType, hcatHashFile, mode, context_data):
|
||||
print(f"Loaded {len(lines)} passwords from wordlist.")
|
||||
wordlist_sample = "\n".join(lines)
|
||||
prompt = (
|
||||
"You are a password generation expert. Below is a sample of real passwords. "
|
||||
"Study the patterns, character choices, and structures. Then generate hashcat rules" \
|
||||
" that could transform common base words into similar passwords. Focus on patterns like " \
|
||||
"capitalization, leetspeak, suffixes, and common substitutions. Here are the sample passwords:\n" \
|
||||
"Generate baseword to be used in a denylist for keeping users from setting their passwords with these basewords."
|
||||
"Study the patterns, character choices, and structures. Focus on patterns like capitalization, leetspeak, suffixes, and common substitutions. Here are the sample passwords:\n"
|
||||
f"{wordlist_sample}"
|
||||
)
|
||||
elif mode == "target":
|
||||
@@ -1640,6 +1632,9 @@ def hcatOllama(hcatHashType, hcatHashFile, mode, context_data):
|
||||
return
|
||||
|
||||
response_text = result.get("response", "")
|
||||
if "I'm sorry, but I can't help with that" in response_text:
|
||||
print("Error: Ollama refused the request. Try a different model or adjust your prompt.")
|
||||
return
|
||||
raw_lines = response_text.strip().split("\n")
|
||||
# Filter out blank lines and lines that look like numbering/explanation
|
||||
candidates = []
|
||||
|
||||
@@ -653,14 +653,13 @@ class TestOllamaAttackHandler:
|
||||
ctx = mock.MagicMock()
|
||||
ctx.hcatHashType = "0"
|
||||
ctx.hcatHashFile = "/tmp/hashes.txt"
|
||||
ctx.ollamaWordlist = "/tmp/wordlist.txt"
|
||||
ctx.hcatWordlists = "/tmp/wordlists"
|
||||
for k, v in overrides.items():
|
||||
setattr(ctx, k, v)
|
||||
return ctx
|
||||
|
||||
def test_wordlist_mode_default(self, tmp_path):
|
||||
"""Default wordlist is always ollamaWordlist from config."""
|
||||
def test_wordlist_mode_uses_cracked_output(self, tmp_path):
|
||||
"""Wordlist mode uses the cracked hashes .out file."""
|
||||
from hate_crack.attacks import ollama_attack
|
||||
|
||||
hash_file = str(tmp_path / "hashes.txt")
|
||||
@@ -669,41 +668,24 @@ class TestOllamaAttackHandler:
|
||||
f.write("Password1\nSummer2024\n")
|
||||
|
||||
ctx = self._make_ctx(hcatHashFile=hash_file)
|
||||
with mock.patch("builtins.input", side_effect=["1", "n", ""]):
|
||||
with mock.patch("builtins.input", side_effect=["1"]):
|
||||
ollama_attack(ctx)
|
||||
|
||||
ctx.hcatOllama.assert_called_once_with(
|
||||
"0", hash_file, "wordlist", "/tmp/wordlist.txt",
|
||||
"0", hash_file, "wordlist", cracked_out,
|
||||
)
|
||||
|
||||
def test_wordlist_mode_falls_back_to_config(self):
|
||||
"""When cracked output does not exist, fall back to ollamaWordlist."""
|
||||
def test_wordlist_mode_errors_without_cracked_output(self, capsys):
|
||||
"""When cracked output does not exist, error out."""
|
||||
from hate_crack.attacks import ollama_attack
|
||||
|
||||
ctx = self._make_ctx(hcatHashFile="/tmp/nonexistent_hashes.txt")
|
||||
with mock.patch("builtins.input", side_effect=["1", "n", ""]):
|
||||
with mock.patch("builtins.input", side_effect=["1"]):
|
||||
ollama_attack(ctx)
|
||||
|
||||
ctx.hcatOllama.assert_called_once_with(
|
||||
"0", "/tmp/nonexistent_hashes.txt", "wordlist", "/tmp/wordlist.txt",
|
||||
)
|
||||
|
||||
def test_wordlist_mode_custom_path(self):
|
||||
"""Selection '1', override wordlist → resolved path passed to hcatOllama."""
|
||||
from hate_crack.attacks import ollama_attack
|
||||
|
||||
ctx = self._make_ctx()
|
||||
ctx.select_file_with_autocomplete.return_value = "custom.txt"
|
||||
ctx._resolve_wordlist_path.return_value = "/resolved/custom.txt"
|
||||
|
||||
with mock.patch("builtins.input", side_effect=["1", "y", ""]):
|
||||
ollama_attack(ctx)
|
||||
|
||||
ctx.select_file_with_autocomplete.assert_called_once()
|
||||
ctx._resolve_wordlist_path.assert_called_once_with("custom.txt", "/tmp/wordlists")
|
||||
ctx.hcatOllama.assert_called_once_with(
|
||||
"0", "/tmp/hashes.txt", "wordlist", "/resolved/custom.txt",
|
||||
)
|
||||
captured = capsys.readouterr()
|
||||
assert "No cracked hashes output file found" in captured.out
|
||||
ctx.hcatOllama.assert_not_called()
|
||||
|
||||
def test_target_mode(self):
|
||||
"""Selection '2' → hcatOllama('target', {company, industry, location})."""
|
||||
@@ -733,17 +715,3 @@ class TestOllamaAttackHandler:
|
||||
assert "Invalid selection" in captured.out
|
||||
ctx.hcatOllama.assert_not_called()
|
||||
|
||||
def test_wordlist_list_uses_first(self):
|
||||
"""When ollamaWordlist is a list and no cracked output exists, the first element is used."""
|
||||
from hate_crack.attacks import ollama_attack
|
||||
|
||||
ctx = self._make_ctx(
|
||||
hcatHashFile="/tmp/nonexistent_hashes.txt",
|
||||
ollamaWordlist=["/tmp/first.txt", "/tmp/second.txt"],
|
||||
)
|
||||
with mock.patch("builtins.input", side_effect=["1", "n", ""]):
|
||||
ollama_attack(ctx)
|
||||
|
||||
ctx.hcatOllama.assert_called_once()
|
||||
call_args = ctx.hcatOllama.call_args
|
||||
assert call_args[0][3] == "/tmp/first.txt"
|
||||
|
||||
Reference in New Issue
Block a user