updated output path

This commit is contained in:
Justin Bollinger
2026-02-03 21:50:40 -05:00
parent bfa2eb5720
commit c46504a979
2 changed files with 91 additions and 68 deletions
+28 -2
View File
@@ -688,10 +688,25 @@ class HashviewAPI:
"name": name,
"hashfile_id": hashfile_id,
"customer_id": customer_id,
"limit_recovered": bool(limit_recovered),
}
if notify_email is not None:
data["notify_email"] = bool(notify_email)
resp = self.session.post(url, json=data, headers=headers)
resp.raise_for_status()
return resp.json()
try:
payload = resp.json()
except Exception:
return resp.json()
msg = str(payload.get("msg", ""))
if "invalid keyword argument for JobNotifications" in msg:
# Retry without notify_email for older Hashview servers.
data.pop("notify_email", None)
resp = self.session.post(url, json=data, headers=headers)
resp.raise_for_status()
return resp.json()
return payload
def download_left_hashes(self, customer_id, hashfile_id, output_file=None):
import sys
@@ -810,7 +825,18 @@ class HashviewAPI:
data = {"name": name}
resp = self.session.post(url, json=data, headers=headers)
resp.raise_for_status()
return resp.json()
try:
payload = resp.json()
except Exception:
return resp.json()
msg = str(payload.get("msg", ""))
if "invalid keyword argument for Customers" in msg:
# Fallback for older Hashview servers that choke on JSON body parsing.
resp = self.session.post(url, data={"name": name})
resp.raise_for_status()
return resp.json()
return payload
def get_hashfile_hash_type(self, hashtype_id):
"""
+63 -66
View File
@@ -135,6 +135,33 @@ def _resolve_hate_path(package_path, config_dict=None):
return package_path
def _ensure_hashfile_in_cwd(hashfile_path):
"""Ensure hashfile path points to cwd to keep output files in execution dir."""
if not hashfile_path:
return hashfile_path
try:
cwd = os.getcwd()
except Exception:
return hashfile_path
if not os.path.isabs(hashfile_path):
return hashfile_path
if os.path.dirname(hashfile_path) == cwd:
return hashfile_path
basename = os.path.basename(hashfile_path)
local_path = os.path.join(cwd, basename)
if os.path.exists(local_path):
return local_path
try:
os.symlink(hashfile_path, local_path)
return local_path
except Exception:
try:
shutil.copy2(hashfile_path, local_path)
return local_path
except Exception:
return hashfile_path
# First get a temporary path to load config
_initial_package_path = os.path.dirname(os.path.realpath(__file__))
_config_path = _resolve_config_path()
@@ -1884,29 +1911,40 @@ def hashview_api():
except Exception as e:
print(f"\n✗ Error fetching customers: {str(e)}")
# Offer to create a customer before proceeding
create_customer = input("\nCreate a new customer? (y/N): ").strip().lower()
if create_customer == 'y':
# 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')}")
if 'customer_id' in result:
print(f" Customer ID: {result['customer_id']}")
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
hashfile_path = select_file_with_autocomplete(
"Enter path to hashfile (TAB to autocomplete)"
)
hashfile_path = hcatHashFile
if not hashfile_path or not os.path.exists(hashfile_path):
hashfile_path = select_file_with_autocomplete(
"Enter path to hashfile (TAB to autocomplete)"
)
if not hashfile_path or not os.path.exists(hashfile_path):
print(f"Error: File not found: {hashfile_path}")
continue
customer_id = int(input("Enter customer ID: "))
hash_type = int(input(f"Enter hash type (default: {hcatHashType}): ") or hcatHashType)
print("\nFile formats:")
@@ -1914,7 +1952,18 @@ def hashview_api():
print(" 3 = shadow, 4 = user:hash, 5 = hash_only")
file_format = int(input("Enter file format (default: 5): ") or 5)
hashfile_name = input(f"Enter hashfile name (default: {os.path.basename(hashfile_path)}): ") or None
hashfile_name = select_file_with_autocomplete(
f"Enter hashfile name (default: {hashfile_path}) (TAB to autocomplete)"
)
if isinstance(hashfile_name, list):
hashfile_name = hashfile_name[0] if hashfile_name else None
if isinstance(hashfile_name, str):
hashfile_name = hashfile_name.strip()
if isinstance(hashfile_name, str) and hashfile_name:
# Hashview expects a name, not a path.
hashfile_name = os.path.basename(hashfile_name)
if not hashfile_name:
hashfile_name = None
try:
result = api_harness.upload_hashfile(
@@ -1933,49 +1982,10 @@ def hashview_api():
create_job = input("\nWould you like to create a job for this hashfile? (Y/n): ") or "Y"
if create_job.upper() == 'Y':
job_name = input("Enter job name: ")
limit_recovered = input("Limit to recovered hashes only? (y/N): ").upper() == 'Y'
notify_email = input("Send email notifications? (Y/n): ").upper() != 'N'
# Ask if user wants to upload a custom wordlist for this job
upload_wordlist = input("\nUpload a custom wordlist to Hashview? (y/N): ").upper() == 'Y'
limit_recovered = False
notify_email = True
uploaded_wordlist_id = None
if upload_wordlist:
print("\n" + "="*60)
print("WORDLIST UPLOAD")
print("="*60)
print("Select a wordlist file to upload to Hashview.")
print("After upload, you'll need to:")
print(" 1. Create a task in Hashview using this wordlist")
print(" 2. Manually add the task to this job via web interface")
print("\nPress TAB to autocomplete file paths.")
print("="*60)
wordlist_path = select_file_with_autocomplete(
"Enter path to wordlist file",
base_dir=hcatWordlists,
)
if wordlist_path and os.path.isfile(wordlist_path):
# Ask for wordlist name
default_name = os.path.basename(wordlist_path)
wordlist_name = input(f"\nEnter wordlist name (default: {default_name}): ").strip() or default_name
try:
# Upload the wordlist
upload_result = api_harness.upload_wordlist(wordlist_path, wordlist_name)
print(f"\n✓ Success: {upload_result.get('msg', 'Wordlist uploaded')}")
if 'wordlist_id' in upload_result:
uploaded_wordlist_id = upload_result['wordlist_id']
print(f" Wordlist ID: {uploaded_wordlist_id}")
print(f" Wordlist Name: {wordlist_name}")
except Exception as e:
print(f"\n✗ Error uploading wordlist: {str(e)}")
print("Continuing with job creation...")
else:
print("\n✗ No valid wordlist file selected.")
print("Continuing with job creation...")
try:
job_result = api_harness.create_job(
job_name, result['hashfile_id'], customer_id,
@@ -1987,20 +1997,6 @@ def hashview_api():
print(f"\nNote: Job created with automatically assigned tasks based on")
print(f" historical effectiveness for hash type {hash_type}.")
if uploaded_wordlist_id:
print(f"\n{'='*60}")
print("NEXT STEPS - Configure Task in Hashview Web Interface:")
print(f"{'='*60}")
print(f"1. Go to: {hashview_url}")
print(f"2. Navigate to Tasks → Create New Task")
print(f"3. Configure task with:")
print(f" - Wordlist ID: {uploaded_wordlist_id} ({wordlist_name})")
print(f" - Rule: (select appropriate rule)")
print(f" - Attack mode: 0 (dictionary)")
print(f"4. Go to Jobs → Job ID {job_result['job_id']}")
print(f"5. Add the new task to this job")
print(f"{'='*60}")
# Offer to start the job
start_now = input("\nStart the job now? (Y/n): ") or "Y"
if start_now.upper() == 'Y':
@@ -2608,6 +2604,7 @@ def main():
if args.hashfile and args.hashtype:
hcatHashFile = resolve_path(args.hashfile)
hcatHashFile = _ensure_hashfile_in_cwd(hcatHashFile)
hcatHashType = args.hashtype
if not hcatHashFile or not os.path.isfile(hcatHashFile):
print(f"Error: hashfile not found: {args.hashfile}")