mirror of
https://github.com/trustedsec/hate_crack.git
synced 2026-07-28 14:47:22 -07:00
+68
-9
@@ -1,3 +1,11 @@
|
||||
# Methodology provided by Martin Bos (pure_hate) - https://www.trustedsec.com/team/martin-bos/
|
||||
# Original script created by Larry Spohn (spoonman) - https://www.trustedsec.com/team/larry-spohn/
|
||||
# Python refactoring and general fixing, Justin Bollinger (bandrel) - https://www.trustedsec.com/team/justin-bollinger/
|
||||
# Hashview integration by Justin Bollinger (bandrel) and Claude Sonnet 4.5
|
||||
# special thanks to hans for all his hard work on hashview and creating APIs for us to use
|
||||
|
||||
# Load config before anything that needs hashview_url/hashview_api_key
|
||||
|
||||
import sys
|
||||
import os
|
||||
import json
|
||||
@@ -25,13 +33,7 @@ if os.path.isdir(_pkg_dir):
|
||||
if "__spec__" in globals() and __spec__ is not None:
|
||||
__spec__.submodule_search_locations = __path__
|
||||
|
||||
# Methodology provided by Martin Bos (pure_hate) - https://www.trustedsec.com/team/martin-bos/
|
||||
# Original script created by Larry Spohn (spoonman) - https://www.trustedsec.com/team/larry-spohn/
|
||||
# Python refactoring and general fixing, Justin Bollinger (bandrel) - https://www.trustedsec.com/team/justin-bollinger/
|
||||
# Hashview integration by Justin Bollinger (bandrel) and Claude Sonnet 4.5
|
||||
# special thanks to hans for all his hard work on hashview and creating APIs for us to use
|
||||
|
||||
# Load config before anything that needs hashview_url/hashview_api_key
|
||||
hate_path = os.path.dirname(os.path.realpath(__file__))
|
||||
if not os.path.isfile(hate_path + '/config.json'):
|
||||
print('Initializing config.json from config.json.example')
|
||||
@@ -103,6 +105,7 @@ from hate_crack.api import (
|
||||
download_hashes_from_hashview,
|
||||
download_hashmob_wordlists,
|
||||
download_weakpass_torrent,
|
||||
extract_with_7z,
|
||||
)
|
||||
from hate_crack.cli import (
|
||||
add_common_args,
|
||||
@@ -216,6 +219,38 @@ def verify_wordlist_dir(directory, wordlist):
|
||||
print('Exiting. Please check configuration and try again.')
|
||||
return None
|
||||
|
||||
def cleanup_wordlist_artifacts():
|
||||
wordlists_dir = hcatWordlists or os.path.join(hate_path, "wordlists")
|
||||
if not os.path.isabs(wordlists_dir):
|
||||
wordlists_dir = os.path.join(hate_path, wordlists_dir)
|
||||
targets = [hate_path, os.getcwd()]
|
||||
if wordlists_dir not in targets:
|
||||
targets.append(wordlists_dir)
|
||||
|
||||
for base in targets:
|
||||
if not os.path.isdir(base):
|
||||
continue
|
||||
for name in os.listdir(base):
|
||||
path = os.path.join(base, name)
|
||||
if name.endswith(".out"):
|
||||
try:
|
||||
os.remove(path)
|
||||
except Exception as e:
|
||||
print(f"[!] Failed to remove output file {path}: {e}")
|
||||
if base == wordlists_dir and name.endswith(".torrent"):
|
||||
try:
|
||||
os.remove(path)
|
||||
except Exception as e:
|
||||
print(f"[!] Failed to remove torrent file {path}: {e}")
|
||||
if base == wordlists_dir and name.endswith(".7z"):
|
||||
ok = extract_with_7z(path)
|
||||
if not ok:
|
||||
try:
|
||||
os.remove(path)
|
||||
print(f"[!] Removed failed archive: {path}")
|
||||
except Exception as e:
|
||||
print(f"[!] Failed to remove archive {path}: {e}")
|
||||
|
||||
if not SKIP_INIT:
|
||||
# hashcat biniary checks for systems that install hashcat binary in different location than the rest of the hashcat files
|
||||
if hcatPath:
|
||||
@@ -1169,15 +1204,28 @@ def check_potfile():
|
||||
def combine_ntlm_output():
|
||||
hashes = {}
|
||||
check_potfile()
|
||||
if not os.path.isfile(hcatHashFile + ".out"):
|
||||
print("No hashes found in POT file.")
|
||||
return
|
||||
with open(hcatHashFile + ".out", "r") as hcatCrackedFile:
|
||||
for crackedLine in hcatCrackedFile:
|
||||
hash, password = crackedLine.split(':')
|
||||
parts = crackedLine.split(':', 1)
|
||||
if len(parts) != 2:
|
||||
continue
|
||||
hash, password = parts
|
||||
hashes[hash] = password.rstrip()
|
||||
if not hashes:
|
||||
print("No hashes found in POT file.")
|
||||
return
|
||||
with open(hcatHashFileOrig + ".out", "w+") as hcatCombinedHashes:
|
||||
with open(hcatHashFileOrig, "r") as hcatOrigFile:
|
||||
for origLine in hcatOrigFile:
|
||||
if origLine.split(':')[3] in hashes:
|
||||
password = hashes[origLine.split(':')[3]]
|
||||
orig_parts = origLine.split(':')
|
||||
if len(orig_parts) < 4:
|
||||
continue
|
||||
ntlm_hash = orig_parts[3]
|
||||
if ntlm_hash in hashes:
|
||||
password = hashes[ntlm_hash]
|
||||
hcatCombinedHashes.write(origLine.strip()+password+'\n')
|
||||
|
||||
# Cleanup Temp Files
|
||||
@@ -1650,6 +1698,12 @@ def pipal():
|
||||
print('Killing PID {0}...'.format(str(pipalProcess.pid)))
|
||||
pipalProcess.kill()
|
||||
print("Pipal file is at " + hcatHashFilePipal + ".pipal\n")
|
||||
view_choice = input("Would you like to view (cat) the pipal output? (Y/n): ").strip().lower()
|
||||
if view_choice in ('', 'y', 'yes'):
|
||||
print("\n--- Pipal Output Start ---\n")
|
||||
with open(hcatHashFilePipal + ".pipal") as pipalfile:
|
||||
print(pipalfile.read())
|
||||
print("\n--- Pipal Output End ---\n")
|
||||
with open(hcatHashFilePipal + ".pipal") as pipalfile:
|
||||
pipal_content = pipalfile.readlines()
|
||||
raw_pipal = '\n'.join(pipal_content)
|
||||
@@ -1764,6 +1818,7 @@ def main():
|
||||
parser.add_argument('--weakpass', action='store_true', help='Download wordlists from Weakpass')
|
||||
parser.add_argument('--rank', type=int, default=-1, help='Only show wordlists with this rank (use 0 to show all, default: >4)')
|
||||
parser.add_argument('--hashmob', action='store_true', help='Download wordlists from Hashmob.net')
|
||||
parser.add_argument('--cleanup', action='store_true', help='Cleanup .out files, torrents, and extract or remove .7z archives')
|
||||
parser.add_argument('--debug', action='store_true', help='Enable debug mode')
|
||||
# Removed add_common_args(parser) since config items are now only set via config file
|
||||
args = parser.parse_args()
|
||||
@@ -1805,6 +1860,10 @@ def main():
|
||||
)
|
||||
sys.exit(0)
|
||||
|
||||
if args.cleanup:
|
||||
cleanup_wordlist_artifacts()
|
||||
sys.exit(0)
|
||||
|
||||
if args.download_all_torrents:
|
||||
try:
|
||||
download_all_weakpass_torrents(
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import json
|
||||
import os
|
||||
import threading
|
||||
import time
|
||||
from queue import Queue
|
||||
import shutil
|
||||
from typing import Callable, Tuple
|
||||
@@ -80,6 +81,14 @@ def register_torrent_cleanup():
|
||||
_TORRENT_CLEANUP_REGISTERED = True
|
||||
|
||||
def fetch_all_weakpass_wordlists_multithreaded(total_pages=67, threads=10, output_file="weakpass_wordlists.json"):
|
||||
if os.path.isfile(output_file):
|
||||
try:
|
||||
mtime = os.path.getmtime(output_file)
|
||||
if (time.time() - mtime) < 24 * 60 * 60:
|
||||
print(f"[i] Using cached wordlist file: {output_file}")
|
||||
return
|
||||
except Exception:
|
||||
pass
|
||||
wordlists = []
|
||||
lock = threading.Lock()
|
||||
q = Queue()
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
import os
|
||||
|
||||
|
||||
def _write_executable(path, content):
|
||||
path.write_text(content)
|
||||
os.chmod(path, 0o755)
|
||||
|
||||
|
||||
def test_pipal_runs_and_parses_basewords(hc_module, tmp_path, capsys):
|
||||
hc = hc_module
|
||||
hc.hcatHashType = "0"
|
||||
hc.pipal_count = 3
|
||||
hc.hcatHashFile = str(tmp_path / "hashes")
|
||||
|
||||
out_path = tmp_path / "hashes.out"
|
||||
out_path.write_text(
|
||||
"hash1:password123\n"
|
||||
"hash2:$HEX[70617373313233]\n"
|
||||
)
|
||||
|
||||
pipal_stub = tmp_path / "pipal_stub.py"
|
||||
_write_executable(
|
||||
pipal_stub,
|
||||
"#!/usr/bin/env python3\n"
|
||||
"import sys\n"
|
||||
"out = None\n"
|
||||
"if '--output' in sys.argv:\n"
|
||||
" out = sys.argv[sys.argv.index('--output') + 1]\n"
|
||||
"if not out:\n"
|
||||
" out = sys.argv[-1]\n"
|
||||
"with open(out, 'w') as f:\n"
|
||||
" f.write('Top 3 base words\\n')\n"
|
||||
" f.write('pass123 10\\n')\n"
|
||||
" f.write('letmein 5\\n')\n"
|
||||
" f.write('welcome 3\\n')\n"
|
||||
)
|
||||
hc.pipalPath = str(pipal_stub)
|
||||
|
||||
result = hc.pipal()
|
||||
captured = capsys.readouterr()
|
||||
|
||||
assert result == ["pass123", "letmein", "welcome"]
|
||||
assert "Pipal file is at" in captured.out
|
||||
|
||||
passwords_file = tmp_path / "hashes.passwords"
|
||||
assert passwords_file.exists()
|
||||
content = passwords_file.read_text()
|
||||
assert "password123" in content
|
||||
assert "pass123" in content
|
||||
|
||||
|
||||
def test_pipal_missing_out_returns_empty(hc_module, tmp_path, capsys):
|
||||
hc = hc_module
|
||||
hc.hcatHashType = "0"
|
||||
hc.pipal_count = 3
|
||||
hc.hcatHashFile = str(tmp_path / "hashes")
|
||||
|
||||
pipal_stub = tmp_path / "pipal_stub.py"
|
||||
_write_executable(
|
||||
pipal_stub,
|
||||
"#!/usr/bin/env python3\n"
|
||||
"import sys\n"
|
||||
"out = None\n"
|
||||
"if '--output' in sys.argv:\n"
|
||||
" out = sys.argv[sys.argv.index('--output') + 1]\n"
|
||||
"if not out:\n"
|
||||
" out = sys.argv[-1]\n"
|
||||
"with open(out, 'w') as f:\n"
|
||||
" f.write('Top 3 base words\\n')\n"
|
||||
" f.write('pass123 10\\n')\n"
|
||||
" f.write('letmein 5\\n')\n"
|
||||
" f.write('welcome 3\\n')\n"
|
||||
)
|
||||
hc.pipalPath = str(pipal_stub)
|
||||
|
||||
result = hc.pipal()
|
||||
captured = capsys.readouterr()
|
||||
|
||||
assert result == []
|
||||
assert "No hashes were cracked" in captured.out
|
||||
@@ -0,0 +1,43 @@
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
|
||||
import json
|
||||
|
||||
def test_pipal_executable_and_runs(tmp_path):
|
||||
# Read pipalPath from config.json
|
||||
config_path = os.path.join(os.path.dirname(__file__), '..', 'config.json')
|
||||
with open(config_path, 'r') as f:
|
||||
config = json.load(f)
|
||||
pipal_path = config.get('pipalPath')
|
||||
if not pipal_path or not os.path.isfile(pipal_path):
|
||||
import pytest
|
||||
pytest.skip("pipalPath not configured or file missing")
|
||||
|
||||
if not os.access(pipal_path, os.X_OK):
|
||||
raise AssertionError(
|
||||
f"pipalPath exists but is not executable: {pipal_path}. "
|
||||
"Ensure pipal is installed/compiled and has execute permissions."
|
||||
)
|
||||
|
||||
input_file = tmp_path / "pipal_input.txt"
|
||||
input_file.write_text("password\npassword123\nletmein\n")
|
||||
output_file = tmp_path / "pipal_output.txt"
|
||||
|
||||
result = subprocess.run(
|
||||
[pipal_path, str(input_file), "-t", "3", "--output", str(output_file)],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
|
||||
if result.returncode != 0:
|
||||
raise AssertionError(
|
||||
"pipal did not run successfully. "
|
||||
f"returncode={result.returncode}, stdout={result.stdout}, stderr={result.stderr}"
|
||||
)
|
||||
|
||||
if not output_file.exists():
|
||||
raise AssertionError("pipal did not produce an output file; it may need to be compiled.")
|
||||
|
||||
content = output_file.read_text()
|
||||
assert "Top 3 base words" in content
|
||||
Reference in New Issue
Block a user