mirror of
https://github.com/trustedsec/hate_crack.git
synced 2026-03-12 21:23:05 -07:00
added debug options
This commit is contained in:
@@ -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",
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user