mirror of
https://github.com/trustedsec/hate_crack.git
synced 2026-07-28 14:47:22 -07:00
fix(hashview): degrade to direct hashfile-ID entry when listing API absent
The hashfile-listing route (/v1/hashfiles/hash_type/<type>) only exists on Hashview builds from 2026-06-08+ (v0.8.3-dev). On `main` and older servers it 404s, which surfaced as "Could not list hashfiles" and a dead end. The download flow is now version-adaptive: - get_all_customer_hashfiles stops sweeping after the first 404 (the listing endpoint is absent) instead of probing every common type. - The menu treats listing as best-effort: when no listing is available it prints a clear note and accepts a hashfile ID typed directly (read from the web UI), with no list-membership check. The chosen file's type is resolved via GET /v1/getHashType/<id> and downloaded via GET /v1/hashfiles/<id> -- both present on all server versions. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
347e3529d5
commit
c07bab1a2b
@@ -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/<type>` endpoint. Since that route lists per hash type, the download flow sweeps common hashcat modes to display all of a customer's uploaded hashfiles (falling back to a manual hash-type prompt only if none are found among the common set), then filters by customer. Additional client-side route fixes: hashfile hash-type lookup now uses `GET /v1/getHashType/<id>`; "left" (uncracked) hash download uses `GET /v1/hashfiles/<id>`; `delete_job` uses `DELETE /v1/jobs/<id>`; `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/<type>` endpoint where available — the download flow sweeps common hashcat modes to display a customer's uploaded hashfiles. That listing route only exists on Hashview builds from 2026-06-08+ (the `v0.8.3-dev` branch); on `main`/older servers there is no hashfile-listing API at all, so the flow now degrades gracefully to entering the hashfile ID directly (looked up in the Hashview web UI) and resolving its type via `GET /v1/getHashType/<id>`. Additional client-side route fixes: hashfile hash-type lookup now uses `GET /v1/getHashType/<id>`; "left" (uncracked) hash download uses `GET /v1/hashfiles/<id>`; `delete_job` uses `DELETE /v1/jobs/<id>`; `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
|
||||
|
||||
@@ -1261,6 +1261,18 @@ class HashviewAPI:
|
||||
try:
|
||||
files = self.get_hashfiles_by_type(ht)
|
||||
except Exception as e:
|
||||
status = getattr(getattr(e, "response", None), "status_code", None)
|
||||
if status == 404:
|
||||
# The /v1/hashfiles/hash_type route doesn't exist on this
|
||||
# server (e.g. Hashview main, or builds before 2026-06-08),
|
||||
# so no per-type sweep is possible. Stop after the first 404
|
||||
# instead of hammering the server with one request per type.
|
||||
if self.debug:
|
||||
print(
|
||||
"[DEBUG] get_all_customer_hashfiles: hash_type listing "
|
||||
"endpoint absent (404); aborting sweep"
|
||||
)
|
||||
break
|
||||
if self.debug:
|
||||
print(f"[DEBUG] get_all_customer_hashfiles: type {ht} failed: {e}")
|
||||
continue
|
||||
|
||||
+32
-45
@@ -3758,55 +3758,31 @@ def hashview_api():
|
||||
)
|
||||
continue
|
||||
|
||||
# List hashfiles for the customer. Hashview has no
|
||||
# list-all route, so we enumerate by sweeping the
|
||||
# per-type endpoint across common hashcat modes and
|
||||
# showing whatever the customer has uploaded.
|
||||
# Try to list the customer's hashfiles for convenience.
|
||||
# This only works on Hashview servers that expose the
|
||||
# /v1/hashfiles/hash_type endpoint (v0.8.3-dev, added
|
||||
# 2026-06-08); on `main` or older builds there is no
|
||||
# hashfile-listing API, so we fall back to entering the
|
||||
# hashfile ID directly (look it up in the web UI).
|
||||
hashfile_map = {}
|
||||
try:
|
||||
print(
|
||||
"\nScanning customer hashfiles across common hash types..."
|
||||
)
|
||||
customer_hashfiles = api_harness.get_all_customer_hashfiles(
|
||||
customer_id
|
||||
customer_hashfiles = (
|
||||
api_harness.get_all_customer_hashfiles(customer_id)
|
||||
)
|
||||
except Exception as e:
|
||||
customer_hashfiles = []
|
||||
if debug_mode:
|
||||
print(f"[DEBUG] hashfile listing unavailable: {e}")
|
||||
|
||||
# Fallback: nothing among common types. The file may
|
||||
# use an uncommon mode, so let the user name it.
|
||||
if not customer_hashfiles:
|
||||
print(
|
||||
f"\nNo hashfiles found for customer ID {customer_id} "
|
||||
"among common hash types."
|
||||
)
|
||||
ht_input = input(
|
||||
"Enter a specific hashcat hash-type to check "
|
||||
"(e.g. 1000), 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
|
||||
customer_hashfiles = (
|
||||
api_harness.get_customer_hashfiles(
|
||||
customer_id, hash_type=ht_input
|
||||
)
|
||||
)
|
||||
|
||||
if not customer_hashfiles:
|
||||
print(
|
||||
f"\nNo hashfiles found for customer ID {customer_id}"
|
||||
)
|
||||
continue
|
||||
|
||||
if customer_hashfiles:
|
||||
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")
|
||||
@@ -3826,22 +3802,33 @@ def hashview_api():
|
||||
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
|
||||
else:
|
||||
print(
|
||||
"\nThis Hashview server has no hashfile-listing API "
|
||||
"(it lacks the /v1/hashfiles/hash_type endpoint), or "
|
||||
f"customer {customer_id} has no hashfiles of a common "
|
||||
"type. Look up the hashfile ID in the Hashview web UI "
|
||||
"and enter it below."
|
||||
)
|
||||
|
||||
while True:
|
||||
hashfile_id_input = input(
|
||||
"\nEnter hashfile ID (or Q to cancel): "
|
||||
).strip()
|
||||
if hashfile_id_input.lower() == "q":
|
||||
cancel_download = True
|
||||
break
|
||||
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:
|
||||
# Only restrict to the listed set when we actually
|
||||
# have a listing; otherwise accept any ID the user
|
||||
# read from the web UI.
|
||||
if hashfile_map and hashfile_id not in hashfile_map:
|
||||
print(
|
||||
"\n✗ Error: Hashfile ID not in the list. Please try again."
|
||||
)
|
||||
|
||||
@@ -118,6 +118,22 @@ class TestHashviewAPI:
|
||||
assert str(by_id[1]["hash_type"]) == "1000" # first type seen wins
|
||||
assert str(by_id[3]["hash_type"]) == "5600"
|
||||
|
||||
def test_get_all_customer_hashfiles_aborts_on_404(self, api):
|
||||
"""A 404 means the listing endpoint is absent (e.g. Hashview main);
|
||||
the sweep stops after the first request instead of probing every type."""
|
||||
import requests
|
||||
|
||||
def _raise_404(ht):
|
||||
resp = Mock()
|
||||
resp.status_code = 404
|
||||
raise requests.exceptions.HTTPError("404 Not Found", response=resp)
|
||||
|
||||
api.get_hashfiles_by_type = Mock(side_effect=_raise_404)
|
||||
result = api.get_all_customer_hashfiles(1, hash_types=[1000, 5600, 3000])
|
||||
assert result == []
|
||||
# Stopped after the first 404, did not sweep all three types.
|
||||
assert api.get_hashfiles_by_type.call_count == 1
|
||||
|
||||
def test_get_all_customer_hashfiles_skips_failing_types(self, api):
|
||||
"""A per-type query that errors is skipped, not fatal."""
|
||||
|
||||
|
||||
Reference in New Issue
Block a user