Commit Graph
540 Commits
Author SHA1 Message Date
Justin Bollinger 88064a5a4f fix: use git fetch --tags and uv sync --reinstall to fix version after upgrade
uv's PEP 517 build isolation copies source to a temp dir without .git,
so setuptools-scm can't determine the version and falls back to the
existing _version.py. Forcing --reinstall-package hate_crack makes uv
rebuild from source in the actual repo dir (editable mode) where git
is accessible, so the correct version is generated.
v2.3.4
2026-03-19 18:02:54 -04:00
Justin Bollinger 0f692efe55 fix: drop make clean from upgrade command to fix version detection
make clean deinits submodules and wipes the uv cache, causing
setuptools-scm to compute a wrong version during the subsequent
uv sync. The clean step is unnecessary for an upgrade - just
git pull && make install is sufficient.
v2.3.3
2026-03-19 17:57:09 -04:00
Justin Bollinger 8483a17242 feat: add --update flag to trigger in-place upgrade on demand
Refactors the upgrade logic into _run_upgrade() so it can be called
both from the startup version check prompt and directly via --update,
bypassing the version comparison entirely.
2026-03-19 17:49:23 -04:00
Justin Bollinger 9fba3f7bf6 feat: add upgrade prompt to version update check
When a newer release is found, prompt the user to upgrade in-place.
Resolves the git repo root via `git rev-parse --show-toplevel` before
running `git pull && make clean && make && make install`, so the upgrade
works correctly whether hate_crack is run from source or installed into
site-packages. Ctrl-C and EOF at the prompt continue normally.
v2.3.2
2026-03-19 17:45:36 -04:00
Justin BollingerandGitHub 7b3293aee5 Merge pull request #102 from trustedsec/bug/analyze-rules-tab-complete
fix: use tab-completion file selector for analyze hashcat rules
v2.3.1
2026-03-19 16:23:21 -04:00
Justin Bollinger 7cd98ea714 Merge remote-tracking branch 'origin/main' into bug/analyze-rules-tab-complete 2026-03-19 16:22:23 -04:00
Justin BollingerandGitHub 917a1b5977 Merge pull request #95 from trustedsec/feat/rule-tools
feat: add rule file management submenu (#93)
v2.3.0
2026-03-19 16:04:12 -04:00
Justin Bollinger 95d0eb229a feat: add rule file tools submenu (key 81) with cleanup and optimize
Resolves merge conflict with origin/main (keys 19-22 and 80 from main
kept, rule tools submenu added at key 81).
2026-03-19 16:03:40 -04:00
Justin BollingerandGitHub 961da2d350 Merge pull request #99 from trustedsec/feat/wordlist-tools
feat: add wordlist tools submenu (len, req-include, req-exclude, cutb, rli, rli2, splitlen, gate)
2026-03-19 16:00:40 -04:00
Justin Bollinger 4b5091ef89 feat: add wordlist tools submenu (key 80) with 7 hashcat-utils filters
Resolves merge conflict with origin/main (keys 19-22 from main kept,
wordlist tools submenu added at key 80).

Fixes test isolation bug in test_random_rules_attack.py where
load_cli_module() nuked hate_crack.attacks from sys.modules, breaking
__globals__ references in wordlist_tools_submenu for downstream tests.
Also corrects wrong menu key assertion (21 not 20) for generate_rules_crack.
2026-03-19 16:00:09 -04:00
Justin BollingerandGitHub de65388d62 Merge pull request #101 from trustedsec/feat/combipow-attack
feat: Passphrase combination attack (issue #88)
2026-03-19 15:50:58 -04:00
Justin Bollinger a31497649f feat: add combipow passphrase attack at menu key 22
Resolves merge conflict with origin/main (keys 19-21 used by ngram,
permute, random-rules). Combipow takes key 22.

- gzip support: decompress .gz wordlists to a temp file before passing
  path to combipow.bin (which requires a filename argument)
- UI line-counting uses gzip.open for .gz files
- Update tests to reference key 22 instead of 21
2026-03-19 15:50:25 -04:00
Justin BollingerandGitHub a59309c29d Merge pull request #96 from trustedsec/feat/random-rules-attack
feat: add random rules attack (#87)
2026-03-19 15:42:47 -04:00
Justin Bollinger 6032787fa0 chore: ignore large local wordlist files 2026-03-19 15:42:18 -04:00
Justin Bollinger 63e7286fcb merge: resolve conflicts with main - random rules moves to key 21 2026-03-19 15:42:11 -04:00
Justin BollingerandGitHub 243013dcdf Merge pull request #100 from trustedsec/feat/permute-attack
feat: add permutation attack (key 19)
2026-03-19 15:34:09 -04:00
Justin Bollinger 92a835c6d8 merge: resolve conflicts with main - permute moves to key 20, ngram stays at 19 2026-03-19 15:33:48 -04:00
Justin Bollinger ffce245f78 fix: remove unused tempfile import 2026-03-19 15:30:27 -04:00
Justin Bollinger 142ee4b245 fix: transparently decompress gzip wordlists for external tool attacks
Add _open_wordlist() helper that returns gzip.open() or open() based on
the .gz extension. Apply it to the three functions that open wordlist
files themselves before piping to an external binary:

- hcatPrince: prince base list fed to pp64.bin
- hcatPermute: wordlist fed to permute.bin
- hcatMarkovTrain: source file fed to hcstat2gen.bin

Attacks that pass wordlist paths directly to hashcat are unaffected -
hashcat already handles .gz natively.
2026-03-19 15:29:59 -04:00
Justin Bollinger 78b51f9b10 revert: update hashcat-utils submodule to reverted ngramX 2026-03-19 15:05:37 -04:00
Justin Bollinger 724e089d63 Revert "fix: ngramX generates n-grams per line, not across entire corpus"
This reverts commit e354f3e57e.
2026-03-19 15:05:29 -04:00
Justin Bollinger e354f3e57e fix: ngramX generates n-grams per line, not across entire corpus
Previously ngramX.bin accumulated all words from the whole file into a
flat array, producing candidates that crossed line boundaries. With a
corpus of "roses are red\nviolets are blue", it generated "red violets"
and "are red violets" - mixing lines that represent separate phrases.

Fixes ngramX.c to process each line independently so n-grams stay
within a single line, matching the expected behavior for song lyrics,
book passages, and similar per-line corpora.
2026-03-19 15:02:42 -04:00
Justin BollingerandGitHub ee1b861f4a Merge pull request #98 from trustedsec/feat/combinator3-combinatorX
feat: add combinator3 and combinatorX attacks (#84, #85)
v2.2.0
2026-03-19 14:48:27 -04:00
Justin Bollinger 5fe6e21405 feat: add ngramX attack and gzip auto-detection for external binaries
- 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)
2026-03-19 14:35:29 -04:00
Justin Bollinger 7a7401266b feat: unify combinator attacks into single 2-8 wordlist handler
- 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
2026-03-19 14:18:25 -04:00
Justin Bollinger a9a533e41e refactor: improve TestHcatCombipow to use patch.object context managers
Replace direct module attribute mutation with patch.object for cleaner
test isolation. Use hate_crack.main.subprocess.Popen for targeted mocking
instead of the global subprocess.Popen.
2026-03-19 12:22:29 -04:00
Justin Bollinger 9345e69bf4 fix: ensure hashcat.induct exists before loopback test
Hashcat renames ~/.hashcat/sessions/hashcat.induct after each session.
When the directory is absent the loopback test fails with "No such file
or directory". Recreate the directory in the test setup so it always
exists when the loopback command runs.
2026-03-19 12:20:37 -04:00
Justin Bollinger 399cd06fdb docs: add Wordlist Tools submenu to README 2026-03-19 12:17:48 -04:00
Justin Bollinger 2d77d8e582 feat: add combipow passphrase attack (#88)
Add menu option 21 (Combipow Passphrase Attack) using combipow.bin from
hashcat-utils. Generates all unique non-empty subset combinations from a
short wordlist and pipes them into hashcat stdin.

- hcatCombipow(hash_type, hash_file, wordlist, use_space_sep) in main.py
- combipow_crack handler in attacks.py validates file, enforces 63-line
  limit, prompts for space separator, then delegates to hcatCombipow
- Wired into get_main_menu_items() and get_main_menu_options() in both
  main.py and hate_crack.py
- 12 tests covering menu presence, handler delegation, line-count
  enforcement, file validation, and subprocess flag construction
2026-03-19 12:17:26 -04:00
Justin Bollinger 69e6fb9cb3 feat: add combinator3 and combinatorX attacks to combinator submenu
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
2026-03-19 12:16:04 -04:00
Justin Bollinger 7664afda55 feat: add wordlist tools submenu (len, req-include, req-exclude, cutb, rli, rli2, splitlen, gate) (#90, #91, #92, #94)
Add Wordlist Tools submenu (key 80) with 7 preprocessing utilities backed by hashcat-utils binaries:

- Filter by length (len.bin) - #90
- Require/exclude character classes (req-include.bin, req-exclude.bin) - #90
- Extract substring (cutb.bin) - #90
- Remove matching lines (rli.bin, rli2.bin) - #91
- Split by length (splitlen.bin) - #92
- Shard wordlist (gate.bin) - #94

Three-layer pattern:
- main.py: low-level wrappers (wordlist_filter_len, wordlist_filter_req_include,
  wordlist_filter_req_exclude, wordlist_cutb, wordlist_splitlen, wordlist_subtract,
  wordlist_subtract_single, wordlist_gate) return bool for testability
- attacks.py: UI handlers with input validation and the wordlist_tools_submenu dispatcher
- hate_crack.py + main.py: menu item "80" wired in both get_main_menu_items and
  get_main_menu_options

Move interactive_menu import to attacks.py module level (was local import in
combinator_submenu) to support patching in tests.
2026-03-19 12:15:43 -04:00
Justin Bollinger e41134eb1a feat: add permutation attack using permute.bin (closes #86)
Adds Permutation Attack (menu option 19) that generates all character
permutations of each word in a targeted wordlist and pipes them to
hashcat via permute.bin from hashcat-utils.

- hcatPermute() in main.py: pipes permute.bin < wordlist | hashcat
- permute_crack() in attacks.py: prompts for single wordlist file with
  factorial-growth warning, tab-autocomplete support
- Menu option 19 wired in both main.py and hate_crack.py
- hcatPermuteCount tracking alongside other count globals
- Tests: test_permute_attack.py (handler behavior) and
  test_permute_wrapper.py (subprocess wiring)
- README: added entry in menu listing and attack descriptions
2026-03-19 12:14:05 -04:00
Justin Bollinger dfe9e28e70 feat: add random rules attack using generate-rules.bin (#87)
- Add hcatGenerateRules() in main.py: runs generate-rules.bin to
  produce N random rules, writes them to a temp file, runs hashcat
  with -r against a chosen wordlist, cleans up on exit
- Add generate_rules_crack() handler in attacks.py with count
  prompt (default 65536), wordlist picker with tab-completion,
  input validation, and abort on invalid input
- Add dispatcher generate_rules_crack() in main.py and key "20"
  in both main.py and hate_crack.py get_main_menu_options()
- Add ("20", "Random Rules Attack") to get_main_menu_items()
- Add tests: test_random_rules_attack.py (menu presence, handler
  wiring), test_random_rules_wrapper.py (subprocess behavior,
  cleanup, count passing, count tracking)
- Add key "20" to MENU_OPTION_TEST_CASES in test_ui_menu_options.py
- Update README: add option 20 to menu listing, add attack
  description, add version history entry
2026-03-19 12:13:38 -04:00
Justin Bollinger 9611565471 feat: add rule file management tools using cleanup-rules.bin and rules_optimize.bin (closes #93)
Adds Rule File Tools submenu (menu option 81) with three operations:
- Clean: removes invalid/duplicate rules via cleanup-rules.bin
- Optimize: consolidates redundant operations via rules_optimize.bin
- Clean and optimize: both in sequence with temp file handling

Wired through the standard three-layer pattern: main.py utility
functions + dispatcher, attacks.py handlers + submenu, root
hate_crack.py menu registration.
2026-03-19 12:12:12 -04:00
Justin Bollinger 53b99d4395 fix: use raw LZMA2 stream instead of XZ container for hcstat2 compression
hashcat's Lzma2Decode() expects a raw LZMA2 stream starting with 0xff chunk
headers, not an XZ container format (0xfd 37 7a...). FORMAT_RAW with
FILTER_LZMA2 produces the correct format.
2026-03-18 20:16:42 -04:00
Justin Bollinger afb4b279fc fix: compress hcstat2 output with LZMA2 format for hashcat compatibility
Hashcat requires hcstat2 files to be LZMA2-compressed. The hcstat2gen.bin utility outputs uncompressed binary data, so we need to compress the output after generation.

Root cause: Hashcat's source code (mpsp.c) uses hc_lzma2_decompress() to read hcstat2 files. The bundled hashcat.hcstat2 file uses LZMA2 compression (XZ format). hcstat2gen.bin outputs plain hcstat format which hashcat cannot decompress, causing "Could not uncompress data" error.

Fix: After hcstat2gen.bin completes, compress the output file using Python's lzma module with XZ format (LZMA2-based).

Also restore proper subprocess invocation: pass output path as argument to hcstat2gen.bin instead of using stdout redirection.

Tests: All 8 markov E2E tests pass, 479 total tests pass
2026-03-18 19:40:18 -04:00
Justin Bollinger c71d2a5321 fix: restore hcstat2gen.bin output path argument - markov training was creating empty file
The previous commit attempted to simplify by passing hcstat2_path as an argument and removing stdout redirection. However, hcstat2gen.bin writes directly to the file specified as its first argument and doesn't use stdout. The code was missing the proper invocation.

Root cause: Removed stdout=PIPE capture but forgot that the binary doesn't write to stdout - it writes directly to the file argument. This left the .hcstat2 file empty/uncreated.

Fix: Restore proper subprocess invocation - pass output path as argument, stdin from wordlist, stderr for error reporting. Remove stdout=PIPE since the binary doesn't use it.

Tests: All 8 markov E2E tests pass, 479 total tests pass
2026-03-18 19:30:23 -04:00
Justin Bollinger 374ac25233 fix: simplify markov training - hcstat2gen.bin supports gzipped input
- 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
2026-03-18 19:24:19 -04:00
Justin Bollinger 07f7cad16f fix: pass hcstat2 output path as argument to hcstat2gen.bin
- hcstat2gen.bin expects: hcstat2gen.bin <outfile> < dictionary
- Previously tried to write to stdout which caused 'usage' error
- Now correctly passes output path as command argument
- Markov training now works for both plain text and gzipped wordlists
- Fixes 'Markov table generation failed' error
2026-03-18 19:19:31 -04:00
Justin Bollinger a13cec1792 fix: add detailed error reporting to markov training
- Check hcstat2gen.bin exists before running
- Verify source file is readable
- Capture stdout and stderr from hcstat2gen.bin
- Report hcstat2gen.bin errors with stderr output
- Verify output file created and non-empty
- Add 300 second timeout to prevent hanging
- Write proper output to .hcstat2 file from stdout
- Helps debug training failures with specific error messages
2026-03-18 19:17:54 -04:00
Justin Bollinger 120c251e92 fix: properly decompress gzipped files before markov training
- Add tempfile import for secure temporary file handling
- Detect gzipped files by magic bytes (0x1f 0x8b)
- Decompress gzipped files to temporary file first
- Pass uncompressed data directly to hcstat2gen.bin
- Properly clean up temporary files on success or error
- Fixes 'hcstat2: Could not uncompress data' error
2026-03-18 19:13:10 -04:00
Justin Bollinger 4ff0cff656 fix: handle gzipped training files in markov attack
- Add gzip import to main.py
- Detect gzipped files by magic bytes (0x1f 0x8b)
- Use gzip.open() for compressed files, regular open() for plain text
- Properly close file handles in all cases
- Allows markov training from both .txt and .txt.gz wordlists
2026-03-18 19:09:49 -04:00
Justin Bollinger 02ce2092ec docs: add ad-hoc mask and markov brute force attack documentation
- Update menu listing to show combinator submenu and new attacks (17, 18)
- Remove keys 10/11/12 from main menu (consolidated into submenu)
- Add detailed descriptions for Combinator Attacks submenu, Ad-hoc Mask Attack, and Markov Brute Force Attack
- Document Markov training source selection and table persistence
- Update version history with new feature details
2026-03-18 19:02:35 -04:00
Justin Bollinger 0d83bd03e2 test: fix menu option tests for new attack structure
- 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
2026-03-18 19:01:51 -04:00
Justin Bollinger 9a432f8b07 feat: add ad-hoc mask attack, markov brute force, and combinator sub-menu
- 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
2026-03-18 19:00:40 -04:00
Justin BollingerandGitHub e735c7887e Merge pull request #83 from trustedsec/feat/omen-rules-and-cwd-fix
feat: add rule support to OMEN attack and fix relative path resolution
2026-03-18 18:18:23 -04:00
Justin Bollinger 3bc85d9b8a feat: add rule support to OMEN attack and fix relative path resolution
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.
2026-03-18 18:17:41 -04:00
Justin Bollinger 9423b5a64d feat: restore wordlist_optimizer.py with modernizations
- Resolve binary paths relative to script location with platform-specific extensions
- Replace shell=True with proper subprocess.run argument lists (security fix)
- Convert %-formatting to f-strings throughout
- Replace lineCount() utility with os.path.getsize()
- Add fail-fast error handling with actionable messages
- Verify binaries exist before running
2026-03-17 19:15:20 -04:00
Justin Bollinger fda38d3498 docs: update README with OMEN fix and issues #80, #81, #82 2026-03-17 15:07:02 -04:00
Justin Bollinger 67c462cd41 fix: OMEN attack silent failure - validate model files, capture enumNG errors, add wordlist picker
- 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
2026-03-17 15:05:36 -04:00