Hash Cracking with Hashcat and John the Ripper
- Hash Cracking with Hashcat and John the Ripper
Why offline cracking matters
When you breach a system — or audit your own — you rarely walk away with plaintext passwords. You walk away with hashes: the one-way cryptographic digests systems store instead of passwords. A hash cannot be reversed, but it can be guessed against: hash a candidate, compare it to the stolen digest, repeat billions of times per second. That is all “cracking” is. It is the offensive mirror of the defensive choices a system made — fast hash vs. slow hash, salted vs. unsalted, MFA vs. password-only.
This page covers the two dominant offline crackers — Hashcat (GPU-first) and John the Ripper (CPU-first, batteries-included) — and the techniques that separate a dictionary that finds nothing from one that finds everything: wordlists, rules, mask attacks, combinators, and dynamic expansions like the Purple Rain attack. It pairs with Cryptography (what a hash is and why slow KDFs exist) and Identity and Access Management (why password policy and MFA are the real fixes).
⚠️ Authorization first. Cracking hashes you were not authorized to obtain — or testing credentials against systems you do not own — is a crime in the general sense, the same way cracking WiFi is. Everything here assumes your own lab, a CTF, or a scoped engagement with written permission.
How cracking actually works
Three properties of the stored hash decide how hard your job is:
| Property | Defender’s choice | Effect on cracking |
|---|---|---|
| Algorithm speed | Fast (MD5, SHA-1, NTLM) vs. slow KDF (bcrypt, scrypt, Argon2, PBKDF2) | A GPU does billions of MD5/s but only thousands of bcrypt/s. The KDF is the single biggest defense. |
| Salt | Unique random salt per password | Defeats precomputed (rainbow-table) attacks and forces each hash to be attacked individually. |
| Iterations / cost | High work factor (e.g. bcrypt cost 12) | Each guess costs more, multiplying total attack time. |
The cracker’s job is to generate candidates and hash them faster than the defender hoped. You never brute-force the whole keyspace for a real password — you exploit the fact that humans pick predictable passwords. That is why wordlists + rules beat raw brute force almost every time.
Identifying the hash first
You cannot crack what you cannot name. Before anything else, identify the hash type:
# hashid ships in Kali; it maps a hash string to candidate types + hashcat -m modes
hashid '$2b$12$R9h/cIPz0gi.URNNX3kh2OPST9/PgBkqquzi.Ss7KIUgO2t0jWMUW'
# -> bcrypt, hashcat mode 3200
# John's own identifier
~/john/run/hashid.pl <hashfile> # (or simply let `john` autodetect on load)
Hashcat’s -m (mode) numbers are the canonical reference: 0 = MD5, 100 = SHA-1, 1000 = NTLM, 1800 = sha512crypt, 3200 = bcrypt, 22000 = WPA-PBKDF2 (the modern WPA mode). Full list: hashcat --help | less or the hashcat hash-modes reference.
John the Ripper: batteries included
John the Ripper — specifically the community “jumbo” build (openwall/john) — supports 400+ formats and autodetects most of them. Its great strength for beginners is that it needs almost no configuration: point it at a file and it just runs.
john --list=formats | tr ',' '\n' | wc -l # 400+ supported formats
john --list=formats | tr ',' '\n' | grep -i bcrypt
Worked example — Linux shadow hashes
The classic exercise. John bundles unshadow to merge /etc/passwd and /etc/shadow into the single file its crackers expect:
# 1. Combine passwd + shadow (needs root to read /etc/shadow)
sudo unshadow /etc/passwd /etc/shadow > mypasswd.txt
# 2. Single-crack mode first (uses usernames/GECOS as hints) — fast, free wins
john --single mypasswd.txt
# 3. Then a wordlist with mangling rules
john --wordlist=/usr/share/wordlists/rockyou.txt --rules=Jumbo mypasswd.txt
# 4. Show what cracked (reads john.pot)
john --show mypasswd.txt
John writes every crack to ~/.john/john.pot and tracks progress in john.rec, so you can Ctrl-C and resume later with john --restore.
ℹ️ John’s default mode (just
john mypasswd.txtwith no flags) runs single-crack, then its built-in wordlist with rules, then incremental brute force — a sensible escalation you can lean on when you do not know where to start.
The *2john conversion utilities
This is John’s signature feature and the main reason to keep it installed even if you crack on a GPU with Hashcat: a family of 50+ extractors that pull a crackable hash out of an encrypted file or artifact. They all follow the <thing>2john naming pattern, write a hash to stdout, and produce output you can feed to either John or (often) Hashcat.
ls ~/john/run/*2john* # from source build
ls /usr/sbin/*2john* /usr/bin/*2john* 2>/dev/null # Kali package layout
Commonly used converters:
| Utility | Extracts a hash from |
|---|---|
zip2john / rar2john / 7z2john.pl |
Password-protected archives |
office2john.py |
MS Office (Word/Excel/PowerPoint) documents |
pdf2john.pl |
Encrypted PDFs |
ssh2john.py |
Passphrase-protected SSH private keys |
keepass2john |
KeePass .kdbx databases |
bitlocker2john |
BitLocker-encrypted volumes |
gpg2john |
GnuPG secret keyrings |
unshadow |
/etc/passwd + /etc/shadow (merge, not extract) |
Worked example — crack a password-protected ZIP:
# Extract the hash, then crack it
zip2john secrets.zip > zip.hash
john --wordlist=/usr/share/wordlists/rockyou.txt zip.hash
john --show zip.hash
# Or hand the same hash to Hashcat (mode 17200/17210/13600 depending on ZIP variant)
hashcat -m 17200 -a 0 zip.hash /usr/share/wordlists/rockyou.txt
The ssh2john → crack flow is identical and is the standard way to attack a stolen but passphrase-protected SSH key (see SSH).
Hashcat: the GPU workhorse
Hashcat is built for raw throughput on the GPU. Where John autodetects, Hashcat makes you specify the mode (-m) and attack (-a) explicitly — the tradeoff for being the fastest cracker available.
The attack modes you will actually use:
-a |
Attack | Use it when |
|---|---|---|
0 |
Straight (wordlist, optionally + rules) | You have a good wordlist — the default starting point |
1 |
Combinator (word ⊕ word from two lists) | Passwords look like wordword / two concatenated tokens |
3 |
Mask (brute force over a charset pattern) | You know the structure (e.g. 8 digits, or Word####) |
6 / 7 |
Hybrid (wordlist + mask, or mask + wordlist) | password2026, summer!23 — word with appended/prepended pattern |
Each attack is really a different candidate generator feeding the same GPU hashing core. The rest of this section takes the four you will actually use and shows each one cracking a purpose-built hash file, with real output. Common flags throughout: -m 0 is MD5 (used here so the examples finish instantly), -O enables the optimized kernel (faster, caps password length ~31), and -w 3 sets a high workload profile.
ℹ️ Reproduce these yourself. Every hash below lives in
hashcat-examples/(in the course-environment repo) with an answer key and a per-file command table. They are MD5 of known plaintexts, chosen so each attack cracks its own file and nothing else. The transcripts here were run on Kali with Hashcat v7.1.2 against an RTX-class GPU. Your cracked lines may print in a different order — Hashcat reports them as GPU threads finish, not in file order.
Hashcat saves every crack to hashcat.potfile and never re-cracks a known hash. Two commands you will use constantly:
hashcat -m 0 hashes.txt --show # print cracked hash:plain pairs from the potfile
hashcat -m 0 hashes.txt --left # print the hashes still uncracked
It also supports --restore to resume an interrupted session, --status --status-timer=10 for periodic progress lines, and -b to benchmark a hash mode so you know what is feasible before you start. The --quiet flag (used below) suppresses the live status screen and prints only results.
Dictionary (straight) attack — -a 0
The straight attack hashes each line of a wordlist verbatim and compares. It is the first thing you run, because most real passwords are in a big enough leak. dictionary.hash holds three that are in rockyou.txt:
hashcat -m 0 -a 0 -O -w 3 dictionary.hash /usr/share/wordlists/rockyou.txt
5f4dcc3b5aa765d61d8327deb882cf99:password
f25a2fc72690b780b2a14e140ef6a9e0:iloveyou
8621ffdbc5698829397d97767ac13db3:dragon
That is the whole technique: no password structure is assumed, only that the plaintext appears somewhere in the list. Its ceiling is the wordlist — a straight run can never crack a password that is not literally in the file. The two ways past that ceiling are a bigger list (next section’s dynamic expansions) or rules, which stretch the list you have.
Rules: turning one word into many
A rule is a per-word transformation applied on the fly: capitalize, append digits, swap a→@, reverse. One rule turns each dictionary word into one new candidate; a rule file with 100 rules turns a 14-million-word list into 1.4 billion candidates — without writing a bigger file to disk, and at GPU speed. This is the single highest-value technique in password cracking, because it models how humans actually mutate a base word to satisfy a complexity policy.
Rule syntax. Each line of a rule file is one rule, made of single-character functions applied left to right. The ones worth memorizing:
| Function | Meaning | password → |
|---|---|---|
: |
do nothing (pass the word through unchanged) | password |
l / u / c |
lowercase all / uppercase all / capitalize first | password / PASSWORD / Password |
t / T3 |
toggle all case / toggle case at position 3 | PASSWORD / pasSword |
r |
reverse | drowssap |
d / f |
duplicate / reflect (append reversed) | passwordpassword / passworddrowssap |
$X |
append character X |
$1 → password1 |
^X |
prepend character X |
^! → !password |
sXY |
substitute every X with Y |
ss$ → pa$$word |
sa@ so0 |
leetspeak substitutions (chain them) | sa@ so0 → p@ssw0rd |
[ / ] |
delete first / last character | assword / passwor |
Rules combine on one line: c$2$0$2$6 means capitalize, then append 2026 → Password2026. Whitespace separates nothing — each line is a complete, independent rule.
Capability and cost. A rule file multiplies keyspace by its line count, so it multiplies runtime too. Ship-with-Hashcat rule files, smallest to largest:
best66.rule(~66 rules) — the tuned everyday default; high yield per rule.rockyou-30000.rule(~30k) — derived from rockyou itself; a strong middle ground.dive.rule(~99k) — exhaustive and slow; a last-resort thorough pass.
⚠️
best64.rulewas renamedbest66.rulein Hashcat 7.0 (it dropped an ambiguous two-rule section). On Kali’s current Hashcat 7.x the path is/usr/share/hashcat/rules/best66.rule; the oldbest64.rulename only exists on 6.x and earlier. Blog posts and older labs still saybest64everywhere.
Worked example. rules.hash contains Pilots, portland99, and HAWTHORNE — none of which are in rockyou.txt. But their base words are in corp.txt, a five-line targeted list (the kind cewl scrapes from an organization’s website):
cat corp.txt
portland
vikings
pilots
willamette
hawthorne
The straight attack with that list alone cracks nothing — the stored passwords are capitalized or carry appended digits, and none appear verbatim:
hashcat -m 0 -a 0 -O -w 3 rules.hash corp.txt
(no cracks — exhausted, 0/3 recovered)
Add best66.rule and the same five words expand into the capitalized, uppercased, and digit-appended variants that match:
hashcat -m 0 -a 0 -O -w 3 rules.hash corp.txt -r /usr/share/hashcat/rules/best66.rule
b69b61f776eebcb31934e3a1ec761c01:portland99
0811a9378f6a25444779180cca216277:Pilots
f912f69d9021fb59e89477c0dc98aa8a:HAWTHORNE
Five words became all three cracks. Notice the honest limitation on display: for common words, rockyou already contains the mangled forms (Monkey1, yeknom, and friends are all in there), so rules add little on top of a giant leak. Rules earn their keep on targeted, uncommon base words — company names, product names, local sports teams — exactly the material a straight rockyou run misses. Stack rule files to multiply further (the keyspaces multiply too):
hashcat -m 0 -a 0 corp.txt -r rules/best66.rule -r rules/toggles1.rule rules.hash
Combination attack — -a 1
The combinator concatenates every word of a left list with every word of a right list — list1 × list2. It targets passwords built from two real words (redvikings, gopilots), which are common, defeat a single-word dictionary, and are far too irregular for a mask. combination.hash holds three such pairings absent from rockyou; left.txt is {go, red, portland} and right.txt is {vikings, pilots, ducks}:
hashcat -m 0 -a 1 -O -w 3 combination.hash left.txt right.txt
d81304d292822855b937f82ffcde254a:gopilots
541e927106be6983041efc0544e0a43d:redvikings
fce0098666d9465c6fff67c94e6e982b:portlandducks
Three left words × three right words is only nine candidates here, but with two copies of rockyou the combinator generates 14M × 14M ≈ 2×10¹⁴ candidates — so you feed it small, curated lists, not two giant ones. You can also apply a rule to each side as it is joined with -j (left) and -k (right), e.g. -j '$ ' to insert a space between the two words.
Mask attack — -a 3
A mask attack is targeted brute force: instead of the entire keyspace, you enumerate only strings matching a known structure. Each position is a placeholder drawn from a charset:
| Token | Charset | Size |
|---|---|---|
?l |
a–z |
26 |
?u |
A–Z |
26 |
?d |
0–9 |
10 |
?s |
!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~ and space |
33 |
?a |
?l?u?d?s (everything above) |
95 |
?b |
0x00–0xff |
256 |
mask.hash holds Summer26! and Winter99!, both matching uppercase, five lowercase, two digits, one symbol:
hashcat -m 0 -a 3 -O -w 3 mask.hash '?u?l?l?l?l?l?d?d?s'
The full status screen (shown here once; --quiet hides it) is worth reading in full — it reports speed, total keyspace, and progress:
Session..........: hashcat
Status...........: Cracked
Hash.Mode........: 0 (MD5)
Guess.Mask.......: ?u?l?l?l?l?l?d?d?s [9]
Speed.#01........: 17292.4 MH/s (95.18ms) @ Accel:128 Loops:1024 Thr:512 Vec:1
Recovered........: 2/2 (100.00%) Digests (total), 2/2 (100.00%) Digests (new)
Progress.........: 70312394752/1019422060800 (6.90%)
Candidates.#01...: Nabxrr57* -> Qhljhr49!
The Progress denominator is the whole point: this mask has 26 × 26⁵ × 10² × 33 = 1,019,422,060,800 candidates (~1 trillion), and at 17.3 GH/s the GPU exhausts it in about a minute — it cracked both here at 6.9% in 4 seconds. Widen the mask and the cost explodes: replacing the four fixed-type positions with ?a (all-95) would multiply the keyspace by orders of magnitude. Masks only stay feasible because each ?d/?l is far smaller than ?a.
Custom charsets (-1 through -4) let you define your own placeholder. To model “letter or digit” in each position:
# -1 defines charset 1 = lowercase + digits; ?1 then references it
hashcat -m 0 -a 3 -1 '?l?d' hashes.txt '?1?1?1?1?1?1'
--increment sweeps mask lengths rather than fixing one, e.g. try all 4-to-8-character digit PINs in one run:
hashcat -m 0 -a 3 --increment --increment-min 4 --increment-max 8 hashes.txt '?d?d?d?d?d?d?d?d'
Use --keyspace to print a mask’s candidate count before committing to a run, so you can estimate wall-clock time from your benchmark.
Hybrid attack — -a 6 and -a 7
Hybrid glues a wordlist and a mask together — the single most realistic model of how people build passwords: a memorable word plus a required digit/symbol tail (or a prefix). It is a combinator where one side is a dictionary and the other is a generated mask.
-a 6= wordlist + mask (mask appended).hybrid-append.hashholdspassword2026anddragon1988— a rockyou word with four trailing digits:
hashcat -m 0 -a 6 -O -w 3 hybrid-append.hash /usr/share/wordlists/rockyou.txt '?d?d?d?d'
a52c0791010f798018442d6fc49c3446:dragon1988
7bd2c0a350219e670803d270e85439b0:password2026
-a 7= mask + wordlist (mask prepended).hybrid-prepend.hashholds2026summerand99dragon— digits then a word:
hashcat -m 0 -a 7 -O -w 3 hybrid-prepend.hash '?d?d' /usr/share/wordlists/rockyou.txt
3ee9d5803d8c027e8878cbc1a881f51e:99dragon
2d927547725f4b68d40b432d83f46c25:2026summer
Hybrid cost is wordlist_size × mask_keyspace, so keep the mask short: rockyou (14M) × ?d?d?d?d (10⁴) is 1.4×10¹¹ candidates — a few seconds on a GPU — but rockyou × ?a?a?a?a (95⁴ ≈ 8×10⁷) is 1.1×10¹⁵, which is a different afternoon. Hybrid overlaps with rules ($2$0$2$6 appends a fixed 2026; -a 6 … '?d?d?d?d' appends any four digits), so reach for hybrid when the tail varies and for rules when it follows a pattern.
Wordlists, rules, and expansions
Candidate generation is where cracking is won or lost. Three layers, in increasing power:
1. Wordlists (dictionaries)
The base material. rockyou.txt (14M leaked passwords) ships with Kali at /usr/share/wordlists/rockyou.txt.gz — gunzip it once. For real work, build bigger/targeted lists:
- SecLists — the standard curated collection (
/usr/share/seclists/Passwords/). - Weakpass / crackstation — multi-gigabyte real-world dumps.
- Targeted lists — generate from the victim’s own site with
cewl(scrapes a URL into a wordlist) or from known facts withcrunch.
cewl -d 2 -m 6 -w company.txt https://example.com # words from the org's website
2. Rules (mangling)
The highest-value multiplier — covered in depth above with syntax and a worked example. The key point for the layered view: rules stretch whatever wordlist you already have, so they compose with every list in this section. John speaks the same idea with named rule sets defined in john.conf:
# John uses named rule sets rather than standalone .rule files
john --wordlist=rockyou.txt --rules=Jumbo hashes.txt # broad default
john --wordlist=rockyou.txt --rules=KoreLogic hashes.txt # aggressive policy-style rules
The best66, d3ad0ne, and KoreLogic rule sets exist in both tools’ formats — rules are largely portable between Hashcat and John.
3. Dynamic expansions: PRINCE and Purple Rain
When a wordlist + finite rules are exhausted, you move to generators that produce a practically infinite, non-deterministic candidate stream.
PRINCE (PRobability INfinite Chained Elements, princeprocessor / pp64.bin) takes a wordlist and chains its words into longer candidates automatically — turning red, dragon, 99 into reddragon, dragonred99, and so on, ordered by likelihood. It is not in Kali’s default install — grab the prebuilt binary from GitHub releases:
# Download + unpack the latest princeprocessor release (v0.22 as of 2026)
cd /opt && sudo curl -fsSLO https://github.com/hashcat/princeprocessor/releases/download/v0.22/princeprocessor-0.22.7z
sudo apt install -y p7zip-full && sudo 7z x princeprocessor-0.22.7z
sudo ln -sf /opt/princeprocessor-0.22/pp64.bin /usr/local/bin/pp64.bin # put it on PATH
pp64.bin --version
Feed its stream straight into Hashcat:
pp64.bin --pw-min=8 rockyou.txt | hashcat -m 0 -a 0 -w 4 hashes.txt
The Purple Rain attack (Netmux) combines three sources of randomness so the attack never stops finding new candidates: shuffle the dictionary, chain it through PRINCE, and have Hashcat generate a fresh batch of random rules on top via -g:
# shuf -> randomize the dictionary order
# pp64 -> PRINCE-chain words into candidates of length >= 8
# -g N -> Hashcat generates N RANDOM mangling rules on the fly and applies them
shuf dict.txt | pp64.bin --pw-min=8 | hashcat -a 0 -m <type> -w 4 -O hashes.txt -g 300000
The -g 300000 tells Hashcat to invent 300,000 random rules for this run (you can push toward 1,000,000 — but the larger the random ruleset, the longer Hashcat takes to start). Because all three stages are randomized, the candidate stream is non-deterministic: every run explores a different slice of the keyspace.
ℹ️ Purple Rain’s selling point is unattended, long-tail cracking. After your curated wordlists and named rules have done their work, you fire this off and let it run for hours or days on hashes nothing else cracked. As Netmux puts it: “non-deterministic output is your friend.” It is a last-resort technique, not a first move — a good
rockyou + best66run is far more efficient on the easy 80%.
A practical workflow
The two tools are complementary, not competing. The professional pattern:
- Identify the hash (
hashid/hashcat --help). - Extract if it is inside a file (
*2john), and merge shadow files (unshadow). - Triage with John for autodetect,
--singlemode, and exotic formats it handles natively. - Move fast hashes to Hashcat on the GPU for the heavy wordlist + rule passes.
- Escalate: wordlist → wordlist+rules → hybrid/mask → PRINCE → Purple Rain.
- Report what cracked and, crucially, why it was crackable — that is the defensive payoff: enforce a slow KDF (bcrypt/Argon2), unique salts, length-based policy, and MFA.
Key takeaways
- Cracking is guess-and-check against a one-way hash; the defender’s algorithm speed, salt, and work factor decide how hard it is — a slow KDF (bcrypt/Argon2) is the real defense, not secrecy.
- Always identify the hash first and let the
-mmode (Hashcat) or autodetect (John) drive everything else. - John the Ripper is the batteries-included CPU tool: autodetect,
--singlemode, and the indispensable*2johnconverters (zip2john,ssh2john,office2john,keepass2john, …) plusunshadow. - Hashcat is the GPU workhorse, and each attack is just a different candidate generator: straight (
-a 0) hashes a wordlist verbatim; rules (-r) mangle each word into many; combinator (-a 1) joins two lists; mask (-a 3) brute-forces a known structure; hybrid (-a 6/-a 7) bolts a mask onto a wordlist. Match the generator to what you know about the password. - Wordlists < wordlists+rules < dynamic expansion. Rules are the highest-value technique; PRINCE chains words; Purple Rain (
shuf | pp64 | hashcat -g) is the non-deterministic, run-it-unattended last resort. - The output of an engagement is not the cracked passwords — it is the policy fix that makes them uncrackable next time.
References
- Hashcat — official site, wiki, and example-hash/mode reference — https://hashcat.net/hashcat/
- Hashcat example hashes (the canonical
-mmode list) — https://hashcat.net/wiki/doku.php?id=example_hashes - Hashcat rule-based attack reference (the full function list) — https://hashcat.net/wiki/doku.php?id=rule_based_attack
- Hashcat mask-attack reference — https://hashcat.net/wiki/doku.php?id=mask_attack
- Hashcat combinator- and hybrid-attack reference — https://hashcat.net/wiki/doku.php?id=combinator_attack
best66.rule(formerlybest64.rule) in the Hashcat source tree — https://github.com/hashcat/hashcat/blob/master/rules/best66.rule- John the Ripper (jumbo) source and docs — https://github.com/openwall/john
- John the Ripper community wiki — https://openwall.info/wiki/john
- PRINCEprocessor (PRINCE generator) — https://github.com/hashcat/princeprocessor
- Netmux, “Purple Rain Attack” — https://www.netmux.com/blog/purple-rain-attack
- SecLists wordlist collection — https://github.com/danielmiessler/SecLists
Related course pages: Cryptography · Identity and Access Management · Cracking WiFi · SSH · Host Security and the Attack Lifecycle
🛠️ Maintenance note: the worked-example transcripts were captured with Hashcat v7.1.2 against an RTX-class GPU (speeds and timings will differ on other hardware) and John 1.9.0-jumbo. Two version-sensitive facts on this page:
best64.rulewas renamedbest66.rulein Hashcat 7.0 — thebest64name only exists on 6.x, and most online material still uses it; and WPA cracking moved to mode22000(was2500/16800). Mode numbers and paths (/usr/share/hashcat/rules/,/usr/share/wordlists/) are otherwise stable on Kali but confirm against the course VM. KDF guidance drifts: OWASP/NIST periodically raise recommended bcrypt cost and Argon2 parameters — keep the Cryptography page’s KDF section in sync. The reproducible hash sets live inhashcat-examples/in theintrosec-envrepo (the HW3 challenge files are inhw3-files/there too); regenerate finding counts if you swap the wordlist.