added debug options

This commit is contained in:
Justin Bollinger
2026-02-09 14:30:37 -05:00
parent 1fab01d444
commit 90696278d3
5 changed files with 390 additions and 74 deletions

View File

@@ -3,6 +3,7 @@
"hcatBin": "hashcat", "hcatBin": "hashcat",
"hcatTuning": "--force --remove", "hcatTuning": "--force --remove",
"hcatPotfilePath": "~/.hashcat/hashcat.potfile", "hcatPotfilePath": "~/.hashcat/hashcat.potfile",
"hcatDebugLogPath": "./hashcat_debug",
"hcatWordlists": "/Passwords/wordlists", "hcatWordlists": "/Passwords/wordlists",
"hcatOptimizedWordlists": "/Passwords/optimized_wordlists", "hcatOptimizedWordlists": "/Passwords/optimized_wordlists",
"rules_directory": "/path/to/hashcat/rules", "rules_directory": "/path/to/hashcat/rules",

View File

@@ -962,14 +962,16 @@ class HashviewAPI:
"combined_file": combined_file, "combined_file": combined_file,
} }
def download_found_hashes(self, customer_id, hashfile_id, output_file=None): def download_found_hashes(self, customer_id, hashfile_id, output_file=None, hash_type=None):
import sys import sys
import subprocess
url = f"{self.base_url}/v1/hashfiles/{hashfile_id}/found" url = f"{self.base_url}/v1/hashfiles/{hashfile_id}/found"
resp = self.session.get(url, headers=self._auth_headers(), stream=True) resp = self.session.get(url, headers=self._auth_headers(), stream=True)
resp.raise_for_status() resp.raise_for_status()
if output_file is None: if output_file is None:
output_file = f"found_{customer_id}_{hashfile_id}.txt" output_file = f"found_{customer_id}_{hashfile_id}.txt"
total = int(resp.headers.get("content-length", 0)) total = int(resp.headers.get("content-length", 0))
downloaded = 0 downloaded = 0
chunk_size = 8192 chunk_size = 8192
@@ -992,81 +994,110 @@ class HashviewAPI:
if total == 0: if total == 0:
print(f"Downloaded {downloaded} bytes.") print(f"Downloaded {downloaded} bytes.")
# Combine with corresponding left file output if it exists # Split found file into hashes and clears
# Only combine if the output file matches the expected naming pattern output_dir = os.path.dirname(os.path.abspath(output_file)) or os.getcwd()
found_hashes_file = os.path.join(output_dir, f"found_hashes_{customer_id}_{hashfile_id}.txt")
found_clears_file = os.path.join(output_dir, f"found_clears_{customer_id}_{hashfile_id}.txt")
hashes_count = 0
clears_count = 0
combined_count = 0 combined_count = 0
combined_file = None combined_file = None
# Extract customer_id and hashfile_id from the output filename to ensure proper matching try:
import re with open(found_hashes_file, "w", encoding="utf-8") as hf, \
open(found_clears_file, "w", encoding="utf-8") as cf:
output_basename = os.path.basename(output_file) with open(output_file, "r", encoding="utf-8", errors="ignore") as f:
# Match pattern: found_{customer_id}_{hashfile_id}.txt for line in f:
match = re.match(r"found_(\d+)_(\d+)\.txt$", output_basename) line = line.strip()
if line:
if match: parts = line.rsplit(":", 1) # Split on last colon
found_customer_id = match.group(1) if len(parts) == 2:
found_hashfile_id = match.group(2) hash_part, clear_part = parts
hf.write(hash_part + "\n")
# Only proceed if the IDs from filename match the actual download IDs cf.write(clear_part + "\n")
if ( hashes_count += 1
str(customer_id) == found_customer_id clears_count += 1
and str(hashfile_id) == found_hashfile_id
): print(f"✓ Split found file into {hashes_count} hashes and {clears_count} clears")
left_base = f"left_{customer_id}_{hashfile_id}.txt"
# Run hashcat to combine them into an output file
# Check for regular format .out file combined_file = output_file + ".out"
left_out = left_base + ".out" try:
if not os.path.exists(left_out): tuning_args = get_hcat_tuning_args()
# Check for pwdump format .nt.txt.out file
left_out = f"left_{customer_id}_{hashfile_id}.nt.txt.out" # Create temporary outfile for hashcat
temp_outfile = output_file + ".tmp"
if os.path.exists(left_out):
# Read existing hashes from .out file into a set if self.debug:
found_hashes = set() print(f"[DEBUG] download_found_hashes: hash_type={hash_type}, type={type(hash_type)}")
with open(left_out, "r", encoding="utf-8", errors="ignore") as f:
for line in f: # Build command with hash type if provided
line = line.strip() cmd = ["hashcat", *tuning_args]
if line: if hash_type:
found_hashes.add(line) cmd.extend(["-m", str(hash_type)])
cmd.extend([
original_count = len(found_hashes) found_hashes_file,
found_clears_file,
# Read and add hashes from the downloaded found file "--outfile",
with open(output_file, "r", encoding="utf-8", errors="ignore") as f: temp_outfile,
for line in f: "--outfile-format=1,2",
line = line.strip() ])
if line:
found_hashes.add(line) if self.debug:
print(f"[DEBUG] Running command: {' '.join(cmd)}")
combined_count = len(found_hashes) - original_count
print(f"Running: {' '.join(cmd)}")
# Write combined results to the .out file
combined_file = left_out result = subprocess.run(cmd, stderr=subprocess.PIPE, stdout=subprocess.PIPE, text=True, timeout=300)
with open(combined_file, "w", encoding="utf-8") as f:
for line in sorted(found_hashes): if result.returncode != 0:
f.write(line + "\n") print(f"Warning: hashcat exited with code {result.returncode}")
if result.stderr:
print(f"Combined {combined_count} new hashes from {output_file}") print(f" stderr: {result.stderr}")
print(
f"Total unique hashes in {combined_file}: {len(found_hashes)}" # Write the output file
) if os.path.exists(temp_outfile):
with open(temp_outfile, "r", encoding="utf-8", errors="ignore") as tmp_f:
with open(combined_file, "w", encoding="utf-8") as out_f:
out_f.write(tmp_f.read())
# Delete the found file after successful merge # Count lines in output
with open(combined_file, "r", encoding="utf-8", errors="ignore") as f:
combined_count = len(f.readlines())
print(f"✓ Created {combined_file} (total lines: {combined_count})")
# Clean up temp file
try: try:
os.remove(output_file) os.remove(temp_outfile)
print(f"Deleted {output_file} after merge") except Exception:
pass
else:
print("Note: No cracked hashes generated")
except FileNotFoundError:
print("✗ Error: hashcat not found in PATH")
except subprocess.TimeoutExpired:
print("✗ Error: hashcat execution timed out")
except Exception as e:
print(f"✗ Error running hashcat: {e}")
# Clean up temporary files (keep when debug is enabled)
if not self.debug:
files_to_delete = [found_hashes_file, found_clears_file]
for temp_file in files_to_delete:
try:
if os.path.exists(temp_file):
os.remove(temp_file)
except Exception as e: except Exception as e:
print(f"Warning: Could not delete {output_file}: {e}") if self.debug:
print(f"Warning: Could not delete {temp_file}: {e}")
else: else:
print( print("Debug enabled: keeping split files")
f"Skipping combine: customer_id/hashfile_id mismatch (expected {customer_id}/{hashfile_id}, filename has {found_customer_id}/{found_hashfile_id})"
) except Exception as e:
else: print(f"✗ Error splitting found file: {e}")
print(
f"Skipping combine: output filename '{output_basename}' doesn't match expected pattern 'found_<customer_id>_<hashfile_id>.txt'"
)
return { return {
"output_file": output_file, "output_file": output_file,
"size": downloaded, "size": downloaded,

View File

@@ -327,7 +327,75 @@ def fingerprint_crack(ctx: Any) -> None:
def combinator_crack(ctx: Any) -> None: def combinator_crack(ctx: Any) -> None:
ctx.hcatCombination(ctx.hcatHashType, ctx.hcatHashFile) print("\n" + "=" * 60)
print("COMBINATOR ATTACK")
print("=" * 60)
print("This attack combines two wordlists to generate candidates.")
print("Example: wordlist1='password' + wordlist2='123' = 'password123'")
print("=" * 60)
use_default = (
input("\nUse default combinator wordlists from config? (Y/n): ").strip().lower()
)
if use_default != "n":
print("\nUsing default wordlist(s) from config:")
if isinstance(ctx.hcatCombinationWordlist, list):
for wl in ctx.hcatCombinationWordlist:
print(f" - {wl}")
wordlists = ctx.hcatCombinationWordlist
else:
print(f" - {ctx.hcatCombinationWordlist}")
wordlists = [ctx.hcatCombinationWordlist]
else:
print("\nSelect wordlists for combinator attack.")
print("You need to provide exactly 2 wordlists.")
print("You can enter:")
print(" - Two file paths separated by commas")
print(" - Press TAB to autocomplete file paths")
selection = ctx.select_file_with_autocomplete(
"Enter 2 wordlist files (comma-separated)", allow_multiple=True
)
if not selection:
print("No wordlists selected. Aborting combinator attack.")
return
if isinstance(selection, str):
wordlists = [selection]
else:
wordlists = selection
if len(wordlists) < 2:
print("\n[!] Combinator attack requires at least 2 wordlists.")
print("Aborting combinator attack.")
return
valid_wordlists = []
for wl in wordlists[:2]: # Only use first 2
resolved = ctx._resolve_wordlist_path(wl, ctx.hcatWordlists)
if os.path.isfile(resolved):
valid_wordlists.append(resolved)
print(f"✓ Found: {resolved}")
else:
print(f"✗ Not found: {resolved}")
if len(valid_wordlists) < 2:
print("\nCould not find 2 valid wordlists. Aborting combinator attack.")
return
wordlists = valid_wordlists
wordlists = [ctx._resolve_wordlist_path(wl, ctx.hcatWordlists) for wl in wordlists[:2]]
print(f"\nStarting combinator attack with 2 wordlists:")
print(f" Wordlist 1: {wordlists[0]}")
print(f" Wordlist 2: {wordlists[1]}")
print(f"Hash type: {ctx.hcatHashType}")
print(f"Hash file: {ctx.hcatHashFile}")
ctx.hcatCombination(ctx.hcatHashType, ctx.hcatHashFile, wordlists)
def hybrid_crack(ctx: Any) -> None: def hybrid_crack(ctx: Any) -> None:

View File

@@ -3,6 +3,7 @@
"hcatBin": "hashcat", "hcatBin": "hashcat",
"hcatTuning": "--force --remove", "hcatTuning": "--force --remove",
"hcatPotfilePath": "~/.hashcat/hashcat.potfile", "hcatPotfilePath": "~/.hashcat/hashcat.potfile",
"hcatDebugLogPath": "./hashcat_debug",
"hcatWordlists": "/Passwords/wordlists", "hcatWordlists": "/Passwords/wordlists",
"hcatOptimizedWordlists": "/Passwords/optimized_wordlists", "hcatOptimizedWordlists": "/Passwords/optimized_wordlists",
"rules_directory": "/path/to/hashcat/rules", "rules_directory": "/path/to/hashcat/rules",

View File

@@ -454,6 +454,18 @@ except KeyError as e:
) )
hcatGoodMeasureBaseList = default_config[e.args[0]] hcatGoodMeasureBaseList = default_config[e.args[0]]
try:
hcatDebugLogPath = config_parser.get("hcatDebugLogPath", "./hashcat_debug")
# Expand user home directory if present
hcatDebugLogPath = os.path.expanduser(hcatDebugLogPath)
except KeyError as e:
print(
"{0} is not defined in config.json using defaults from config.json.example".format(
e
)
)
hcatDebugLogPath = os.path.expanduser(default_config.get("hcatDebugLogPath", "./hashcat_debug"))
hcatExpanderBin = "expander.bin" hcatExpanderBin = "expander.bin"
hcatCombinatorBin = "combinator.bin" hcatCombinatorBin = "combinator.bin"
hcatPrinceBin = "pp64.bin" hcatPrinceBin = "pp64.bin"
@@ -689,6 +701,28 @@ def _debug_cmd(cmd):
print(f"[DEBUG] hashcat cmd: {_format_cmd(cmd)}") print(f"[DEBUG] hashcat cmd: {_format_cmd(cmd)}")
def _add_debug_mode_for_rules(cmd):
"""Add debug mode arguments to hashcat command if rules are being used.
This function detects if rules are present in the command (by looking for -r flags)
and adds --debug-mode=1 and --debug-file=<path> if rules are found.
Debug log path is configurable via hcatDebugLogPath in config.json
"""
if "-r" in cmd:
# Create debug output directory if it doesn't exist
os.makedirs(hcatDebugLogPath, exist_ok=True)
# Create a debug output filename based on the session ID or hash file
debug_filename = os.path.join(hcatDebugLogPath, "hashcat_debug.log")
if "--session" in cmd:
session_idx = cmd.index("--session") + 1
if session_idx < len(cmd):
debug_filename = os.path.join(hcatDebugLogPath, f"hashcat_debug_{cmd[session_idx]}.log")
cmd.extend(["--debug-mode", "4", "--debug-file", debug_filename])
return cmd
# Sanitize filename for use as hashcat session name # Sanitize filename for use as hashcat session name
def generate_session_id(): def generate_session_id():
"""Sanitize the hashfile name for use as a hashcat session name """Sanitize the hashfile name for use as a hashcat session name
@@ -932,6 +966,7 @@ def hcatDictionary(hcatHashType, hcatHashFile):
cmd.extend(["-r", rule_best66]) cmd.extend(["-r", rule_best66])
cmd.extend(shlex.split(hcatTuning)) cmd.extend(shlex.split(hcatTuning))
_append_potfile_arg(cmd) _append_potfile_arg(cmd)
cmd = _add_debug_mode_for_rules(cmd)
hcatProcess = subprocess.Popen(cmd) hcatProcess = subprocess.Popen(cmd)
try: try:
hcatProcess.wait() hcatProcess.wait()
@@ -956,6 +991,7 @@ def hcatDictionary(hcatHashType, hcatHashFile):
] ]
cmd.extend(shlex.split(hcatTuning)) cmd.extend(shlex.split(hcatTuning))
_append_potfile_arg(cmd) _append_potfile_arg(cmd)
cmd = _add_debug_mode_for_rules(cmd)
hcatProcess = subprocess.Popen(cmd) hcatProcess = subprocess.Popen(cmd)
try: try:
hcatProcess.wait() hcatProcess.wait()
@@ -979,6 +1015,7 @@ def hcatDictionary(hcatHashType, hcatHashFile):
] ]
cmd.extend(shlex.split(hcatTuning)) cmd.extend(shlex.split(hcatTuning))
_append_potfile_arg(cmd) _append_potfile_arg(cmd)
cmd = _add_debug_mode_for_rules(cmd)
hcatProcess = subprocess.Popen(cmd) hcatProcess = subprocess.Popen(cmd)
try: try:
hcatProcess.wait() hcatProcess.wait()
@@ -1020,6 +1057,7 @@ def hcatQuickDictionary(
cmd.extend(shlex.split(hcatChains)) cmd.extend(shlex.split(hcatChains))
cmd.extend(shlex.split(hcatTuning)) cmd.extend(shlex.split(hcatTuning))
_append_potfile_arg(cmd, use_potfile_path=use_potfile_path, potfile_path=potfile_path) _append_potfile_arg(cmd, use_potfile_path=use_potfile_path, potfile_path=potfile_path)
cmd = _add_debug_mode_for_rules(cmd)
_debug_cmd(cmd) _debug_cmd(cmd)
hcatProcess = subprocess.Popen(cmd) hcatProcess = subprocess.Popen(cmd)
try: try:
@@ -1178,9 +1216,35 @@ def hcatFingerprint(
# Combinator Attack # Combinator Attack
def hcatCombination(hcatHashType, hcatHashFile): def hcatCombination(hcatHashType, hcatHashFile, wordlists=None):
global hcatCombinationCount global hcatCombinationCount
global hcatProcess global hcatProcess
# Use provided wordlists or fall back to config default
if wordlists is None:
wordlists = hcatCombinationWordlist
# Ensure wordlists is a list with at least 2 items
if not isinstance(wordlists, list):
wordlists = [wordlists]
if len(wordlists) < 2:
print("[!] Combinator attack requires at least 2 wordlists.")
return
# Resolve wordlist paths
resolved_wordlists = []
for wordlist in wordlists[:2]: # Only use first 2 wordlists
resolved = _resolve_wordlist_path(wordlist, hcatWordlists)
if os.path.isfile(resolved):
resolved_wordlists.append(resolved)
else:
print(f"[!] Wordlist not found: {resolved}")
if len(resolved_wordlists) < 2:
print("[!] Could not find 2 valid wordlists. Aborting combinator attack.")
return
cmd = [ cmd = [
hcatBin, hcatBin,
"-m", "-m",
@@ -1192,8 +1256,8 @@ def hcatCombination(hcatHashType, hcatHashFile):
f"{hcatHashFile}.out", f"{hcatHashFile}.out",
"-a", "-a",
"1", "1",
hcatCombinationWordlist[0], resolved_wordlists[0],
hcatCombinationWordlist[1], resolved_wordlists[1],
] ]
cmd.extend(shlex.split(hcatTuning)) cmd.extend(shlex.split(hcatTuning))
_append_potfile_arg(cmd) _append_potfile_arg(cmd)
@@ -1620,6 +1684,7 @@ def hcatPrince(hcatHashType, hcatHashFile):
] ]
hashcat_cmd.extend(shlex.split(hcatTuning)) hashcat_cmd.extend(shlex.split(hcatTuning))
_append_potfile_arg(hashcat_cmd) _append_potfile_arg(hashcat_cmd)
hashcat_cmd = _add_debug_mode_for_rules(hashcat_cmd)
with open(prince_base, "rb") as base: with open(prince_base, "rb") as base:
prince_proc = subprocess.Popen(prince_cmd, stdin=base, stdout=subprocess.PIPE) prince_proc = subprocess.Popen(prince_cmd, stdin=base, stdout=subprocess.PIPE)
hcatProcess = subprocess.Popen(hashcat_cmd, stdin=prince_proc.stdout) hcatProcess = subprocess.Popen(hashcat_cmd, stdin=prince_proc.stdout)
@@ -1656,6 +1721,7 @@ def hcatGoodMeasure(hcatHashType, hcatHashFile):
] ]
cmd.extend(shlex.split(hcatTuning)) cmd.extend(shlex.split(hcatTuning))
_append_potfile_arg(cmd) _append_potfile_arg(cmd)
cmd = _add_debug_mode_for_rules(cmd)
hcatProcess = subprocess.Popen(cmd) hcatProcess = subprocess.Popen(cmd)
try: try:
hcatProcess.wait() hcatProcess.wait()
@@ -1739,6 +1805,7 @@ def hcatLMtoNT():
] ]
cmd.extend(shlex.split(hcatTuning)) cmd.extend(shlex.split(hcatTuning))
_append_potfile_arg(cmd) _append_potfile_arg(cmd)
cmd = _add_debug_mode_for_rules(cmd)
hcatProcess = subprocess.Popen(cmd) hcatProcess = subprocess.Popen(cmd)
try: try:
hcatProcess.wait() hcatProcess.wait()
@@ -1778,6 +1845,7 @@ def hcatRecycle(hcatHashType, hcatHashFile, hcatNewPasswords):
] ]
cmd.extend(shlex.split(hcatTuning)) cmd.extend(shlex.split(hcatTuning))
_append_potfile_arg(cmd) _append_potfile_arg(cmd)
cmd = _add_debug_mode_for_rules(cmd)
hcatProcess = subprocess.Popen(cmd) hcatProcess = subprocess.Popen(cmd)
try: try:
hcatProcess.wait() hcatProcess.wait()
@@ -1897,6 +1965,7 @@ def hashview_api():
menu_options.append(("upload_wordlist", "Upload Wordlist")) menu_options.append(("upload_wordlist", "Upload Wordlist"))
menu_options.append(("download_wordlist", "Download Wordlist")) menu_options.append(("download_wordlist", "Download Wordlist"))
menu_options.append(("download_left", "Download Left Hashes (with automatic merge if found)")) menu_options.append(("download_left", "Download Left Hashes (with automatic merge if found)"))
menu_options.append(("download_found", "Download Found Hashes (with automatic split)"))
if hcatHashFile: if hcatHashFile:
menu_options.append(("upload_hashfile_job", "Upload Hashfile and Create Job")) menu_options.append(("upload_hashfile_job", "Upload Hashfile and Create Job"))
menu_options.append(("back", "Back to Main Menu")) menu_options.append(("back", "Back to Main Menu"))
@@ -2415,6 +2484,152 @@ def hashview_api():
except Exception as e: except Exception as e:
print(f"\n✗ Error downloading hashes: {str(e)}") print(f"\n✗ Error downloading hashes: {str(e)}")
elif option_key == "download_found":
# Download found hashes
try:
while True:
# First, list customers to help user select
customers_result = api_harness.list_customers()
customers = (
customers_result.get("customers", [])
if isinstance(customers_result, dict)
else customers_result
)
if customers:
api_harness.display_customers_multicolumn(customers)
else:
print("\nNo customers found.")
# Select or create customer
customer_input = input(
"\nEnter customer ID or N to create new: "
).strip()
if customer_input.lower() == "n":
customer_name = input("Enter customer name: ").strip()
if customer_name:
try:
result = api_harness.create_customer(customer_name)
print(
f"\n✓ Success: {result.get('msg', 'Customer created')}"
)
customer_id = result.get("customer_id") or result.get("id")
if not customer_id:
print("\n✗ Error: Customer ID not returned.")
continue
print(f" Customer ID: {customer_id}")
except Exception as e:
print(f"\n✗ Error creating customer: {str(e)}")
continue
else:
print("\n✗ Error: Customer name cannot be empty.")
continue
else:
try:
customer_id = int(customer_input)
except ValueError:
print(
"\n✗ Error: Invalid ID entered. Please enter a numeric ID or N."
)
continue
# List hashfiles for the customer
try:
customer_hashfiles = api_harness.get_customer_hashfiles(
customer_id
)
if not customer_hashfiles:
print(f"\nNo hashfiles found for customer ID {customer_id}")
continue
print("\n" + "=" * 120)
print(f"Hashfiles for Customer ID {customer_id}:")
print("=" * 120)
print(f"{'ID':<10} {'Hash Type':<10} {'Name':<96}")
print("-" * 120)
hashfile_map = {}
for hf in customer_hashfiles:
hf_id = hf.get("id")
hf_name = hf.get("name", "N/A")
hf_type = hf.get("hash_type") or hf.get("hashtype") or "N/A"
if hf_id is None:
continue
# Truncate long names to fit within 120 columns
if len(str(hf_name)) > 96:
hf_name = str(hf_name)[:93] + "..."
if debug_mode:
print(f"[DEBUG] Hashfile {hf_id}: hash_type={hf.get('hash_type')}, hashtype={hf.get('hashtype')}, combined={hf_type}")
print(f"{hf_id:<10} {hf_type:<10} {hf_name:<96}")
hashfile_map[int(hf_id)] = hf_type
print("=" * 120)
print(f"Total: {len(hashfile_map)} hashfile(s)")
except Exception as e:
print(f"\nWarning: Could not list hashfiles: {e}")
continue
while True:
try:
hashfile_id_input = input("\nEnter hashfile ID: ").strip()
hashfile_id = int(hashfile_id_input)
except ValueError:
print("\n✗ Error: Invalid ID entered. Please enter a numeric ID.")
continue
if hashfile_id not in hashfile_map:
print("\n✗ Error: Hashfile ID not in the list. Please try again.")
continue
break
break
# Set output filename automatically
output_file = f"found_{customer_id}_{hashfile_id}.txt"
# Get hash type for hashcat from the hashfile map
selected_hash_type = hashfile_map.get(hashfile_id)
if debug_mode:
print(f"[DEBUG] selected_hash_type from map: {selected_hash_type}")
if not selected_hash_type or selected_hash_type == "N/A":
try:
details = api_harness.get_hashfile_details(hashfile_id)
selected_hash_type = details.get("hashtype")
if debug_mode:
print(f"[DEBUG] selected_hash_type from get_hashfile_details: {selected_hash_type}")
except Exception as e:
if debug_mode:
print(f"[DEBUG] Error fetching hashfile details: {e}")
selected_hash_type = None
# Download the found hashes
if debug_mode:
print(f"[DEBUG] Calling download_found_hashes with hash_type={selected_hash_type}")
download_result = api_harness.download_found_hashes(
customer_id, hashfile_id, output_file
)
print(f"\n✓ Success: Downloaded {download_result['size']} bytes")
print(f" File: {download_result['output_file']}")
if selected_hash_type:
print(f" Hash mode: {selected_hash_type}")
# Ask if user wants to switch to this hashfile
switch = (
input("\nSwitch to this hashfile for cracking? (Y/n): ")
.strip()
.lower()
)
if switch != "n":
hcatHashFile = download_result["output_file"]
if selected_hash_type:
hcatHashType = str(selected_hash_type)
else:
hcatHashType = "1000" # Default to NTLM if unavailable
print(f"✓ Switched to hashfile: {hcatHashFile}")
print("\nReturning to main menu to start cracking...")
return # Exit hashview menu and return to main menu
except ValueError:
print("\n✗ Error: Invalid ID entered. Please enter a numeric ID.")
except Exception as e:
print(f"\n✗ Error downloading found hashes: {str(e)}")
elif option_key == "back": elif option_key == "back":
break break