From 874c30dcafffe3285d2f978663605e98be9f2d95 Mon Sep 17 00:00:00 2001 From: Justin Bollinger Date: Thu, 18 Jun 2026 17:40:36 -0400 Subject: [PATCH] fix(hashview): prompt for hash type when session type is unset The download-hashes flow scoped the customer hashfile listing to hcatHashType, but that is None when the Hashview menu is entered without a hashfile loaded this session -> "No hashfiles of type None found". Since Hashview only lists hashfiles per type, prompt for a hashcat hash-type when none is set (Q cancels back to the menu) and use it for the listing. Downstream selection still derives the real type from the chosen hashfile. Co-Authored-By: Claude Opus 4.8 (1M context) --- README.md | 2 +- hate_crack/main.py | 32 +++++++++++++++++++++++++++----- 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index d31bcd8..9e2ce22 100644 --- a/README.md +++ b/README.md @@ -913,7 +913,7 @@ Interactive menu for downloading and managing wordlists from Weakpass.com via Bi ### Version History Version 2.10.6 - - Fixed the Hashview integration calling API routes that don't exist in Hashview (verified against v0.8.3-dev), which 404'd as soon as a customer ID was entered ("Could not list hashfiles"). The customer→hashfile listing relied on a phantom `GET /v1/hashfiles` list-all route; it now enumerates via the real `GET /v1/hashfiles/hash_type/` endpoint, scoped to the session hash type, and filters by customer. Additional client-side route fixes: hashfile hash-type lookup now uses `GET /v1/getHashType/`; "left" (uncracked) hash download uses `GET /v1/hashfiles/`; `delete_job` uses `DELETE /v1/jobs/`; `start_job` uses `POST`. Hashview exposes no stop-job route, so `stop_job` now raises with guidance to use `delete_job`; and no bulk cracked-hash export exists, so the best-effort "found" merge degrades gracefully + - Fixed the Hashview integration calling API routes that don't exist in Hashview (verified against v0.8.3-dev), which 404'd as soon as a customer ID was entered ("Could not list hashfiles"). The customer→hashfile listing relied on a phantom `GET /v1/hashfiles` list-all route; it now enumerates via the real `GET /v1/hashfiles/hash_type/` endpoint, scoped to the session hash type (prompting for a hash type when none is loaded this session), and filters by customer. Additional client-side route fixes: hashfile hash-type lookup now uses `GET /v1/getHashType/`; "left" (uncracked) hash download uses `GET /v1/hashfiles/`; `delete_job` uses `DELETE /v1/jobs/`; `start_job` uses `POST`. Hashview exposes no stop-job route, so `stop_job` now raises with guidance to use `delete_job`; and no bulk cracked-hash export exists, so the best-effort "found" merge degrades gracefully Version 2.10.5 - Pipal analysis no longer corrupts its input when cracked passwords contain `$HEX[...]` rows. `binascii.unhexlify().decode()` returned the bytes without the trailing newline that normal rows inherit from `password[-1]`, so every HEX-encoded password got concatenated with the next one in the `.passwords` file fed to pipal (e.g. three cracks → two lines, one of them a bogus mashup). Pipal then under-counted entries and reported wrong top base words. The HEX branch now re-appends `\n` so each cracked password lands on its own line diff --git a/hate_crack/main.py b/hate_crack/main.py index 1e4e940..88802d9 100755 --- a/hate_crack/main.py +++ b/hate_crack/main.py @@ -3710,6 +3710,7 @@ def hashview_api(): elif option_key == "download_hashes": # Download left hashes try: + cancel_download = False while True: # First, list customers to help user select customers_result = api_harness.list_customers() @@ -3758,17 +3759,34 @@ def hashview_api(): continue # List hashfiles for the customer. Hashview has no - # list-all route, so we enumerate by the session hash - # type (the type of the hashes loaded in this session). + # list-all route, so we must enumerate by hash type. + # Prefer the session hash type (set when a hashfile is + # loaded); otherwise prompt, since None can't be queried. + download_hash_type = hcatHashType + if not download_hash_type: + ht_input = input( + "\nNo hash type loaded this session. Enter the hashcat " + "hash-type to list (e.g. 1000 for NTLM), or Q to cancel: " + ).strip() + if ht_input.lower() == "q": + cancel_download = True + break + if not ht_input.isdigit(): + print( + "\n✗ Error: Hash type must be a numeric hashcat mode." + ) + continue + download_hash_type = ht_input + try: customer_hashfiles = api_harness.get_customer_hashfiles( - customer_id, hash_type=hcatHashType + customer_id, hash_type=download_hash_type ) if not customer_hashfiles: print( - f"\nNo hashfiles of type {hcatHashType} found for " - f"customer ID {customer_id}" + f"\nNo hashfiles of type {download_hash_type} found " + f"for customer ID {customer_id}" ) continue @@ -3820,6 +3838,10 @@ def hashview_api(): break break + # User cancelled at the hash-type prompt: back to the menu. + if cancel_download: + continue + # Set output filename automatically output_file = f"left_{customer_id}_{hashfile_id}.txt"