- Add _is_gzipped() magic-byte detector and _wordlist_path() context
manager that transparently decompresses gzip files to a temp path
- Apply gzip handling to hcatCombinator3 and hcatCombinatorX via
contextlib.ExitStack so compressed wordlists work without manual prep
- Add hcatNgramX() wrapper using ngramX.bin <corpus> <group_size> piped
to hashcat, with gzip auto-detection on the corpus file
- Add ngram_attack() handler in attacks.py with tab-autocomplete corpus
selection and configurable group size (default 3)
- Register attack as menu option 19 in both main.py and hate_crack.py
- Fix wordlist_optimizer.py: .app extension on macOS was wrong, use .bin
- Add tests/test_ngram_gzip.py covering ngram_attack handler, _is_gzipped,
and _wordlist_path context manager (temp file cleanup, plain passthrough)
- Merge combinator, combinator3, and combinatorX into one unified
combinator_crack function that routes by wordlist count:
2 (no sep) -> hcatCombination, 3 (no sep) -> hcatCombinator3,
4+ or any separator -> hcatCombinatorX
- Replace comma-separated wordlist input with one-at-a-time
tab-autocomplete prompts (blank line to finish)
- Add _prompt_wordlist_paths helper using existing readline infrastructure
- Add hcatCombinator3Wordlist and hcatCombinatorXWordlist config vars
with rockyou.txt defaults
- Print full hashcat command to stdout in --debug mode by calling
_debug_cmd at the end of _append_potfile_arg (covers all 27 invocations)
- Collapse combinator submenu from 6 options to 4; keep combinator3_crack,
combinatorX_crack, and combinator_3plus_crack as delegation shims
- Update tests to cover unified routing and new prompt interface
Extends the combinator submenu (option 6) with two new attacks using
hashcat-utils binaries that were already compiled but unused.
- hcatCombinator3: 3-way wordlist combination via combinator3.bin piped
to hashcat stdin
- hcatCombinatorX: 2-8 wordlist combination via combinatorX.bin with
optional --sepFill separator, piped to hashcat stdin
- combinator3_crack handler: prompts for 3 comma-separated wordlist paths
- combinatorX_crack handler: prompts for 2-8 paths plus optional separator
- combinator_submenu updated with options 5 and 6
Closes#84, closes#85
- Remove unnecessary gzip decompression logic
- hcstat2gen.bin can read both plain text and gzipped files directly
- Simplified code: just pass source file and output path as arguments
- Removed gzip and tempfile imports (no longer needed)
- Add comprehensive end-to-end tests covering all markov attack flows
- Tests verify training with plain text and gzipped wordlists
- Tests verify all handler menu paths (use existing, generate new, cancel, training failure)
- Fixes 'cannot uncompress data' error by not trying to decompress
- Update test cases to reflect combinator_submenu for key 6
- Remove test cases for keys 10/11/12 (moved to sub-menu)
- Add test cases for new keys 17 and 18
- Simplify adhoc_mask tests to avoid global state issues
- Add three hashcat wrapper functions: hcatAdHocMask, hcatMarkovTrain, hcatMarkovBruteForce
- Add corresponding attack handlers in attacks.py with OMEN-style training flow
- Consolidate 4 combinator attacks (keys 10/11/12) into interactive sub-menu (key 6)
- Add key 17 for ad-hoc mask attack and key 18 for markov brute force
- Update both main.py and hate_crack.py menu systems
- Add comprehensive test coverage for new handlers and wrappers
- Training source picker supports cracked passwords or any wordlist
Extract _select_rules() helper from quick_crack/loopback_attack and wire
it into omen_attack so OMEN can run with rule chains. Extend hcatOmen()
to accept and apply an hcatChains argument including debug mode injection.
Fix resolve_path() to honour HATE_CRACK_ORIG_CWD (set by the install shim)
so relative hash/wordlist paths resolve against the caller's working directory
instead of the repo root. Increase default omenMaxCandidates to 50M.
- Add _omen_model_is_valid() checking all 5 required model files
- Add _omen_model_info() reading model_info.json metadata
- hcatOmenTrain returns bool and writes training metadata
- Capture enumNG stderr and report errors instead of silent failure
- Rewrite omen_attack with train/use/cancel menu and wordlist picker
- Rewrite list_and_download_hashmob_rules with ThreadPoolExecutor
- Update quick_crack to use list_wordlist_files
- Add parallel download tests with stdin TTY mock
Config and assets were not found when running hate_crack from outside the
repo directory. The shim uses `uv run --directory` to always execute from
the repo root, and _candidate_roots() now includes _repo_root and
_package_path as fallback search locations.
A blank first line caused the format detection regex chain to fall
through to the error exit. Read lines in a loop and skip empty ones
before matching. Add tests for blank lines with LF, CRLF, whitespace,
and BOM variations.
Use utf-8-sig encoding to natively strip BOM and remove null bytes from
UTF-16 artifacts so the bare hash regex matches correctly. Replace the
unhelpful "unknown format" error with a diagnostic message showing the
actual first-line content and expected formats.
- Remove vendor-assets and clean-vendor Makefile targets
- Lima VM test and Dockerfile.test now use make install (editable)
- Remove hate_crack/hashcat-utils from submodules-pre expander generation
- Update README to reflect no-vendoring install
In dev checkouts where submodules are built in the repo root rather than
vendored into hate_path, OMEN binaries were not found. Introduces _omen_dir
that checks hate_path/omen first and falls back to the repo root omen dir.
Also removes vendor-assets from install/update targets and drops vendored
submodule paths from pyproject.toml package data.
When the Hashview server returns HTTP 200 with an error message and no
job_id (due to its internal notify_email bug), the CLI and interactive
paths now:
- exit 1 (not 0) in the CLI path
- print "✗ Error" instead of "✓ Success"
- print a hint to check the Hashview UI before retrying, preventing
duplicate job creation
Adds test for the error response path in test_cli_flags.py.
- Add rsync to _require_lima() prerequisite check; missing rsync now
skips cleanly instead of failing with an opaque command-not-found
- Add _truncate_output() helper and apply to all assertion messages to
keep failure output readable when make/install emits thousands of lines
- Increase limactl start timeout from 300s to 600s to accommodate slow
Ubuntu image downloads
- Add limactl stop before delete in cleanup for more reliable teardown
- Add flag verification to test_lima_vm_install_and_run: checks 10 CLI
flags in --help output, matching the local install test pattern
- Add 3 unit tests: test_truncate_output_trims_long_text,
test_truncate_output_short_text_unchanged,
test_require_lima_skips_without_rsync
OpenCL/device build errors are environment-specific issues, not code
bugs. Detect clCreateProgramWithBinary and kernel build failures in
stderr and pytest.skip instead of pytest.fail.
Add Ubuntu 24.04 Lima VM test track that runs hate_crack installation
end-to-end in a real VM, giving higher confidence than Docker-based tests.
- Add lima/hate-crack-test.yaml: Ubuntu 24.04 VM config with hashcat and
build deps pre-installed via apt; uv installed via official installer
- Add tests/test_lima_vm_install.py: mirrors Docker E2E test structure;
uses rsync with targeted excludes (wordlists, compiled host binaries)
and builds wheel directly to avoid setuptools-scm sdist file filtering
- Fix Makefile: add princeprocessor build step with aarch64-compatible
CFLAGS (drops -m64); copy binary to submodule root for vendor-assets
- Add Lima tests to prek.toml pre-push hook
- Document Lima VM tests in TESTING.md
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add hashcat as git submodule; compile with make, skip if already in PATH
- Convert princeprocessor from tracked files to git submodule
- Change .DEFAULT_GOAL to install so plain `make` does a full install
- Install uv, Xcode CLT (macOS), build-essential (Debian) if missing
- vendor-assets falls back to system hashcat if submodule not compiled
- Remove hcatOptimizedWordlists; all attacks now use hcatWordlists
- Default hcatWordlists to ./wordlists, rules_directory to ./hashcat/rules
- Default hcatTuning to empty string (no --force --remove)
- Backfill missing config.json keys from config.json.example at startup
- Wrap hcatBin/hcatTuning/hcatWordlists loading in try/except with defaults
- Fall back to vendored hashcat binary at hate_path/hashcat/hashcat
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The PassGPT training device menu now uses _detect_device() to default
to the best available device (CUDA > MPS > CPU) rather than always
defaulting to CUDA, which fails on systems without NVIDIA GPUs.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Show estimated training times for CUDA/MPS/CPU before starting a
training run. Add device selection prompt with cuda as the default.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Training previously loaded entire wordlists into RAM and tokenized all at
once, causing OOM on large files like rockyou.txt. This adds memory
estimation, lazy dataset loading, and training optimizations.
- Add _get_available_memory_mb() for cross-platform RAM detection
- Add _estimate_training_memory_mb() to predict peak usage before loading
- Replace bulk tokenization with LazyPasswordDataset (file offset index + on-the-fly tokenization)
- Add --max-lines flag to limit training to first N lines
- Add --memory-limit flag to auto-tune --max-lines based on available RAM
- Enable gradient checkpointing and gradient accumulation (steps=4)
- Enable fp16 on CUDA devices
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add ability to fine-tune PassGPT models on custom password wordlists.
Models save locally to ~/.hate_crack/passgpt/ with no data uploaded to
HuggingFace (push_to_hub=False, HF_HUB_DISABLE_TELEMETRY=1). The
PassGPT menu now shows available models (default + local fine-tuned)
and a training option. Adds datasets to [ml] deps and passgptTrainingList
config key.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add optional startup version check against GitHub releases (check_for_updates config option)
- Add packaging dependency for version comparison
- Fix PassGPT OOM on MPS by capping batch size to 64 and setting memory watermark limits
- Fix PassGPT output having spaces between every character
- Hide PassGPT menu item (17) unless torch/transformers are installed
- Fix mypy errors in passgpt_generate.py with type: ignore comments
- Update README with version check docs, optional ML deps section, and PassGPT CLI options
- Add test_version_check.py with 8 tests covering update check behavior
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add PassGPT as attack mode 17, using a GPT-2 model trained on leaked
password datasets to generate candidate passwords. The generator pipes
candidates to hashcat via stdin, matching the existing OMEN pipe pattern.
- Add standalone generator module (python -m hate_crack.passgpt_generate)
- Add [ml] optional dependency group (torch, transformers)
- Add config keys: passgptModel, passgptMaxCandidates, passgptBatchSize
- Wire up menu entries in main.py, attacks.py, and hate_crack.py
- Auto-detect GPU (CUDA/MPS) with CPU fallback
- Add unit tests for pipe construction, handler, and ML deps check
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add OMEN (Ordered Markov ENumerator) as a probability-ordered password
candidate generator. Trains n-gram models on leaked passwords via
createNG, then pipes candidates from enumNG into hashcat.
Also fix a pre-existing bug where ensure_binary() used quit(1) instead
of sys.exit(1) - quit() closes stdin before raising SystemExit, which
caused "ValueError: I/O operation on closed file" when any optional
binary check failed and the program continued to use input().
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Reuses existing _count_computer_accounts() and _filter_computer_accounts()
to optionally strip computer accounts before NetNTLM deduplication.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add TestE2EPreprocessingFlow class that simulates the exact main()
preprocessing logic (format detection, filtering, NT/LM extraction)
with realistic secretsdump.py output. Covers: filter accept/decline,
no computers, all computers, LM hash detection, domain\computer$
format.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Catch PermissionError/OSError in file operations (not just FileNotFoundError)
- Refactor _dedup_netntlm_by_username to two-pass streaming (memory safe)
- Handle CRLF line endings in filter and dedup functions
- Add KeyboardInterrupt handling with temp file cleanup during preprocessing
- Track .filtered/.dedup temp files for cleanup on interruption
- Add CRLF line ending tests for both filter and dedup
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add TestWriteFieldSortedUnique (7 tests) and TestPwdumpFilterPipeline
(8 tests) covering the full filter -> extract NT/LM pipeline. Add
edge case tests for unicode, BOM, long lines, permissions, and
delimiter handling.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add type hints to _filter_computer_accounts and _dedup_netntlm_by_username
- Fix unclosed file handle when reading hash file for format detection
- Extract _count_computer_accounts helper to eliminate duplicate file reads
- Stream _filter_computer_accounts output instead of collecting in memory
- Only write .dedup file when duplicates actually exist
- Add tests for _count_computer_accounts, malformed lines, and no-file-on-zero-dupes
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Detect and optionally filter Windows computer accounts (username
ending with $) from pwdump-format NTLM hash files (type 1000)
- Detect and optionally deduplicate NetNTLMv1/v2 hashes (types
5500/5600) by username, keeping first occurrence
- Add 10 tests covering both features
Fixes#27Fixes#28
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
download_wordlist() now resolves relative filenames against
get_hcat_wordlists_dir() instead of saving to cwd. Also ensures
the parent directory exists before writing.
Fixes#70
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The test built its own mock context instead of using the shared
ollama_globals helper, missing the rulesDirectory and hcatPotfilePath
patches. This caused FileNotFoundError on CI where /path/to/hashcat/rules
doesn't exist.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Mock rulesDirectory in ollama test fixture so hcatOllama doesn't
fail with FileNotFoundError on CI where /path/to/hashcat/rules
doesn't exist
- Mock potfile path in hashview auto-merge test so found file cleanup
isn't blocked by missing ~/.hashcat directory
- Update pre-push hook to match CI env vars (HATE_CRACK_SKIP_INIT=1)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove ollamaWordlist config key and all references. Wordlist mode now
requires the cracked hashes .out file to exist and extracts passwords
by splitting on the first colon. Detect Ollama refusal responses and
abort gracefully. Update tests accordingly.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove 500-line wordlist cap and send the entire file to Ollama.
Add ollamaNumCtx config key (default 32768) to control the context
window size. Invert wordlist prompt to default-yes, remove unused
ollamaCandidateCount config.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Rename markov_attack → ollama_attack and hcatMarkov → hcatOllama across
menu, attacks, and tests. Remove candidate count prompts and cracked-output
default wordlist logic. Rename config keys (markov* → ollama*) and drop
ollamaUrl. Fix Dockerfile.test to use granular build steps.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Merge download_left and download_found into single "Download Hashes" menu option
- Append found hash:clear pairs to potfile instead of running broken hashcat re-crack
- Append found hashes to left file so hashcat --show returns full results
- Clean up found_ temp files after merge
- Split found file on first colon (not last) to handle passwords containing colons
- Filter hashcat parse errors from --show stdout in _run_hashcat_show
- Add get_hcat_potfile_path() helper to api.py for potfile resolution
- Remove obsolete download_found_hashes API method and CLI subcommand
- Fix ollama tests to match current 4-arg hcatOllama signature and rule loop
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When the cracked hashes output file (.out) exists, use it as the default
wordlist for the LLM Markov attack instead of the generic markovWordlist
config. This makes the attack learn from already-cracked passwords for
the current engagement, falling back to config when no cracked output
exists.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add a new attack mode that uses a local LLM via Ollama to generate
password candidates, converts them into hashcat .hcstat2 Markov
statistics via hcstat2gen, and runs a Markov-enhanced mask attack.
Two generation sub-modes:
- Wordlist-based: feeds sample from an existing wordlist to the LLM
as pattern context (config-selectable default with Y/N override)
- Target-based: prompts for company name, industry, and location
for contextual password generation
Pipeline: Ollama API -> candidate file -> hcstat2gen -> LZMA compress
-> hashcat -a 3 --markov-hcstat2
Config additions: ollamaUrl, ollamaModel, markovCandidateCount,
markovWordlist. No new pip dependencies (uses stdlib urllib/lzma).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
hcatPath now exclusively points to the hashcat install directory and is
auto-discovered from PATH when not configured. hate_path is resolved
from the package directory (installed) or repo root (development) with
no auto-discovery. Extracted vendor-assets/clean-vendor Makefile targets
to deduplicate the install logic.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>