Simple Bash wrapper around hashcat for running a curated set of cracking workflows from an interactive menu. Background and usage philosophy are covered in this blog post.
Useful wordlist sources:
Potfile-based processors decode valid $HEX[...] records automatically before generating candidates. To inspect these values yourself, see hex-to-readable or use CyberChef.
git clone https://github.com/crypt0rr/hash-cracker
cd hash-cracker
chmod +x hash-cracker.shhashcatmust be installed and executablehash-cracker.confmust exist in the repository root
At startup, hash-cracker checks that:
HASHCATpoints to an executablehashcatbinaryPOTFILEis an existing regular file with read/write access- the required config keys are set in
hash-cracker.conf
Normal runs fail before Hashcat starts if the potfile is missing, a directory, or cannot be read and written. Dry-run mode reports a missing potfile without creating or modifying it.
These are only needed for specific menu options.
python3- Needed for options
9–14,17, and19(plaintext extraction and processing) - Option
12requirespyenchant: python3 -m pip install pyenchant==3.3.0
- Needed for options
cewl- Needed for option
18
- Needed for option
scripts/extensions/common-substr-linux- Needed for options
10and11
- Needed for options
scripts/extensions/mkpass-linux- Needed for option
17(Markov password generation)
- Needed for option
- Bundled native helpers support Apple Silicon (
arm64) only; Intel macOS is not supported for those workflows. python3- Needed for options
9–14,17, and19(plaintext extraction and processing) - Option
12requirespyenchant: python3 -m pip install pyenchant==3.3.0
- Needed for options
cewlcommand in PATH, orscripts/extensions/cewl/cewl.rb- Needed for option
18
- Needed for option
scripts/extensions/common-substr-mac- Needed for options
10and11
- Needed for options
scripts/extensions/mkpass-mac- Needed for option
17(Markov password generation)
- Needed for option
The default smoke suite simulates Darwin on Linux and verifies the portable
shell filesystem paths without invoking native helpers. Before closing a
macOS release validation, run options 10, 11, and 17 on an Apple Silicon
host with the bundled helpers available; Intel macOS is not a supported target.
Notes:
- Option
13uses the bundled PACK scripts and generates masks from the existing potfile.
hash-cracker.conf is required and is loaded automatically on every start.
The configuration is sourced as trusted Bash code. Only use a configuration file you created or reviewed locally; do not point HASH_CRACKER_CONFIG at untrusted input.
The file must live in the repository root and define these settings:
HASHCAT- path to thehashcatbinary, preferably as a quoted Bash array assignment such asHASHCAT=("/path with spaces/hashcat")DEVICE- value passed tohashcat -dHASHTYPE- hash mode, for example1000for NTLMHASHLIST- file containing target hashesPOTFILE- path to an existing readable/writable potfileWORDLIST- primary wordlist pathWORDLIST2- secondary wordlist path used by combinator-style workflows
Current example:
# Hashcat path
HASHCAT=("/usr/local/bin/hashcat")
# Device to Use (hashcat -d $DEVICE)
DEVICE=1
# Hashtype
HASHTYPE=1000
# File containing target hashes
HASHLIST="input"
# Potfile you want to use
POTFILE="hash-cracker.pot"
# Wordlist(s)
WORDLIST="wordlists/ignis-1M.txt"
WORDLIST2="wordlists/ignis-1K.txt"Run from the repository root:
./hash-cracker.shWith flags:
./hash-cracker.sh [FLAG]Help:
./hash-cracker.sh --helpShow module descriptions:
./hash-cracker.sh --module-infoSearch the local hash type list:
./hash-cracker.sh --search ntlmRun in dry-run mode:
./hash-cracker.sh --dry-runList available jobs and exit:
./hash-cracker.sh --list-jobsRun one job non-interactively:
./hash-cracker.sh --job 8List built-in presets:
./hash-cracker.sh --list-presetsRun a built-in preset:
./hash-cracker.sh --preset quickPreset runs print a compact execution summary with each executed job, result, exit code, and duration.
Run with session logging disabled:
./hash-cracker.sh --no-session-logExport stats JSON to a file:
./hash-cracker.sh --stats-export /tmp/hash-cracker-stats.jsonExport stats JSON with full log history:
./hash-cracker.sh --stats-export /tmp/hash-cracker-stats.json --stats-export-scope allRun non-interactive health checks:
./hash-cracker.sh --self-testCreate and run a reproducible campaign plan (requires python3):
./hash-cracker.sh --plan deep-plus --output campaign.json
./hash-cracker.sh --execute campaign.json
./hash-cracker.sh --resume campaign.jsonCampaign manifests record the ordered jobs, resolved Hashcat commands, immutable input fingerprints, optional execution-artifact fingerprints, per-step exit codes, and command-level execution state. Plans never execute Hashcat. During execution, every Hashcat command receives a deterministic session name and restore-file path. A native checkpoint/runtime stop recorded as interrupted resumes with the same session and logical attempt; the accepted restore file must still exist. If that file disappears, resume fails before changing campaign state or invoking Hashcat. A command left running without a checkpoint is recorded as an unknown, abandoned attempt, then retried with a new session and attempt using the same registered inputs. Other failed commands are retried with a new session and attempt. Generated processor inputs used by an interrupted command are retained unchanged until that campaign step completes.
Each command stores an attempt_outcomes entry for every known logical attempt, with the attempt number, session, start and finish times, outcome, raw exit code, duration, and a SHA-256 reference to the argv vector. The current attempt entry is updated as that attempt pauses or finishes; native pauses are also recorded separately with their attempt and session, so repeated checkpoint resumes remain part of one logical attempt. The digest is SHA-256 over the UTF-8 argv entries joined by NUL bytes; argv values and candidate contents are not copied into each history entry. When a process disappears without a recorded result, the attempt has outcome unknown, null exit code and duration, and a detection timestamp. History is not truncated and grows with retries. Older schema 1 and 2 manifests without attempt history remain readable.
New manifests include a private artifact workspace under <manifest>.state/workspace; generated processor inputs and interrupted-command state are kept there with restrictive permissions. Manifest-controlled workspace, restore-file, session, and preserved-input paths are rejected if they escape that private state. Older manifests without a workspace retain compatibility only for user-owned generated temporary inputs whose names begin with hash-cracker-campaign-; other external preserved-input paths are rejected. Campaign sidecar state is stored beside the manifest under <manifest>.state. Planning or executing the same campaign uses an exclusive lock; another owner fails fast. New manifests reject changed configuration, runtime flags (including FINGERPRINT_SEGMENT_MAX), hashlist, wordlist, Hashcat, processor, selector, rule-file contents, or referenced helper artifacts when those fingerprints are present; older manifests continue to use their recorded input and runtime checks. These content fingerprints do not record native-binary source commits or toolchain provenance. The potfile contents are mutable campaign state, but its canonical target path must remain stable. Appending to or replacing contents at that path is allowed; reductions trigger a safe statistics recount. Campaign sources are built-in presets or non-interactive job IDs: 1, 9, 10, 11, 12, 13, 14, 16, and 19.
Generated inputs are registered with SHA-256 digests before their first command and retained until the entire step completes. Recovery after a crash, a failed command, or an interruption between commands reuses the registered bytes even if the potfile has grown. Missing, changed, or incomplete registered inputs stop recovery with an error. Older interrupted manifests can adopt their recorded preserved files; an already-started step without recorded inputs must be replanned rather than silently regenerated.
Hashcat checkpoint stops (exit 3) and runtime stops (exit 4) pause a campaign when a restore file exists. The wrapper returns 130, stores Hashcat's raw exit code and interruption history, and stops remaining commands. --resume loads an accepted restore file with the same session and attempt. Exit 2, or a checkpoint/runtime stop without a restore file, fails the step; a later explicit resume retries that command with a new session using the same registered inputs. Exhaustion (exit 1) completes a search normally.
Manifest symlinks use the canonical target for sidecar state, locking, and atomic writes; writing through an alias preserves the symlink. Stats exports cannot target the active manifest or anything in its .state directory, including symlink and hardlink aliases of state files.
Potfile-derived iteration, prefix/suffix, common-substring, PACK, fingerprint, Markov, and digit-removal preprocessing share a byte-oriented Python plaintext reader. Potfile records use the last colon-delimited field; Markov wordlist records retain their entire contents, including colons. Valid $HEX[...] values decode before transformations, preserving encoded colons and non-ASCII bytes. Empty encodings are valid. In processor mode, valid encodings that decode to NUL, LF, or CR are skipped with line-numbered byte-category warnings and an aggregate count; processing continues with representable records and fails if all source records are skipped. Malformed or odd-length encodings and raw embedded NUL/CR bytes remain fatal. The standalone reader remains strict by default. Ordinary LF and CRLF record endings are accepted. This reader does not interpret bytes as Unicode characters or change helper input formats.
Run the smoke suite with Bash coverage (requires python3):
make coverageThe report is written to coverage/ and includes HTML and JSON output, including executable-line and function coverage. Coverage uses Bash’s execution trace so nested bash -lc test cases and sourced processor scripts are attributed to the application files. The measured application scope excludes bundled wordlists, rules, and third-party helper binaries; it also excludes shell-only syntax, here-document data, and embedded awk source that Bash cannot execute or trace as Bash lines. When coverage-baseline.txt or function-coverage-baseline.txt is absent, the run writes a candidate under coverage/; once a baseline is committed, subsequent coverage runs fail if the corresponding metric decreases.
Python campaign coverage is measured separately with:
make coverage-pythonThe Python report covers only first-party scripts/campaign.py logic and is written to coverage/python/. If python-coverage-baseline.txt is absent, the run writes a candidate there; once committed, its independent baseline keeps Python and Bash metrics separate.
The default CI suite never starts Hashcat and does not require GPU hardware. An opt-in integration check is available for a Linux runner with a real Hashcat installation and OpenCL/CUDA backend:
HASHCAT_INTEGRATION_DEVICE=1 \
HASHCAT_INTEGRATION_REQUIRE_GPU=1 \
make test-integrationThe check creates isolated temporary inputs, runs real Hashcat-backed mask,
wordlist/rules, hybrid, and combinator paths against known MD5 test values,
and verifies the cracked results in a temporary potfile. It does not use the
developer configuration or modify repository data. HASHCAT_INTEGRATION_REQUIRE_GPU=0
permits a CPU OpenCL backend for local validation.
The manual Hashcat Integration workflow
targets a trusted self-hosted Linux runner with the labels self-hosted,
linux, x64, and hashcat. It is intentionally manual-only because real
Hashcat execution and self-hosted runners are outside the deterministic default
CI contract. The dispatch form defaults to GPU validation; selecting 0 for
require_gpu validates the same cases on a CPU OpenCL backend when the runner
provides one.
By default, hash-cracker enables optimized kernels, enables loopback, disables hardware monitoring, and shows cracked hashes on stdout.
-l,--no-loopback- disable loopback functionality-n,--no-limit- disable optimized kernels--hwmon-enable- enable hardware monitoring-m,--module-info- print descriptions of the available modules and exit-s [hash-name],--search [hash-name]- search the local hash type database and exit-d,--disable-cracked- suppress cracked hashes on stdout by writing them to/dev/null--dry-run- print the resolved hashcat commands without executing them; preprocessor file/tool actions are reported and skipped--no-session-log- disable session stats logging to file--session-log-keep [N],--session-log-keep=N- keep lastNauto-created session logs inlogs/(default:0, no pruning)--stats-debug- print whether stats refresh used incremental update or full recount--stats-export [PATH],--stats-export=PATH- export machine-readable session stats JSON toPATH--stats-export-scope [latest|all],--stats-export-scope=...- export latest snapshot only (latest, default) or include all parsed entries fromlogs/session-*.log(all)--job [ID],--job=ID- run one menu option non-interactively and exit (supports1-22and99)--list-jobs- print available job IDs and exit--preset [NAME],--preset=NAME- run a built-in non-interactive job preset and exit--list-presets- print available built-in presets and exit--plan [PRESET|JOB] --output PATH- create a versioned campaign manifest without executing Hashcat--execute PATH- execute a campaign manifest from its first incomplete step--resume PATH- resume an interrupted or failed campaign manifest--self-test,--doctor- run non-interactive configuration and dependency checks, then exit with status code
Presets run safe non-interactive jobs in sequence, stop on the first failed job, and print a run summary with timings.
quick- baseline and iteration coverage (1,9)quick-plus- quick coverage plus common substring pass (1,9,11)deep- baseline, iteration, prefix/suffix, substring, and digit-remover coverage (1,9,10,11,19)deep-plus- extended potfile-driven coverage with prefix/suffix and substring passes (1,9,10,11,14,19,9)
When the tool starts successfully, it opens an interactive menu with these options:
- Brute force
- Light rules
- Heavy rules
- Enter specific word/name/company
- Enter specific word/name/company (brute force)
- Hybrid
- Toggle-case
- Combinator
- Iterate results
- Prefix suffix
- Common substring
- PACK rulegen
- PACK mask
- Fingerprint attack
- Directory of word lists plain and then with
OneRuleToRuleThemAll - Username iteration (only complete NTDS)
- Markov-chain passwords generator
- CeWL wordlist generator
- Digit remover
- Stacker
- Custom brute force
- Directory of word lists plain and then with
buka_400k - Session stats dashboard
- Options
2,3,6,7, and20ask whether to use a single wordlist or multiple wordlists. - Option
8usesWORDLISTandWORDLIST2from the config file. - Options
15and22accept either a directory of wordlists or a single wordlist file. - Options
4and5prompt for a custom word or company name. - Option
16expects an NTDS-style input file and extracts usernames fromHASHLIST. - Option
17can generate candidates from either the potfile or a selected wordlist. - Option
18prompts for a URL, output wordlist name, spider depth, and minimum word length. - Option
21prompts for a decimal brute-force length from1to99and whether increment mode should be enabled; leading zeros are normalized. - Option
14generates fingerprint fragments internally from the potfile. SetFINGERPRINT_SEGMENT_MAXto a decimal value from1to64to tune the fragment length limit; leading zeros are normalized. The default is8, producing combinator candidates up to16characters. --jobmode runs selected options directly without entering the interactive menu.- In non-interactive contexts, prompt-dependent jobs (for example
4,5,8,15,17,18,21,22) fail fast with a clear message.
- At startup, before initial counters are built, the tool prints:
Preparing session stats (counting potfile and input hashes)...
- After a job, if the potfile changed and unique plaintext recount is needed, the tool prints:
Refreshing session stats (recounting unique potfile plaintexts, this may take a moment)...
- Option
99prints a dedicated ASCII dashboard with:- session delta (
new ... lines,... unique,... bytes) - total cracked passwords in potfile (
... lines) - unique input hashes from the active
HASHLIST - log status/retention/current log file
- session delta (
- Session stats are written to a timestamped log file:
- Default per-session file:
logs/session-YYYYmmdd-HHMMSS-PID.log - Convenience pointer:
logs/latest.logpoints to the current session file - Retention for auto-created logs defaults to
0(no pruning) - Set
--session-log-keep [N]to tune retention. Values are decimal integers from0to2147483647; leading zeros are normalized (08means8). Out-of-range values fail before logs are created or pruned - Override path with
SESSION_STATS_LOGFILE - Set
--no-session-logto disable file logging
- Default per-session file:
- With
--stats-debug, the tool prints which refresh path was used (incrementalorfull recount). - With
--stats-export, current stats are written as JSON on each refresh cycle. - Session logs and requested JSON exports are local audit artifacts. JSON export write failures are fatal; default session-log failures are reported as warnings so a cracking run is not mistaken for a missing audit record.
- Exported JSON includes a top-level
"schema_version"for stable downstream parsing. - With
--stats-export-scope latest(default), JSON contains the latest snapshot only. - With
--stats-export-scope all, JSON also includes parsed history fromsession-*.logfiles in the configured session-log directory.
Sample hashes are provided in example-hashes/:
- MD5 (
-m 0) - SHA1 (
-m 100) - NTLM (
-m 1000)
For large public datasets, see Have I Been Pwned Passwords.
See VERSION.md.
These checks are maintainer-only and are not required for end users running hash-cracker.
make lint
make format-check
make format
make qa
make test-smokemake lintrunsshellcheckon project shell scripts.make format-checkverifies formatting withshfmt.make formatappliesshfmtformatting changes.make verify-toolschecks ShellCheck0.10.0and shfmt3.8.0.make test-smokeruns non-interactive menu and dry-run smoke checks.make coverage-pythonmeasures first-party campaign coverage separately from Bash coverage.- CI installs the pinned lint and format tool releases with checksum verification.
- CI runs the maintainer checks, campaign tests, and separate Bash/Python coverage jobs automatically on push and pull request;
workflow_dispatchis available for a manual rerun.
GNU GPLv3