mirror of
https://github.com/trustedsec/hate_crack.git
synced 2026-07-28 14:47:22 -07:00
updated loopback logic and added testing
This commit is contained in:
@@ -82,6 +82,7 @@ def get_main_menu_options():
|
||||
"11": _attacks.middle_combinator,
|
||||
"12": _attacks.thorough_combinator,
|
||||
"13": _attacks.bandrel_method,
|
||||
"14": _attacks.loopback_attack,
|
||||
"90": download_hashmob_rules,
|
||||
"91": weakpass_wordlist_menu,
|
||||
"92": download_hashmob_wordlists,
|
||||
|
||||
@@ -170,6 +170,103 @@ def quick_crack(ctx: Any) -> None:
|
||||
)
|
||||
|
||||
|
||||
def loopback_attack(ctx: Any) -> None:
|
||||
empty_wordlist = os.path.join(ctx.hcatWordlists, "empty.txt")
|
||||
os.makedirs(ctx.hcatWordlists, exist_ok=True)
|
||||
if not os.path.exists(empty_wordlist):
|
||||
with open(empty_wordlist, "w"):
|
||||
pass
|
||||
|
||||
print(f"\nUsing loopback attack with wordlist: {empty_wordlist}")
|
||||
|
||||
rule_choice = None
|
||||
selected_hcatRules = []
|
||||
|
||||
rule_files = sorted(
|
||||
f for f in os.listdir(ctx.rulesDirectory) if f != ".DS_Store"
|
||||
)
|
||||
if not rule_files:
|
||||
download_rules = (
|
||||
input("\nNo rules found. Download rules from Hashmob now? (Y/n): ")
|
||||
.strip()
|
||||
.lower()
|
||||
)
|
||||
if download_rules in ("", "y", "yes"):
|
||||
download_hashmob_rules(print_fn=print)
|
||||
rule_files = sorted(os.listdir(ctx.rulesDirectory))
|
||||
|
||||
if not rule_files:
|
||||
print("No rules available. Proceeding without rules.")
|
||||
rule_choice = ["0"]
|
||||
else:
|
||||
print("\nWhich rule(s) would you like to run?")
|
||||
rule_entries = ["0. To run without any rules"]
|
||||
rule_entries.extend(
|
||||
[f"{i}. {file}" for i, file in enumerate(rule_files, start=1)]
|
||||
)
|
||||
rule_entries.append("98. YOLO...run all of the rules")
|
||||
rule_entries.append("99. Back to Main Menu")
|
||||
max_rule_len = max((len(e) for e in rule_entries), default=26)
|
||||
print_multicolumn_list(
|
||||
"Available Rules",
|
||||
rule_entries,
|
||||
min_col_width=max_rule_len,
|
||||
max_col_width=max_rule_len,
|
||||
)
|
||||
|
||||
example_line = ""
|
||||
if len(rule_files) >= 2:
|
||||
example_line = f"For example 1+1 will run {rule_files[0]} chained twice and 1,2 would run {rule_files[0]} and then {rule_files[1]} sequentially.\n"
|
||||
elif len(rule_files) == 1:
|
||||
example_line = f"For example 1+1 will run {rule_files[0]} chained twice.\n"
|
||||
|
||||
while rule_choice is None:
|
||||
raw_choice = input(
|
||||
"Enter Comma separated list of rules you would like to run. To run rules chained use the + symbol.\n"
|
||||
f"{example_line}"
|
||||
"Choose wisely: "
|
||||
)
|
||||
if raw_choice.strip() == "99":
|
||||
return
|
||||
if raw_choice != "":
|
||||
rule_choice = raw_choice.split(",")
|
||||
|
||||
if "99" in rule_choice:
|
||||
return
|
||||
if "98" in rule_choice:
|
||||
for rule in rule_files:
|
||||
selected_hcatRules.append(f"-r {ctx.rulesDirectory}/{rule}")
|
||||
elif "0" in rule_choice:
|
||||
selected_hcatRules = [""]
|
||||
else:
|
||||
for choice in rule_choice:
|
||||
if "+" in choice:
|
||||
combined_choice = ""
|
||||
choices = choice.split("+")
|
||||
for rule in choices:
|
||||
try:
|
||||
combined_choice = f"{combined_choice} -r {ctx.rulesDirectory}/{rule_files[int(rule) - 1]}"
|
||||
except Exception:
|
||||
continue
|
||||
selected_hcatRules.append(combined_choice)
|
||||
else:
|
||||
try:
|
||||
selected_hcatRules.append(
|
||||
f"-r {ctx.rulesDirectory}/{rule_files[int(choice) - 1]}"
|
||||
)
|
||||
except IndexError:
|
||||
continue
|
||||
|
||||
for chain in selected_hcatRules:
|
||||
ctx.hcatQuickDictionary(
|
||||
ctx.hcatHashType,
|
||||
ctx.hcatHashFile,
|
||||
chain,
|
||||
empty_wordlist,
|
||||
loopback=True,
|
||||
)
|
||||
|
||||
|
||||
def extensive_crack(ctx: Any) -> None:
|
||||
ctx.hcatBruteForce(ctx.hcatHashType, ctx.hcatHashFile, "1", "7")
|
||||
ctx.hcatRecycle(ctx.hcatHashType, ctx.hcatHashFile, ctx.hcatBruteCount)
|
||||
|
||||
+59
-25
@@ -268,6 +268,23 @@ hcatWordlists = config_parser["hcatWordlists"]
|
||||
hcatOptimizedWordlists = config_parser["hcatOptimizedWordlists"]
|
||||
hcatRules: list[str] = []
|
||||
|
||||
|
||||
# Optional: override hashcat's default potfile location.
|
||||
# If unset/empty, we use hashcat's built-in default potfile behavior.
|
||||
hcatPotfilePath = (config_parser.get("hcatPotfilePath") or "").strip()
|
||||
if hcatPotfilePath:
|
||||
hcatPotfilePath = os.path.expanduser(hcatPotfilePath)
|
||||
if not os.path.isabs(hcatPotfilePath):
|
||||
hcatPotfilePath = os.path.join(hate_path, hcatPotfilePath)
|
||||
|
||||
|
||||
def _append_potfile_arg(cmd, *, use_potfile_path=True, potfile_path=None):
|
||||
if not use_potfile_path:
|
||||
return
|
||||
pot = potfile_path or hcatPotfilePath
|
||||
if pot:
|
||||
cmd.append(f"--potfile-path={pot}")
|
||||
|
||||
try:
|
||||
rulesDirectory = config_parser["rules_directory"]
|
||||
except KeyError as e:
|
||||
@@ -834,7 +851,8 @@ def _run_hashcat_show(hash_type, hash_file, output_path):
|
||||
[
|
||||
hcatBin,
|
||||
"--show",
|
||||
f"--potfile-path={hate_path}/hashcat.pot",
|
||||
# Use hashcat's built-in potfile unless configured otherwise.
|
||||
*([f"--potfile-path={hcatPotfilePath}"] if hcatPotfilePath else []),
|
||||
"-m",
|
||||
str(hash_type),
|
||||
hash_file,
|
||||
@@ -865,7 +883,7 @@ def hcatBruteForce(hcatHashType, hcatHashFile, hcatMinLen, hcatMaxLen):
|
||||
"?a?a?a?a?a?a?a?a?a?a?a?a?a?a",
|
||||
]
|
||||
cmd.extend(shlex.split(hcatTuning))
|
||||
cmd.append(f"--potfile-path={hate_path}/hashcat.pot")
|
||||
_append_potfile_arg(cmd)
|
||||
hcatProcess = subprocess.Popen(cmd)
|
||||
try:
|
||||
hcatProcess.wait()
|
||||
@@ -897,7 +915,7 @@ def hcatDictionary(hcatHashType, hcatHashFile):
|
||||
cmd.extend(optimized_lists)
|
||||
cmd.extend(["-r", rule_best66])
|
||||
cmd.extend(shlex.split(hcatTuning))
|
||||
cmd.append(f"--potfile-path={hate_path}/hashcat.pot")
|
||||
_append_potfile_arg(cmd)
|
||||
hcatProcess = subprocess.Popen(cmd)
|
||||
try:
|
||||
hcatProcess.wait()
|
||||
@@ -921,7 +939,7 @@ def hcatDictionary(hcatHashType, hcatHashFile):
|
||||
rule_d3ad0ne,
|
||||
]
|
||||
cmd.extend(shlex.split(hcatTuning))
|
||||
cmd.append(f"--potfile-path={hate_path}/hashcat.pot")
|
||||
_append_potfile_arg(cmd)
|
||||
hcatProcess = subprocess.Popen(cmd)
|
||||
try:
|
||||
hcatProcess.wait()
|
||||
@@ -944,7 +962,7 @@ def hcatDictionary(hcatHashType, hcatHashFile):
|
||||
rule_toxic,
|
||||
]
|
||||
cmd.extend(shlex.split(hcatTuning))
|
||||
cmd.append(f"--potfile-path={hate_path}/hashcat.pot")
|
||||
_append_potfile_arg(cmd)
|
||||
hcatProcess = subprocess.Popen(cmd)
|
||||
try:
|
||||
hcatProcess.wait()
|
||||
@@ -956,7 +974,15 @@ def hcatDictionary(hcatHashType, hcatHashFile):
|
||||
|
||||
|
||||
# Quick Dictionary Attack (Optional Chained Rules)
|
||||
def hcatQuickDictionary(hcatHashType, hcatHashFile, hcatChains, wordlists):
|
||||
def hcatQuickDictionary(
|
||||
hcatHashType,
|
||||
hcatHashFile,
|
||||
hcatChains,
|
||||
wordlists,
|
||||
loopback=False,
|
||||
use_potfile_path=False,
|
||||
potfile_path=None,
|
||||
):
|
||||
global hcatProcess
|
||||
cmd = [
|
||||
hcatBin,
|
||||
@@ -972,10 +998,12 @@ def hcatQuickDictionary(hcatHashType, hcatHashFile, hcatChains, wordlists):
|
||||
cmd.extend(wordlists)
|
||||
else:
|
||||
cmd.append(wordlists)
|
||||
if loopback:
|
||||
cmd.append("--loopback")
|
||||
if hcatChains:
|
||||
cmd.extend(shlex.split(hcatChains))
|
||||
cmd.extend(shlex.split(hcatTuning))
|
||||
cmd.append(f"--potfile-path={hate_path}/hashcat.pot")
|
||||
_append_potfile_arg(cmd, use_potfile_path=use_potfile_path, potfile_path=potfile_path)
|
||||
hcatProcess = subprocess.Popen(cmd)
|
||||
try:
|
||||
hcatProcess.wait()
|
||||
@@ -1040,7 +1068,7 @@ def hcatTopMask(hcatHashType, hcatHashFile, hcatTargetTime):
|
||||
f"{hcatHashFile}.hcmask",
|
||||
]
|
||||
cmd.extend(shlex.split(hcatTuning))
|
||||
cmd.append(f"--potfile-path={hate_path}/hashcat.pot")
|
||||
_append_potfile_arg(cmd)
|
||||
hcatProcess = subprocess.Popen(cmd)
|
||||
try:
|
||||
hcatProcess.wait()
|
||||
@@ -1094,7 +1122,7 @@ def hcatFingerprint(hcatHashType, hcatHashFile):
|
||||
f"{hcatHashFile}.expanded",
|
||||
f"{hcatHashFile}.expanded",
|
||||
*shlex.split(hcatTuning),
|
||||
f"--potfile-path={hate_path}/hashcat.pot",
|
||||
*([f"--potfile-path={hcatPotfilePath}"] if hcatPotfilePath else []),
|
||||
]
|
||||
)
|
||||
try:
|
||||
@@ -1125,7 +1153,7 @@ def hcatCombination(hcatHashType, hcatHashFile):
|
||||
hcatCombinationWordlist[1],
|
||||
]
|
||||
cmd.extend(shlex.split(hcatTuning))
|
||||
cmd.append(f"--potfile-path={hate_path}/hashcat.pot")
|
||||
_append_potfile_arg(cmd)
|
||||
hcatProcess = subprocess.Popen(cmd)
|
||||
try:
|
||||
hcatProcess.wait()
|
||||
@@ -1182,7 +1210,7 @@ def hcatHybrid(hcatHashType, hcatHashFile, wordlists=None):
|
||||
*args,
|
||||
]
|
||||
cmd.extend(shlex.split(hcatTuning))
|
||||
cmd.append(f"--potfile-path={hate_path}/hashcat.pot")
|
||||
_append_potfile_arg(cmd)
|
||||
hcatProcess = subprocess.Popen(cmd)
|
||||
try:
|
||||
hcatProcess.wait()
|
||||
@@ -1217,7 +1245,7 @@ def hcatYoloCombination(hcatHashType, hcatHashFile):
|
||||
right_path,
|
||||
]
|
||||
cmd.extend(shlex.split(hcatTuning))
|
||||
cmd.append(f"--potfile-path={hate_path}/hashcat.pot")
|
||||
_append_potfile_arg(cmd)
|
||||
hcatProcess = subprocess.Popen(cmd)
|
||||
try:
|
||||
hcatProcess.wait()
|
||||
@@ -1266,7 +1294,7 @@ def hcatBandrel(hcatHashType, hcatHashFile):
|
||||
mask2.strip(),
|
||||
]
|
||||
cmd.extend(shlex.split(hcatTuning))
|
||||
cmd.append(f"--potfile-path={hate_path}/hashcat.pot")
|
||||
_append_potfile_arg(cmd)
|
||||
hcatProcess = subprocess.Popen(cmd)
|
||||
try:
|
||||
hcatProcess.wait()
|
||||
@@ -1309,7 +1337,7 @@ def hcatBandrel(hcatHashType, hcatHashFile):
|
||||
mask2.strip(),
|
||||
]
|
||||
cmd.extend(shlex.split(hcatTuning))
|
||||
cmd.append(f"--potfile-path={hate_path}/hashcat.pot")
|
||||
_append_potfile_arg(cmd)
|
||||
hcatProcess = subprocess.Popen(cmd)
|
||||
try:
|
||||
hcatProcess.wait()
|
||||
@@ -1351,7 +1379,7 @@ def hcatMiddleCombinator(hcatHashType, hcatHashFile):
|
||||
masks[x],
|
||||
hcatMiddleBaseList,
|
||||
hcatMiddleBaseList,
|
||||
f"--potfile-path={hate_path}/hashcat.pot",
|
||||
*([f"--potfile-path={hcatPotfilePath}"] if hcatPotfilePath else []),
|
||||
]
|
||||
hcatProcess = subprocess.Popen(cmd)
|
||||
try:
|
||||
@@ -1395,7 +1423,7 @@ def hcatThoroughCombinator(hcatHashType, hcatHashFile):
|
||||
hcatThoroughBaseList,
|
||||
]
|
||||
cmd.extend(shlex.split(hcatTuning))
|
||||
cmd.append(f"--potfile-path={hate_path}/hashcat.pot")
|
||||
_append_potfile_arg(cmd)
|
||||
hcatProcess = subprocess.Popen(cmd)
|
||||
try:
|
||||
hcatProcess.wait()
|
||||
@@ -1420,7 +1448,7 @@ def hcatThoroughCombinator(hcatHashType, hcatHashFile):
|
||||
masks[x],
|
||||
hcatThoroughBaseList,
|
||||
hcatThoroughBaseList,
|
||||
f"--potfile-path={hate_path}/hashcat.pot",
|
||||
*([f"--potfile-path={hcatPotfilePath}"] if hcatPotfilePath else []),
|
||||
]
|
||||
hcatProcess = subprocess.Popen(cmd)
|
||||
try:
|
||||
@@ -1450,7 +1478,7 @@ def hcatThoroughCombinator(hcatHashType, hcatHashFile):
|
||||
hcatThoroughBaseList,
|
||||
]
|
||||
cmd.extend(shlex.split(hcatTuning))
|
||||
cmd.append(f"--potfile-path={hate_path}/hashcat.pot")
|
||||
_append_potfile_arg(cmd)
|
||||
hcatProcess = subprocess.Popen(cmd)
|
||||
try:
|
||||
hcatProcess.wait()
|
||||
@@ -1481,7 +1509,7 @@ def hcatThoroughCombinator(hcatHashType, hcatHashFile):
|
||||
hcatThoroughBaseList,
|
||||
]
|
||||
cmd.extend(shlex.split(hcatTuning))
|
||||
cmd.append(f"--potfile-path={hate_path}/hashcat.pot")
|
||||
_append_potfile_arg(cmd)
|
||||
hcatProcess = subprocess.Popen(cmd)
|
||||
hcatProcess.wait()
|
||||
except KeyboardInterrupt:
|
||||
@@ -1506,7 +1534,7 @@ def hcatPathwellBruteForce(hcatHashType, hcatHashFile):
|
||||
os.path.join(hate_path, "masks", "pathwell.hcmask"),
|
||||
]
|
||||
cmd.extend(shlex.split(hcatTuning))
|
||||
cmd.append(f"--potfile-path={hate_path}/hashcat.pot")
|
||||
_append_potfile_arg(cmd)
|
||||
hcatProcess = subprocess.Popen(cmd)
|
||||
try:
|
||||
hcatProcess.wait()
|
||||
@@ -1548,7 +1576,7 @@ def hcatPrince(hcatHashType, hcatHashFile):
|
||||
prince_rule,
|
||||
]
|
||||
hashcat_cmd.extend(shlex.split(hcatTuning))
|
||||
hashcat_cmd.append(f"--potfile-path={hate_path}/hashcat.pot")
|
||||
_append_potfile_arg(hashcat_cmd)
|
||||
with open(prince_base, "rb") as base:
|
||||
prince_proc = subprocess.Popen(prince_cmd, stdin=base, stdout=subprocess.PIPE)
|
||||
hcatProcess = subprocess.Popen(hashcat_cmd, stdin=prince_proc.stdout)
|
||||
@@ -1584,7 +1612,7 @@ def hcatGoodMeasure(hcatHashType, hcatHashFile):
|
||||
hcatGoodMeasureBaseList,
|
||||
]
|
||||
cmd.extend(shlex.split(hcatTuning))
|
||||
cmd.append(f"--potfile-path={hate_path}/hashcat.pot")
|
||||
_append_potfile_arg(cmd)
|
||||
hcatProcess = subprocess.Popen(cmd)
|
||||
try:
|
||||
hcatProcess.wait()
|
||||
@@ -1617,7 +1645,7 @@ def hcatLMtoNT():
|
||||
"?1?1?1?1?1?1?1",
|
||||
]
|
||||
cmd.extend(shlex.split(hcatTuning))
|
||||
cmd.append(f"--potfile-path={hate_path}/hashcat.pot")
|
||||
_append_potfile_arg(cmd)
|
||||
hcatProcess = subprocess.Popen(cmd)
|
||||
try:
|
||||
hcatProcess.wait()
|
||||
@@ -1667,7 +1695,7 @@ def hcatLMtoNT():
|
||||
),
|
||||
]
|
||||
cmd.extend(shlex.split(hcatTuning))
|
||||
cmd.append(f"--potfile-path={hate_path}/hashcat.pot")
|
||||
_append_potfile_arg(cmd)
|
||||
hcatProcess = subprocess.Popen(cmd)
|
||||
try:
|
||||
hcatProcess.wait()
|
||||
@@ -1706,7 +1734,7 @@ def hcatRecycle(hcatHashType, hcatHashFile, hcatNewPasswords):
|
||||
rule_path,
|
||||
]
|
||||
cmd.extend(shlex.split(hcatTuning))
|
||||
cmd.append(f"--potfile-path={hate_path}/hashcat.pot")
|
||||
_append_potfile_arg(cmd)
|
||||
hcatProcess = subprocess.Popen(cmd)
|
||||
try:
|
||||
hcatProcess.wait()
|
||||
@@ -2412,6 +2440,10 @@ def bandrel_method():
|
||||
return _attacks.bandrel_method(_attack_ctx())
|
||||
|
||||
|
||||
def loopback_attack():
|
||||
return _attacks.loopback_attack(_attack_ctx())
|
||||
|
||||
|
||||
# convert hex words for recycling
|
||||
def convert_hex(working_file):
|
||||
processed_words = []
|
||||
@@ -2605,6 +2637,7 @@ def get_main_menu_options():
|
||||
"11": middle_combinator,
|
||||
"12": thorough_combinator,
|
||||
"13": bandrel_method,
|
||||
"14": loopback_attack,
|
||||
"90": download_hashmob_rules,
|
||||
"91": weakpass_wordlist_menu,
|
||||
"92": download_hashmob_wordlists,
|
||||
@@ -3138,6 +3171,7 @@ def main():
|
||||
print("\t(11) Middle Combinator Attack")
|
||||
print("\t(12) Thorough Combinator Attack")
|
||||
print("\t(13) Bandrel Methodology")
|
||||
print("\t(14) Loopback Attack")
|
||||
print("\n\t(90) Download rules from Hashmob.net")
|
||||
print("\n\t(91) Download wordlists from Weakpass")
|
||||
print("\t(92) Download wordlists from Hashmob.net")
|
||||
|
||||
@@ -0,0 +1,172 @@
|
||||
import os
|
||||
import json
|
||||
import shutil
|
||||
import subprocess
|
||||
import shlex
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
_TEST_HASH = "994a24ad0d9ac6f1fd7d4d75adffeda2"
|
||||
|
||||
|
||||
def _format_hashcat_cmd(cmd: list[str]) -> str:
|
||||
# Mirror hate_crack's debug printing: safe shell-style quoting.
|
||||
return " ".join(shlex.quote(part) for part in cmd)
|
||||
|
||||
|
||||
def _get_hcat_tuning_args(repo_root: Path) -> list[str]:
|
||||
config_path = repo_root / "config.json"
|
||||
if not config_path.is_file():
|
||||
return []
|
||||
try:
|
||||
config = json.loads(config_path.read_text())
|
||||
except Exception:
|
||||
return []
|
||||
|
||||
tuning = (config.get("hcatTuning") or "").strip()
|
||||
if not tuning:
|
||||
return []
|
||||
return shlex.split(tuning)
|
||||
|
||||
|
||||
def _hashcat_sessions_writable() -> bool:
|
||||
"""
|
||||
Hashcat writes session files under ~/.hashcat/sessions on macOS/homebrew builds.
|
||||
If that location is not writable (sandbox/MDM), running hashcat will emit stderr.
|
||||
"""
|
||||
sessions_dir = Path.home() / ".hashcat" / "sessions"
|
||||
try:
|
||||
sessions_dir.mkdir(parents=True, exist_ok=True)
|
||||
probe = sessions_dir / f"pytest_write_probe_{os.getpid()}"
|
||||
probe.write_text("probe")
|
||||
probe.unlink(missing_ok=True)
|
||||
return True
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
|
||||
def _run_hashcat(
|
||||
cmd: list[str],
|
||||
cwd: Path,
|
||||
*,
|
||||
timeout_s: int = 60,
|
||||
capsys=None,
|
||||
show_output: bool = False,
|
||||
show_cmd: bool = False,
|
||||
) -> subprocess.CompletedProcess:
|
||||
"""
|
||||
Run hashcat and skip (not fail) on common local-environment issues.
|
||||
|
||||
This repo's normal test suite is offline/mocked; this test is opt-in and
|
||||
depends on a local hashcat installation that can write its session files.
|
||||
"""
|
||||
if show_cmd and capsys is not None:
|
||||
with capsys.disabled():
|
||||
print("\n[DEBUG] hashcat cmd: " + _format_hashcat_cmd(cmd))
|
||||
|
||||
try:
|
||||
result = subprocess.run(
|
||||
cmd,
|
||||
cwd=str(cwd),
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=timeout_s,
|
||||
)
|
||||
except FileNotFoundError:
|
||||
pytest.skip("hashcat not available in PATH")
|
||||
except subprocess.TimeoutExpired:
|
||||
pytest.fail(f"hashcat timed out after {timeout_s}s: {cmd!r}")
|
||||
|
||||
combined = (result.stdout or "") + (result.stderr or "")
|
||||
|
||||
if show_output and capsys is not None:
|
||||
with capsys.disabled():
|
||||
print("\n[hashcat stdout]\n" + (result.stdout or ""))
|
||||
print("\n[hashcat stderr]\n" + (result.stderr or ""))
|
||||
|
||||
# If hashcat crashed, subprocess uses a negative return code (signal).
|
||||
if result.returncode < 0:
|
||||
pytest.fail(
|
||||
f"hashcat terminated by signal {-result.returncode}. stdout={result.stdout!r} stderr={result.stderr!r}"
|
||||
)
|
||||
|
||||
# Per request: fail on any stderr output (warnings/errors are treated as failure).
|
||||
if (result.stderr or "").strip():
|
||||
pytest.fail(
|
||||
f"hashcat wrote to stderr (treated as failure). cmd={_format_hashcat_cmd(cmd)!r} stderr={result.stderr!r}"
|
||||
)
|
||||
|
||||
assert "Segmentation fault" not in combined
|
||||
assert "core dumped" not in combined.lower()
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def test_toggle_rule_parses_with_and_without_loopback(tmp_path: Path, capsys):
|
||||
"""
|
||||
Execute the two hashcat command-lines requested (with an empty wordlist),
|
||||
primarily to ensure hashcat does not crash while parsing/using the rule file.
|
||||
"""
|
||||
if shutil.which("hashcat") is None:
|
||||
pytest.skip("hashcat not available in PATH")
|
||||
if not _hashcat_sessions_writable():
|
||||
pytest.skip("hashcat session directory (~/.hashcat/sessions) is not writable")
|
||||
|
||||
show_output = os.environ.get("HATE_CRACK_SHOW_HASHCAT_OUTPUT") == "1"
|
||||
show_cmd = (
|
||||
os.environ.get("HATE_CRACK_SHOW_HASHCAT_CMD") == "1"
|
||||
or os.environ.get("HATE_CRACK_SHOW_HASHCAT_OUTPUT") == "1"
|
||||
)
|
||||
|
||||
repo_root = Path(__file__).resolve().parents[1]
|
||||
tuning_args = _get_hcat_tuning_args(repo_root)
|
||||
src_rule = repo_root / "rules" / "toggles-lm-ntlm.rule"
|
||||
if not src_rule.is_file():
|
||||
pytest.skip("rules/toggles-lm-ntlm.rule not found")
|
||||
|
||||
# Mirror the requested relative `rules/...` path in a temp working dir.
|
||||
(tmp_path / "rules").mkdir(parents=True, exist_ok=True)
|
||||
rule_path = tmp_path / "rules" / "toggles-lm-ntlm.rule"
|
||||
shutil.copy2(src_rule, rule_path)
|
||||
|
||||
# Equivalent to: `echo > empty.txt`
|
||||
(tmp_path / "empty.txt").write_text("")
|
||||
|
||||
cmd_with_loopback = [
|
||||
"hashcat",
|
||||
*tuning_args,
|
||||
"-m",
|
||||
"1000",
|
||||
"empty.txt",
|
||||
"--loopback",
|
||||
"-r",
|
||||
"rules/toggles-lm-ntlm.rule",
|
||||
_TEST_HASH,
|
||||
]
|
||||
cmd_without_loopback = [
|
||||
"hashcat",
|
||||
*tuning_args,
|
||||
"-m",
|
||||
"1000",
|
||||
"empty.txt",
|
||||
"-r",
|
||||
"rules/toggles-lm-ntlm.rule",
|
||||
_TEST_HASH,
|
||||
]
|
||||
|
||||
_run_hashcat(
|
||||
cmd_with_loopback,
|
||||
cwd=tmp_path,
|
||||
capsys=capsys,
|
||||
show_output=show_output,
|
||||
show_cmd=show_cmd,
|
||||
)
|
||||
_run_hashcat(
|
||||
cmd_without_loopback,
|
||||
cwd=tmp_path,
|
||||
capsys=capsys,
|
||||
show_output=show_output,
|
||||
show_cmd=show_cmd,
|
||||
)
|
||||
@@ -24,6 +24,7 @@ MENU_OPTION_TEST_CASES = [
|
||||
("11", CLI_MODULE._attacks, "middle_combinator", "middle"),
|
||||
("12", CLI_MODULE._attacks, "thorough_combinator", "thorough"),
|
||||
("13", CLI_MODULE._attacks, "bandrel_method", "bandrel"),
|
||||
("14", CLI_MODULE._attacks, "loopback_attack", "loopback"),
|
||||
("90", CLI_MODULE, "download_hashmob_rules", "hashmob-rules"),
|
||||
("91", CLI_MODULE, "weakpass_wordlist_menu", "weakpass-menu"),
|
||||
("92", CLI_MODULE, "download_hashmob_wordlists", "hashmob-wordlists"),
|
||||
|
||||
Reference in New Issue
Block a user