courses

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.txt with 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 $1password1
^X prepend character X ^!!password
sXY substitute every X with Y ss$pa$$word
sa@ so0 leetspeak substitutions (chain them) sa@ so0p@ssw0rd
[ / ] delete first / last character assword / passwor

Rules combine on one line: c$2$0$2$6 means capitalize, then append 2026Password2026. 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:

⚠️ best64.rule was renamed best66.rule in 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 old best64.rule name only exists on 6.x and earlier. Blog posts and older labs still say best64 everywhere.

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.

hashcat -m 0 -a 6 -O -w 3 hybrid-append.hash /usr/share/wordlists/rockyou.txt '?d?d?d?d'
a52c0791010f798018442d6fc49c3446:dragon1988
7bd2c0a350219e670803d270e85439b0:password2026
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.gzgunzip it once. For real work, build bigger/targeted lists:

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 + best66 run is far more efficient on the easy 80%.

A practical workflow

The two tools are complementary, not competing. The professional pattern:

  1. Identify the hash (hashid / hashcat --help).
  2. Extract if it is inside a file (*2john), and merge shadow files (unshadow).
  3. Triage with John for autodetect, --single mode, and exotic formats it handles natively.
  4. Move fast hashes to Hashcat on the GPU for the heavy wordlist + rule passes.
  5. Escalate: wordlist → wordlist+rules → hybrid/mask → PRINCE → Purple Rain.
  6. 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

References


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.rule was renamed best66.rule in Hashcat 7.0 — the best64 name only exists on 6.x, and most online material still uses it; and WPA cracking moved to mode 22000 (was 2500/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 in hashcat-examples/ in the introsec-env repo (the HW3 challenge files are in hw3-files/ there too); regenerate finding counts if you swap the wordlist.