John the Ripper Tutorial: Hash Formats, Wordlists, Rules, and Cracking Modes

Deepak Prasad
Tested on Kali GNU/Linux Rolling 2026.2 (kali-rolling)
Package john 1.9.0-Jumbo-1+git20211102-0kali10
john-data 1.9.0-Jumbo-1+git20211102-0kali10
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 Input pipeline for John the Ripper Jumbo on Kali: prepare hashes, identify formats, wordlist and rules, single mask and incremental modes, zip2john ssh2john pdf2john, pot file and troubleshooting. Does not cover GPU hashcat workflows, live login brute force, or cracking third-party dumps without authorization.
Short on time?Beta Get the gist of this 14-minute read in seconds, then ask follow-up questions

John the Ripper recovers plaintext from password hashes, and from files that first need a *2john converter. Kali ships the Jumbo build, which is the one with extra formats and helpers such as zip2john.

Many John errors come from malformed or incorrectly identified input rather than the cracking command itself. I start from that input, then run the modes on hashes I generated under /tmp/john-lab.

IMPORTANT
Crack only hashes and archives you created in a lab or that come from systems you own or are authorized to test in your rules of engagement. Do not run John against production /etc/shadow, customer dumps, or random pastebin files.

John the Ripper quick start

text
┌─────────────────────────────────────┐
│  Collect hash or protected file     │
└──────────────────┬──────────────────┘
                   ↓
┌─────────────────────────────────────┐
│  Convert with *2john if needed      │
└──────────────────┬──────────────────┘
                   ↓
┌─────────────────────────────────────┐
│  Identify or force --format         │
└──────────────────┬──────────────────┘
                   ↓
┌─────────────────────────────────────┐
│  Choose mode from what you know:    │
│  account metadata → single          │
│  likely dictionary words → wordlist │
│  / rules                            │
│  known pattern → mask               │
│  little useful information →        │
│  incremental (usually last)         │
└──────────────────┬──────────────────┘
                   ↓
┌─────────────────────────────────────┐
│  john --show  /  john.pot           │
└─────────────────────────────────────┘

How John the Ripper works

John does not reverse a hash. It hashes candidate passwords with the same algorithm and salt, then compares the result to the stored value. That is offline work. Guessing against a live SSH or web login is a different problem; see SSH brute force for that.

text
hash or file → prepare (*2john) → format → mode → candidates → compare → pot / --show
Stage What you do
Input Raw hash, user:hash, Unix shadow line, or encrypted file
Prepare Run zip2john, ssh2john, pdf2john, or unshadow when the file is not already John-ready
Format Let John auto-detect, or force --format= when the string is ambiguous
Mode Single, wordlist, rules, mask, or incremental
Result Match lands in the pot file; john --show prints plaintext

Core Openwall John is a smaller binary. Kali ships Jumbo, with hundreds of formats and the *2john helpers. Check that you are on Jumbo before you spend time hunting a converter that core John never had.


Verify the Jumbo build and formats

Confirm the packages:

bash
dpkg-query -W john john-data
output
john	1.9.0-Jumbo-1+git20211102-0kali10
john-data	1.9.0-Jumbo-1+git20211102-0kali10

Both packages are Jumbo. Print the build line:

bash
john --list=build-info 2>&1 | head -8
output
Version: 1.9.0-jumbo-1+bleeding-aec1328d6c 2021-11-02 10:45:52 +0100
Build: linux-gnu 64-bit x86_64 AVX2 AC OMP
SIMD: AVX2, interleaving: MD4:3 MD5:3 SHA1:1 SHA256:1 SHA512:1
System-wide exec: /usr/lib/john
System-wide home: /usr/share/john
Private home: ~/.john
CPU tests: AVX2
CPU fallback binary: john-xop-omp

On this Kali build, john --list=build-info reports Private home: ~/.john, so the default pot is ~/.john/john.pot. Other builds and layouts can differ.

List formats (truncated; this Jumbo build reports about 300):

bash
john --list=formats 2>&1 | head -6
output
descrypt, bsdicrypt, md5crypt, md5crypt-long, bcrypt, scrypt, LM, AFS, 
tripcode, AndroidBackup, adxcrypt, agilekeychain, aix-ssha1, aix-ssha256, 
aix-ssha512, andOTP, ansible, argon2, as400-des, as400-ssha1, asa-md5, 
AxCrypt, AzureAD, BestCrypt, BestCryptVE4, bfegg, Bitcoin, BitLocker, 
bitshares, Bitwarden, BKS, Blackberry-ES10, WoWSRP, Blockchain, chap, 
Clipperz, cloudkeychain, dynamic_n, cq, CRC32, cryptoSafe, sha1crypt, 

You do not need to memorize that list. If a load fails, grep it for the name you expect: sha512crypt, Raw-MD5, PKZIP, SSH, PDF.


Prepare the hash correctly

Create a lab directory and a short wordlist so each mode finishes in a few seconds:

bash
LAB=/tmp/john-lab
mkdir -p "$LAB"
printf 'password\nroot\nsecret123\nadmin\nsummer\n' > "$LAB/wordlist.txt"

John accepts several shapes:

Input shape Example Notes
username:hash debianlab:$6$labsalt$… Best for Unix crypt; shows the account in --show
Raw hash only 5d7845ac6ee7cfffafc5fe5f35cf666d Often needs --format= (ambiguous length)
unshadow line From unshadow passwd shadow Needed for single mode with GECOS fields
*2john line $pkzip$…, $sshng$…, $pdf$… Converter output, not the original file

Pasting a random hex blob from a blog often fails because the salt, prefix, or file format is missing. For learning, generate the hashes yourself.

Build a SHA-512 crypt line that John can load without guessing:

bash
HASH=$(openssl passwd -6 -salt labsalt secret123)
printf 'debianlab:%s\n' "$HASH" > "$LAB/hashes.txt"

The file should look like:

output
debianlab:$6$labsalt$dBjQ56YjzkWY27Iq38NPmtW0AEEmCEe9WNVC8uk1nzd0PjQpoAL8BNCsDTEfmZizepT1JJcGa50QmDVR3DpVu1

$6$ means SHA-512 crypt. That prefix is why this line auto-detects cleanly.


Identify or force the hash format

John auto-detects many formats. Treat that guess as a hint. A 32-character hex string can be LM, NT, Raw-MD5, or several other types.

Generate a raw MD5 of secret123 and load it without --format:

bash
printf '%s\n' "$(printf 'secret123' | md5sum | awk '{print $1}')" > "$LAB/rawmd5.txt"
john --wordlist="$LAB/wordlist.txt" "$LAB/rawmd5.txt" 2>&1 | head -8
output
Warning: detected hash type "LM", but the string is also recognized as "dynamic=md5($p)"
Use the "--format=dynamic=md5($p)" option to force loading these as that type instead
Warning: detected hash type "LM", but the string is also recognized as "HAVAL-128-4"
Use the "--format=HAVAL-128-4" option to force loading these as that type instead
Warning: detected hash type "LM", but the string is also recognized as "MD2"
Use the "--format=MD2" option to force loading these as that type instead

John guessed LM first. For this lab hash, force Raw-MD5:

bash
john --format=raw-md5 --wordlist="$LAB/wordlist.txt" "$LAB/rawmd5.txt"
output
Loaded 1 password hash (Raw-MD5 [MD5 256/256 AVX2 8x3])
secret123        (?)     
1g 0:00:00:00 DONE (2026-09-03 12:45) 25.00g/s 125.0p/s 125.0c/s 125.0C/s password..summer

Loaded 1 password hash (Raw-MD5 …) is the format John actually used. john --show=formats file prints a JSON list of labels it considered for each line.

A wrong --format often fails cleanly:

bash
john --format=nt --wordlist="$LAB/wordlist.txt" "$LAB/hashes.txt"
output
No password hashes loaded (see FAQ)

The shadow line is sha512crypt, not NT. Change --format, or omit it when the $6$ prefix already names the type.


Choose a cracking mode

Pick the mode from what you know about the password. Openwall recommends single mode first when account metadata is available, and its default batch path moves through single, wordlist, then incremental. Mask mode is Jumbo-specific, and whether it belongs before or after rules depends on how much structure you already know.

What you know Start with
Username or GECOS fields (unshadow) Single
Likely dictionary words Wordlist, then rules
Known pattern (secret plus three digits) Mask
Little useful information Incremental, usually last

An unrestricted incremental run against a long SHA-512 crypt password is rarely the right first move. Prefer targeted wordlists, rules, or a constrained mask when you have useful password information.

Mode Flag Best when
Single --single Password may match username or GECOS
Wordlist --wordlist=FILE Password may be in a dictionary
Rules --rules / --rules=NAME Variants of dictionary words
Mask --mask=MASK Known structure (?d digits, ?l letters)
Incremental --incremental[=MODE] Short charset search after smarter modes fail

Crack hashes with a wordlist

Wordlist mode hashes each line from your list and compares it to the stored hash. Attack the SHA-512 crypt file from earlier:

bash
john --wordlist="$LAB/wordlist.txt" "$LAB/hashes.txt"
output
Loaded 1 password hash (sha512crypt, crypt(3) $6$ [SHA512 256/256 AVX2 4x])
Cost 1 (iteration count) is 5000 for all loaded hashes
secret123        (debianlab)     
1g 0:00:00:00 DONE (2026-09-03 12:45) 14.28g/s 71.42p/s 71.42c/s 71.42C/s password..summer

secret123 (debianlab) is the recovered password. Print it again with --show:

bash
john --show "$LAB/hashes.txt"
output
debianlab:secret123

1 password hash cracked, 0 left

For larger runs on Kali, write RockYou into the lab directory so the system copy stays compressed:

bash
gzip -dc /usr/share/wordlists/rockyou.txt.gz > "$LAB/rockyou.txt"
john --wordlist="$LAB/rockyou.txt" "$LAB/hashes.txt"

Do not pass John the .gz path. If you know how the org names accounts, a custom list from a wordlist generator is usually a better start than the full RockYou file.


Use word mangling rules

Rules take each wordlist entry and change it: case, trailing digits, leetspeak, and similar. From summer you get Summer, SUMMER, summer1, and so on without typing every variant.

First, show that a capitalized password is missing from the plain list. Hash Secret123 while the wordlist only has lowercase secret123:

bash
RULES_HASH=$(openssl passwd -6 -salt rules1 Secret123)
printf 'ruleslab:%s\n' "$RULES_HASH" > "$LAB/rules.txt"
john --wordlist="$LAB/wordlist.txt" "$LAB/rules.txt"
output
0g 0:00:00:00 DONE (2026-09-03 12:46) 0g/s 33.33p/s 33.33c/s 33.33C/s secret123..admin

Nothing recovered. Add the built-in Single ruleset:

bash
john --wordlist="$LAB/wordlist.txt" --rules=Single "$LAB/rules.txt"

--rules=Single does not enable Single Crack mode; it applies the ruleset named Single to the supplied wordlist. Single Crack mode itself uses --single.

output
Loaded 1 password hash (sha512crypt, crypt(3) $6$ [SHA512 256/256 AVX2 4x])
Secret123        (ruleslab)     
1g 0:00:00:01 DONE (2026-09-03 12:46) 0.7518g/s 192.4p/s 192.4c/s 192.4C/s secret123..admin.

A rule that only appends 1 is enough to show the idea. Hash summer1, then use an immediate rule:

bash
SUMMER1=$(openssl passwd -6 -salt r2 summer1)
printf 'ruleuser:%s\n' "$SUMMER1" > "$LAB/customrule.txt"
john --wordlist="$LAB/wordlist.txt" --rules=':$1' "$LAB/customrule.txt"
output
summer1          (ruleuser)     
1g 0:00:00:00 DONE (2026-09-03 12:46) 20.00g/s 60.00p/s 60.00c/s 60.00C/s secret1231..admin1

The leading : tells Jumbo that the rest of the argument is an immediate command-line rule rather than a named ruleset; $1 appends the character 1. The full rule language is in the Openwall RULES docs. For beginner use, start with a named ruleset or a short immediate rule before moving into the full rule language.


Use single mode when account metadata exists

Single mode builds candidates from usernames and GECOS fields. Create a throwaway passwd and shadow pair where the password is the username:

bash
printf 'johndoe:x:10001:10001:johndoe:/home/johndoe:/bin/bash\n' > "$LAB/passwd"
SINGLE_HASH=$(openssl passwd -6 -salt single1 johndoe)
printf 'johndoe:%s\n' "$SINGLE_HASH" > "$LAB/shadow"

Combine them with unshadow:

bash
unshadow "$LAB/passwd" "$LAB/shadow" > "$LAB/unshadowed.txt"

Run single mode on the combined file:

bash
john --single "$LAB/unshadowed.txt"
output
Loaded 1 password hash (sha512crypt, crypt(3) $6$ [SHA512 256/256 AVX2 4x])
johndoe          (johndoe)     
1g 0:00:00:00 DONE (2026-09-03 12:46) 10.00g/s 40.00p/s 40.00c/s 40.00C/s johndoe..johnd

John recovered johndoe because the password matched the username. On a real job this only applies after you lawfully have passwd and shadow data.


Mask mode versus incremental mode

Mask mode fills a known pattern. Digits use ?d, lowercase ?l, uppercase ?u. Crack the earlier raw MD5 when you already know it looks like secret plus three digits:

bash
john --format=raw-md5 --mask='secret?d?d?d' "$LAB/rawmd5.txt"
output
Loaded 1 password hash (Raw-MD5 [MD5 256/256 AVX2 8x3])
secret123        (?)     
1g 0:00:00:00 DONE (2026-09-03 12:46) 33.33g/s 12800p/s 12800c/s 12800C/s secret111..secret363

Incremental mode walks a charset in a statistical order. It is not the automatic next step after a wordlist miss. Use it on short or weak hashes. An MD5 crypt hash of the single character a finished in tens of seconds here:

bash
INC_HASH=$(openssl passwd -1 -salt in1 a)
printf 'shortlab:%s\n' "$INC_HASH" > "$LAB/incremental.txt"
john --incremental=ASCII "$LAB/incremental.txt"
output
Loaded 1 password hash (md5crypt, crypt(3) $1$ (and variants) [MD5 256/256 AVX2 8x3])
a                (shortlab)     
1g 0:00:00:23 DONE (2026-09-03 12:46) 0.04244g/s 6454p/s 6454c/s 6454C/s jhunds..lim

An unrestricted incremental run against a long SHA-512 crypt password may be computationally impractical and can run far longer than hours. Prefer targeted wordlists, rules, or a constrained mask when you have useful password information.


Crack ZIP, SSH, and PDF with *2john

A ZIP, SSH key, or PDF is not a shadow line. Convert it first, then crack.

text
file → *2john → john hash file → mode → --show

ZIP with zip2john

Create a password-protected ZIP in the lab. -P is used here only to create a disposable lab archive because it places the password on the command line:

bash
printf 'hosts sample\n' > "$LAB/sample.txt"
zip -P secret123 -j "$LAB/protected.zip" "$LAB/sample.txt"

Extract a John line with zip2john:

bash
zip2john "$LAB/protected.zip" > "$LAB/zip.hashes"

Crack that line with the lab wordlist:

bash
john --wordlist="$LAB/wordlist.txt" "$LAB/zip.hashes"
output
Loaded 1 password hash (PKZIP [32/64])
secret123        (protected.zip/sample.txt)     
1g 0:00:00:00 DONE (2026-09-03 12:47) 20.00g/s 100.0p/s 100.0c/s 100.0C/s password..summer

SSH private key with ssh2john

Generate a throwaway key with a known passphrase, then convert it:

bash
ssh-keygen -t ed25519 -f "$LAB/labkey" -N "secret123" -q
ssh2john "$LAB/labkey" > "$LAB/ssh.hashes"

Run John on the extracted hash:

bash
john --wordlist="$LAB/wordlist.txt" "$LAB/ssh.hashes"
output
Loaded 1 password hash (SSH, SSH private key [RSA/DSA/EC/OPENSSH 32/64])
secret123        (/tmp/john-lab/labkey)     
1g 0:00:00:03 DONE (2026-09-03 12:47) 0.3267g/s 1.633p/s 1.633c/s 1.633C/s password..summer

Delete the throwaway key when you finish.

PDF with pdf2john

On Kali, pdf2john is a symlink to pdf2john.pl. I encrypted a PDF with qpdf, then converted it:

bash
qpdf --encrypt secret123 secret123 256 -- "$LAB/plain.pdf" "$LAB/secret.pdf"
pdf2john "$LAB/secret.pdf" > "$LAB/pdf.hashes"

Crack the PDF hash with the same wordlist:

bash
john --wordlist="$LAB/wordlist.txt" "$LAB/pdf.hashes"
output
Loaded 1 password hash (PDF [MD5 SHA2 RC4/AES 32/64])
secret123        (/tmp/john-lab/secret.pdf)     
1g 0:00:00:00 DONE (2026-09-03 12:47) 6.250g/s 31.25p/s 31.25c/s 31.25C/s password..summer

Other Jumbo helpers (rar2john, 7z2john, keepass2john) follow the same pattern. Match the converter to the file type. Run the appropriate converter first; John reads the converter output rather than the original binary file.


Show cracked passwords and the pot file

Cracked plaintext lands in the pot file. On this Kali build, john --list=build-info reports Private home: ~/.john, so the default pot is ~/.john/john.pot. Other builds and layouts can differ. A second run on the same hash can finish immediately:

bash
john --wordlist="$LAB/wordlist.txt" "$LAB/hashes.txt"
output
No password hashes left to crack (see FAQ)

That usually means the pot already holds the result. Print it:

bash
john --show "$LAB/hashes.txt"
output
debianlab:secret123

1 password hash cracked, 0 left

A pot line looks like ciphertext:plaintext. For a clean lab retest, use a new salt or username, or point John at a temporary pot with --pot=/tmp/john-lab/lab.pot.


John the Ripper versus hashcat

John the Ripper hashcat
Strength Convenient *2john converters, Single mode, mixed CPU workflows GPU acceleration and optimized high-throughput attacks
Best fit Mixed file formats, quick audits on a laptop High-volume offline cracking when a GPU is available
Overlap Both crack many of the same hash types Use both; pick by hardware and input type

Pick by hardware and input type. For GPU-focused work, continue with crack hashes with hashcat.


Troubleshooting John the Ripper

Symptom Likely cause What to check
No password hashes loaded Malformed line, wrong format, unsupported type Confirm user:hash or converter output; try --format=; see FAQ
Autodetect picks LM / NT for hex MD5 Ambiguous 32-hex string Force --format=raw-md5 (or the real type)
Wrong --format Format name does not match the ciphertext john --list=formats; john --show=formats file
Session ends with 0g and no new crack Password not in list, or already in pot Expand wordlist / add rules; run john --show
No password hashes left to crack Hash already in pot --show; new salt for a clean demo; or --pot= temp file
*2john output will not load Wrong converter or unencrypted file Recreate protected file; use zip2john / ssh2john / pdf2john that matches
RockYou path empty Still gzipped Write an uncompressed copy under $LAB with gzip -dc before --wordlist=
Incremental never finishes Slow KDF / long password Prefer wordlist + rules; check john --status

Summary

John Jumbo on Kali hashes candidates and compares them to a prepared line. I checked the Jumbo build, built lab hashes under /tmp/john-lab, and forced --format=raw-md5 after auto-detect picked LM for a 32-character hex string. Wordlist mode recovered secret123 from sha512crypt. Rules found Secret123 and summer1. Single mode recovered a username match. A mask cracked secret123 when the pattern was known. Incremental recovered a one-character md5crypt hash in 23 seconds.

ZIP, SSH, and PDF used the same path: converter, then John, then --show. Recovered plaintext stays in the pot file, so a second run can say nothing is left. Prefer single, wordlist, rules, or a constrained mask when you know something about the password; leave unrestricted incremental for short or weak hashes. Use hashcat when you have a GPU and a large candidate set.


References


Frequently Asked Questions

Why does John say no password hashes loaded?

The input line is not valid for the selected format. Typical causes: missing user: prefix, wrong --format, ciphertext John does not support, extra spaces, or a file that still needs a *2john converter.

How do I identify the hash format for John?

Use prefixes such as $6$ for sha512crypt. For raw hex, length is not enough. Check john --list=formats, john --show=formats file, and force --format= when auto-detect warns about several matches.

Where does John store cracked passwords?

In the pot file. On this Kali Jumbo build that is usually ~/.john/john.pot, matching Private home: ~/.john from john --list=build-info. Use john --show on the same hash file to print recovered plaintext. Override the path with --pot= when you want an isolated lab pot.

What is the difference between wordlist rules and mask mode?

Rules change dictionary words (case, digits, substitutions). Mask mode fills a pattern such as secret?d?d?d. Use rules when you have a base word. Use a mask when you know the shape.

How do I crack a ZIP or SSH key with John?

Convert first with zip2john or ssh2john, save the line to a file, then run John on that file. Finish with john --show.

Should I use John or hashcat?

John is convenient for *2john converters, Single mode, and mixed CPU lab work. hashcat is stronger when you have a GPU and a large candidate set. Many labs keep both.

Finished reading?Beta Get the key takeaways in seconds, then ask follow-up questions
Kennedy Muthii

Information Security Analyst

Accomplished professional proficient in Python, ethical hacking, Linux, cybersecurity, and OSINT. With a track record including winning a national cybersecurity contest, launching a startup in Kenya, and holding a degree in information science, he is currently engaged in cutting-edge research in ethical hacking.

  • Python (programming language)
  • Certified Ethical Hacker
  • White Hat (Computer Security)
  • Linux
  • Penetration Testing