| Tested on | Kali GNU/Linux Rolling 2026.2 (kali-rolling) |
|---|---|
| Package | hashcat 7.1.2+ds1-4pocl-opencl-icd (CPU OpenCL when no GPU) |
| Lab environment | Pentest lab |
| Cert prep | CompTIA PenTest+ PT0-003 — password attacks and offline hash cracking |
| Privilege | Normal user for cracking; sudo for apt install |
| Scope | Hashcat workflow on Kali: -m vs -a, identify modes, dictionary and rules, masks and hybrids, potfile and --show, Cracked vs Exhausted, and common load errors. Does not cover live login brute force, rainbow tables, or cracking third-party dumps without authorization. |
| Related guides | SSH brute force attack |
Hashcat recovers plaintext by hashing candidate passwords and comparing them to your target. Beginners often mix up two independent flags: -m names the hash algorithm, and -a names how candidates are generated.
I ran every command below on Kali Rolling with hashcat 7.1.2 against hashes I created under /tmp/hashcat-lab. This lab VM has no discrete GPU, so OpenCL came from PoCL on the CPU. Speeds change on a machine with a real GPU. The workflow does not.
Understand -m and -a first
Before long install notes, lock these two decisions:
┌──────────────────────┐ ┌──────────────────────┐
│ Hash file / string │ │ Candidates │
└──────────┬───────────┘ └──────────┬───────────┘
↓ ↓
┌──────────────────────┐ ┌──────────────────────┐
│ -m hash mode │ │ -a attack mode │
│ (what algorithm?) │ │ (how are guesses │
│ │ │ built?) │
└──────────┬───────────┘ └──────────┬───────────┘
└────────────┬───────────────┘
↓
hashcat compares digests| Flag | Question it answers | Examples |
|---|---|---|
-m |
What algorithm produced this digest? | 0 MD5, 1000 NTLM, 3200 bcrypt |
-a |
How should candidates be built? | 0 wordlist, 3 mask, 6/7 hybrid |
Wrong -m loads nothing or cracks the wrong thing. Wrong -a wastes a correct mode on a useless candidate space. Treat them as two separate choices every time.
Verify hashcat and hardware
Confirm the package version so your help text and example hashes match what you see in docs for the 7.x line:
hashcat --versionv7.1.2List OpenCL, CUDA, or HIP devices next. This is where you learn whether hashcat sees a GPU or only a CPU runtime:
hashcat -IOpenCL Platform ID #1
Vendor..: The pocl project
Name....: Portable Computing Language
Version.: OpenCL 3.0 PoCL 7.1+debian Linux, None+Asserts, RELOC, SPIR-V, LLVM 21.1.8, SLEEF, DISTRO, POCL_DEBUG
Backend Device ID #01
Type...........: CPU
Vendor.........: GenuineIntel
Name...........: cpu-haswell-12th Gen Intel(R) Core(TM) i7-1255U
Processor(s)...: 2No platform listed usually means a missing OpenCL, CUDA, or HIP runtime, not a broken hashcat binary. On this VM I installed pocl-opencl-icd for CPU OpenCL after hashcat -I reported CL_PLATFORM_NOT_FOUND_KHR. NVIDIA and AMD hosts need their vendor stacks instead.
A short benchmark shows how radically speed depends on the algorithm, not only on hardware. Compare MD5 and bcrypt on the same device:
hashcat -b -m 0* Hash-Mode 0 (MD5)
Speed.#01........: 32386.5 kH/s (16.92ms) @ Accel:287 Loops:1024 Thr:1 Vec:8Repeat the benchmark for bcrypt so you can compare a slow hash on the same device:
hashcat -b -m 3200* Hash-Mode 3200 (bcrypt $2*$, Blowfish (Unix)) [Iterations: 32]
Speed.#01........: 221 H/s (16.13ms) @ Accel:2 Loops:32 Thr:1 Vec:1Same CPU, same hashcat build: MD5 sat near 32 million hashes per second, bcrypt near 221. GPU usually wins on massively parallel fast hashes, but a slow algorithm stays expensive on any device. Do not compare your MD5 GPU number to someone else's bcrypt CPU number and call one setup broken.
Identify the correct hash mode
Create a lab directory and a known MD5 so every later step is reproducible:
mkdir -p /tmp/hashcat-lab && cd /tmp/hashcat-labmkdir is silent on success, so generate the MD5 of password into a hash file next:
echo -n 'password' | md5sum | awk '{print $1}' | tee md5_password.hash5f4dcc3b5aa765d61d8327deb882cf99Ask hashcat which modes fit that string. Autodetect lists several matches because a 32-character hex digest is ambiguous:
hashcat --identify md5_password.hashThe following 12 hash-modes match the structure of your input hash:
# | Name | Category
======+============================================================+======================================
900 | MD4 | Raw Hash
0 | MD5 | Raw Hash
70 | md5(utf16le($pass)) | Raw Hash
2600 | md5(md5($pass)) | Raw Hash salted and/or iterated
3500 | md5(md5(md5($pass))) | Raw Hash salted and/or iterated
4400 | md5(sha1($pass)) | Raw Hash salted and/or iterated
20900 | md5(sha1($pass).md5($pass).sha1($pass)) | Raw Hash salted and/or iterated
32800 | md5(sha1(md5($pass))) | Raw Hash salted and/or iterated
4300 | md5(strtoupper(md5($pass))) | Raw Hash salted and/or iterated
1000 | NTLM | Operating System
9900 | Radmin2 | Operating System
8600 | Lotus Notes/Domino 5 | Enterprise Application Software (EAS)MD5 (0) and NTLM (1000) both appear. Context decides: a Unix web dump is not an Active Directory NTLM hash just because the hex length matches. When you know the source, force -m from that knowledge. When you do not, use example hashes and --hash-info instead of guessing.
Pull the official example and category for the modes you care about:
hashcat -m 0 --hash-infoHash mode #0
Name................: MD5
Category............: Raw Hash
Slow.Hash...........: No
Example.Hash........: 8743b52063cd84097a65d1633f5c74f5
Example.Pass........: hashcatNTLM is the Windows counterpart that often collides with MD5 on --identify. Check its info block the same way:
hashcat -m 1000 --hash-infoHash mode #1000
Name................: NTLM
Category............: Operating System
Slow.Hash...........: No
Example.Hash........: b4b9b02e6f09a9bd760f388b67351e2b
Example.Pass........: hashcatThen pull bcrypt so you can see a Slow.Hash marked Yes and an embedded $2a$ example:
hashcat -m 3200 --hash-infoHash mode #3200
Name................: bcrypt $2*$, Blowfish (Unix)
Category............: Operating System
Slow.Hash...........: Yes
Example.Hash........: $2a$05$MBCzKhG1KhezLh.0LRa0Kuw12nLJtpHy6DIaU.JAnqJUDYspHC.Ou
Example.Pass........: hashcat| Mode | Name | Slow.Hash | Lab note |
|---|---|---|---|
0 |
MD5 | No | Fast raw digest; common in CTFs and old apps |
1000 |
NTLM | No | Windows; same hex length as MD5, different algorithm |
3200 |
bcrypt | Yes | Deliberately slow; expect tiny H/s relative to MD5 |
--example-hashes is an alias of --hash-info on this build. Prefer -m N --hash-info when you already know the candidate mode number.
Dictionary attack with -a 0
Straight mode (-a 0) tries each line in a wordlist as-is. Build a tiny list that contains the known password, then crack the MD5:
printf 'password\nadmin\nwelcome\nlabpass123\nWinter2024\nPassword1\n' > tiny.lstWith the list on disk, run a straight dictionary attack (-a 0) against the MD5:
hashcat -m 0 -a 0 md5_password.hash tiny.lst -O -w 2 --potfile-path /tmp/hashcat-lab/hashcat.pot --logfile-disable5f4dcc3b5aa765d61d8327deb882cf99:password
Session..........: hashcat
Status...........: Cracked
Hash.Mode........: 0 (MD5)
Hash.Target......: 5f4dcc3b5aa765d61d8327deb882cf99
Guess.Base.......: File (tiny.lst)
Speed.#01........: 71 H/s (0.01ms) @ Accel:1024 Loops:1 Thr:1 Vec:8
Recovered........: 1/1 (100.00%) Digests (total), 1/1 (100.00%) Digests (new)
Progress.........: 6/6 (100.00%)
Candidates.#01...: password -> Password1Read the status block before you chase speed numbers:
| Field | What it means |
|---|---|
| Status | Current job state; detailed Cracked vs Exhausted examples below |
| Hash.Mode | Confirms the -m you chose |
| Speed | Current hash rate on each device |
| Progress | How far through the candidate space you are |
| Recovered | Digests cracked versus total loaded |
| Candidates | Sample of what is being tried right now |
| Time.Estimated | ETA for the current attack |
The 71 H/s line looks weak because the six-word list is tiny; hashcat warned that the keyspace was too small to fill the device. That is normal for a teaching wordlist.
On Kali, expand to rockyou after you decompress it once. Install or refresh packages with apt when hashcat or wordlists are missing:
sudo gzip -dk /usr/share/wordlists/rockyou.txt.gzPoint the same -m 0 -a 0 command at the full rockyou file:
hashcat -m 0 -a 0 md5_password.hash /usr/share/wordlists/rockyou.txt -O -w 2 --potfile-path /tmp/hashcat-lab/hashcat.pot --logfile-disable5f4dcc3b5aa765d61d8327deb882cf99:password
Session..........: hashcat
Status...........: Cracked
Hash.Mode........: 0 (MD5)
Guess.Base.......: File (/usr/share/wordlists/rockyou.txt)
Speed.#01........: 206.9 kH/s (8.33ms) @ Accel:1024 Loops:1 Thr:1 Vec:8
Recovered........: 1/1 (100.00%) Digests (total), 1/1 (100.00%) Digests (new)
Progress.........: 2048/14344385 (0.01%)
Candidates.#01...: 123456 -> lovers1password sits early in rockyou, so Recovered hit 100% after a tiny slice of the file. Progress at 0.01% with Status Cracked is success, not a stalled job.
NTLM uses the same attack mode with a different -m. Build an NTLM of Password1 and crack it from the same tiny list:
python3 -c "import hashlib; print(hashlib.new('md4', 'Password1'.encode('utf-16le')).hexdigest())" | tee ntlm.hash64f12cddaa88057e06a81b54e73b949bThis worked on the tested Kali build because its OpenSSL configuration exposes MD4. On distributions where Python reports unsupported hash type md4, use a known NTLM test hash or enable the distribution-supported legacy provider rather than changing the Hashcat command.
Crack that NTLM with mode 1000 and the same tiny wordlist:
hashcat -m 1000 -a 0 ntlm.hash tiny.lst -O -w 2 --potfile-path /tmp/hashcat-lab/hashcat.pot --logfile-disable64f12cddaa88057e06a81b54e73b949b:Password1
Session..........: hashcat
Status...........: Cracked
Hash.Mode........: 1000 (NTLM)
Recovered........: 1/1 (100.00%) Digests (total), 1/1 (100.00%) Digests (new)Same wordlist, different hash mode. That is the -m versus -a split in practice.
Cracked versus Exhausted
Exhausted is not a crash. It means hashcat finished every candidate without recovering all digests.
Prove it with a hash whose plaintext is missing from the list:
echo -n 'secret' | md5sum | awk '{print $1}' | tee md5_secret.hash5ebe2294ecd0e0f08eab7690d2a6ee69Build a three-word list that deliberately omits secret:
printf 'password\nadmin\nwelcome\n' > tiny_miss.lstRun the dictionary attack against that incomplete list and watch the final Status line:
hashcat -m 0 -a 0 md5_secret.hash tiny_miss.lst -O -w 2 --potfile-path /tmp/hashcat-lab/miss.pot --logfile-disableSession..........: hashcat
Status...........: Exhausted
Hash.Mode........: 0 (MD5)
Hash.Target......: 5ebe2294ecd0e0f08eab7690d2a6ee69
Guess.Base.......: File (tiny_miss.lst)
Recovered........: 0/1 (0.00%) Digests (total), 0/1 (0.00%) Digests (new)
Progress.........: 3/3 (100.00%)
Candidates.#01...: password -> welcomeProgress 100% with Recovered 0/1 means the strategy failed, not the tool. Next step is a better wordlist, rules, a mask that matches a known pattern, or confirming -m is still correct.
| Status | Meaning | Typical next step |
|---|---|---|
| Cracked | All loaded digests recovered | hashcat --show / potfile for reporting |
| Exhausted | Candidate space finished; some digests remain | Change list, rules, mask, or mode |
| Running | Attack still in progress | Watch Recovered and Time.Estimated |
Add rules to a wordlist
Rules mutate each base word (case changes, digits, leetspeak) without rewriting the dictionary by hand. Kali ships rule files under /usr/share/hashcat/rules/. This build includes best66.rule (there is no best64.rule in the package I tested).
Crack Winter2024 from a lowercase base word plus rules:
echo -n 'Winter2024' | md5sum | awk '{print $1}' | tee md5_winter.hash2a28bf27d9b2f12a7213bf4635f3f1c6Put the lowercase base word in a short list:
printf 'winter2024\npassword\n' > rules_base.lstApply best66.rule so case and digit mutations can reach Winter2024:
hashcat -m 0 -a 0 md5_winter.hash rules_base.lst -r /usr/share/hashcat/rules/best66.rule -O -w 2 --potfile-path /tmp/hashcat-lab/rules.pot --logfile-disable2a28bf27d9b2f12a7213bf4635f3f1c6:Winter2024
Session..........: hashcat
Status...........: Cracked
Guess.Base.......: File (rules_base.lst)
Guess.Mod........: Rules (/usr/share/hashcat/rules/best66.rule)
Recovered........: 1/1 (100.00%) Digests (total), 1/1 (100.00%) Digests (new)Guess.Mod confirms rules applied. For engagement-specific dictionaries (company names, product codes), build or refine lists with a wordlist generator workflow, then layer rules instead of inventing every variant yourself.
Mask attacks with -a 3
Masks describe each character position with a charset placeholder. Use them when you know the pattern better than you know the exact password.
| Placeholder | Character set |
|---|---|
?l |
lowercase a-z |
?u |
uppercase A-Z |
?d |
digits 0-9 |
?s |
specials |
?a |
?l?u?d?s (printable) |
?b |
full byte 0x00-0xff |
Keyspace grows fast. Compare total candidates for three masks on this build:
hashcat --total-candidates -a 3 '?l?l?l?l'456976Add four digit positions and measure again:
hashcat --total-candidates -a 3 '?l?l?l?l?d?d?d?d'4569760000Finally count eight full ?a positions so the jump in keyspace is obvious:
hashcat --total-candidates -a 3 '?a?a?a?a?a?a?a?a'6634204312890625Four lowercase letters are under half a million candidates. Four lowercase plus four digits jumps to about 4.6 billion. Eight full ?a positions is over six quadrillion. That ?a example is here to show keyspace growth, not as a recommended attack. Broad masks become impractical quickly, especially for slow hashes such as bcrypt or Argon2. Knowing the likely password structure can reduce an impractical keyspace to one that can actually finish.
Crack a short ?l?l?l?d password (lab1) to see mask status fields:
echo -n 'lab1' | md5sum | awk '{print $1}' | tee md5_lab1.hashe274b0a65912e49a28a9ae5c1479bdceAttack it with mask mode and the ?l?l?l?d pattern:
hashcat -m 0 -a 3 md5_lab1.hash '?l?l?l?d' -O -w 2 --potfile-path /tmp/hashcat-lab/mask.pot --logfile-disableSession..........: hashcat
Status...........: Cracked
Hash.Mode........: 0 (MD5)
Guess.Mask.......: ?l?l?l?d [4]
Speed.#01........: 15134.0 kH/s (1.02ms) @ Accel:1024 Loops:16 Thr:1 Vec:8
Recovered........: 1/1 (100.00%) Digests (total), 1/1 (100.00%) Digests (new)
Progress.........: 32768/175760 (18.64%)Guess.Mask shows the pattern. Progress can sit below 100% when the match appears early; Status Cracked is the signal that matters.
Custom charsets with -1 through -4
When policy or OSINT narrows the alphabet, build a custom charset instead of spraying ?a everywhere. -1 through -4 define custom sets; ?1 through ?4 use them in the mask.
Crack Lab! with letters in -1 and three symbols in -2:
echo -n 'Lab!' | md5sum | awk '{print $1}' | tee md5_labbang.hash5c1d4df6ecadc2e6a01b727af5799c69Define two custom sets and reference them as ?1 and ?2 in the mask:
hashcat -m 0 -a 3 md5_labbang.hash -1 '?l?u' -2 '!@#' '?1?1?1?2' -O -w 2 --potfile-path /tmp/hashcat-lab/cs.pot --logfile-disableSession..........: hashcat
Status...........: Cracked
Guess.Mask.......: ?1?1?1?2 [4]
Guess.Charset....: -1 ?l?u, -2 !@#, -3 N/A, -4 N/A, -5 N/A, -6 N/A, -7 N/A, -8 N/A
Recovered........: 1/1 (100.00%) Digests (total), 1/1 (100.00%) Digests (new)Quote custom charsets when the shell would eat ! or other specials. Narrow alphabets are for authorized testing where you already know policy shape, not for blind spraying of every printable character.
Hybrid attacks (-a 6 and -a 7)
Hybrids combine a wordlist with a mask:
| Mode | Layout | Typical use |
|---|---|---|
-a 6 |
wordlist + mask on the right | admin + year digits |
-a 7 |
mask on the left + wordlist | prefix digits/symbols + base word |
Crack admin2024 as dictionary word plus four digits:
echo -n 'admin2024' | md5sum | awk '{print $1}' | tee md5_admin2024.hash8d4db54daf7d67db5f3c96e43f61c609Keep the base words in a two-line list:
printf 'admin\nroot\n' > hybrid.lstRun hybrid mode 6 so each word is followed by four digits:
hashcat -m 0 -a 6 md5_admin2024.hash hybrid.lst '?d?d?d?d' -O -w 2 --potfile-path /tmp/hashcat-lab/hybrid.pot --logfile-disable8d4db54daf7d67db5f3c96e43f61c609:admin2024
Session..........: hashcat
Status...........: Cracked
Guess.Base.......: File (hybrid.lst), Left Side
Guess.Mod........: Mask (?d?d?d?d) [4], Right Side
Recovered........: 1/1 (100.00%) Digests (total), 1/1 (100.00%) Digests (new)Reach for hybrid when users append seasons, years, or ticket numbers to a short dictionary word. Leave obscure attack modes alone until straight, rules, mask, and hybrid are exhausted.
Sessions, restore, potfile, and --show
Name long jobs with --session so you can stop and continue later:
hashcat -m 0 -a 3 md5_lab1.hash '?l?l?l?d' --session labdemo -O -w 2 --potfile-path /tmp/hashcat-lab/hashcat.pot --logfile-disableSession..........: labdemo
Status...........: CrackedOn a job that is still running, resume with the same session name after a clean stop. For a long-running job, press c to stop at the next checkpoint so the restore state is current. q quits immediately and can lose progress made since the most recent restore point. On this Kali package, session files are stored under ~/.local/share/hashcat/sessions/; other installation layouts may use a different location:
hashcat --session labdemo --restoreThat command picks up the saved restore point instead of restarting the keyspace from zero.
Cracked pairs land in the potfile. Show them against the original hash list without rerunning the attack:
hashcat -m 0 md5_password.hash --show --potfile-path /tmp/hashcat-lab/hashcat.pot5f4dcc3b5aa765d61d8327deb882cf99:passwordUse --potfile-path in labs so teaching runs do not pollute your default pot, and so --show reads the same file you just wrote.
Hashcat troubleshooting
Hash-loading errors usually point to the input format or the selected -m, not the candidate attack. The examples below reproduce the common parser messages so you can recognize them quickly.
Token length exception
A truncated or wrong-length token for the selected mode fails before any candidates run:
printf 'deadbeef\n' > short.hashLoad that truncated string as MD5 and read the parser error:
hashcat -m 0 -a 0 short.hash tiny.lst -O -w 2 --potfile-path /tmp/hashcat-lab/err.pot --logfile-disableHashfile 'short.hash' on line 1 (deadbeef): Token length exception
Parsing Hashes: 0/1 (0.00%)...
* Token length exception: 1/1 hashes
This error happens if the wrong hash type is specified, if the hashes are
malformed, or if input is otherwise not as expected (for example, if the
--username or --dynamic-x option is used but no username or dynamic-tag is present)
No hashes loaded.Common causes:
- Wrong
-mfor the string you have - Truncated copy/paste or trailing cut-off
- Extra whitespace or a header line treated as a hash
- Username or dynamic-tag options enabled without matching fields
Separator unmatched
Salted or structured modes expect delimiters. Feeding a raw MD5 into mode 10 (MD5 $pass.$salt) fails like this:
printf '5f4dcc3b5aa765d61d8327deb882cf99\n' > plain_as_salted.hashSelect salted mode 10 against that unsalted line:
hashcat -m 10 -a 0 plain_as_salted.hash tiny.lst -O -w 2 --potfile-path /tmp/hashcat-lab/err.pot --logfile-disableHashfile 'plain_as_salted.hash' on line 1 (5f4dcc3b5aa765d61d8327deb882cf99): Separator unmatched
Parsing Hashes: 0/1 (0.00%)...No hashes loaded.Common causes:
- Hash file format does not match the selected mode
- Wrong file (wordlist or export) passed as the hash list
- Extracted line missing salt, iteration, or
$…$wrappers
No hashes loaded
Empty lines and junk produce the same final line. Whitespace-only input:
printf '\n' > empty.hashAsk hashcat to load it as MD5:
hashcat -m 0 -a 0 empty.hash tiny.lst -O -w 2 --potfile-path /tmp/hashcat-lab/err.pot --logfile-disableParsing Hashes: 0/1 (0.00%)...No hashes loaded.Garbage under bcrypt:
printf 'not-a-bcrypt-hash\n' > junk.hashForce bcrypt mode against that junk line:
hashcat -m 3200 -a 0 junk.hash tiny.lst -w 2 --potfile-path /tmp/hashcat-lab/err.pot --logfile-disableHashfile 'junk.hash' on line 1 (not-a-bcrypt-hash): Separator unmatched
Parsing Hashes: 0/1 (0.00%)...No hashes loaded.Fix the input and -m first. Candidate strategy does not matter until at least one digest loads.
GPU not detected or poor speed
| Symptom | Likely cause | Fix |
|---|---|---|
CL_PLATFORM_NOT_FOUND_KHR / no devices |
Missing OpenCL, CUDA, or HIP runtime | Install vendor runtime or PoCL for CPU labs; re-run hashcat -I |
| Tiny H/s on bcrypt/Argon2 | Slow hash by design | Expected; compare same -m across machines |
| Tiny H/s on MD5 with a six-word list | Keyspace too small to fill the device | Use a real wordlist or mask; ignore teaching-list speed |
| Friend's benchmark much faster | Different GPU, driver, -m, or -O/-w |
Match mode and options before comparing |
| Pure kernel ATTENTION message | Optimized kernel not selected | Add -O when password length fits the optimized limit |
John the Ripper versus hashcat
| Topic | Hashcat | John the Ripper (Jumbo) |
|---|---|---|
| Strength | GPU-oriented high-throughput attack engine; large mode catalog | Single mode, convenient *2john converters, mixed CPU workflows |
| Mode selection | Explicit -m / -a |
--format plus cracking mode flags |
| Rules / masks | First-class -r and -a 3 |
Rules and mask/incremental modes |
| File prep | Often needs an already extracted hash | zip2john, ssh2john, pdf2john, unshadow |
| When I reach for it | Bulk fast hashes, masks, GPU time | Archives, odd formats, single-host CPU work |
Neither tool wins every job. Prepare the hash correctly, then pick the engine that matches hardware and input shape. For John's input pipeline see John the Ripper.
Summary
Hashcat work is two independent decisions plus a feedback loop: identify the digest (-m), choose how candidates are built (-a), read Status and Recovered, then refine. Autodetect on a 32-character hex string can list MD5 and NTLM together, so where the hash came from matters more than hex length alone.
Exhausted means the selected candidate space finished without recovering every hash; change or expand the attack strategy if further testing is justified. Token length exception, Separator unmatched, and No hashes loaded are input or mode problems. Fix those before you tune wordlists.
Start with a controlled dictionary run. Add rules when users mutate passwords. Use masks and custom charsets when the pattern is known. Keep long jobs under a named session and pull plaintext with --show against the potfile. For live login guessing rather than offline hashes, use a service attack path such as SSH brute force instead of hashcat.
References
- hashcat wiki
- hashcat example hashes / hash info
- hashcat mask attack
- hashcat rule-based attack
- hashcat FAQ: supplying more work
Frequently Asked Questions
What is the difference between hashcat -m and -a?
-m selects the hash type and -a selects the candidate-generation method. Hashcat 7 can attempt hash-mode autodetection when -m is omitted, while attack mode defaults to -a 0; in repeatable pentest workflows, explicitly setting the known values makes the command easier to verify.
Why does hashcat say No hashes loaded?
The file did not yield any digests that match the selected mode. Typical causes are the wrong -m, truncated lines, empty or whitespace-only files, missing salts for salted modes, or a wordlist accidentally passed as the hash file.
What does Status Exhausted mean in hashcat?
Hashcat finished the entire candidate space without recovering every loaded digest. It is not a crash. Expand or change the wordlist, add rules, tighten a mask, or verify the hash mode.
Why is my hashcat benchmark much slower than someone else's?
Compare the same hash mode, similar hardware class, and similar options (-O, workload). Fast hashes on a discrete GPU differ wildly from bcrypt on a CPU OpenCL runtime. Different -m values make speed comparisons meaningless.
How do I show cracked passwords in hashcat?
Run hashcat -m <mode> hashfile --show with the same potfile path you used while cracking. Matches are stored as hash:plaintext in the potfile so you do not need to repeat the attack.

