0
Hands-on Labs BEGINNER 16 min read Updated

Cracking Password Hashes with Hashcat: A Walkthrough

A beginner walkthrough of Hashcat: generate practice hashes you own, identify the type, pick the right mode, and crack your first hash with a dictionary, rules, and a mask.

founder & security researcher · 9 writeups
Cracking Password Hashes with Hashcat: A Walkthrough

Hashcat is the tool most people reach for the first time they want to turn a password hash back into the password that made it, and this walkthrough takes you through that first crack start to finish. You will make a couple of practice hashes on your own machine, work out what type they are, pick the matching mode, and recover them three different ways. Nothing here touches a login form or a live account. The whole point is that cracking happens offline, on hashes you already hold, on hardware you own.

Read it top to bottom and run every command as you go. By the end you will have cracked an MD5 and an NTLM hash you generated yourself, and you will understand why some hashes fall in seconds while others outlast you.

Scope

Everything below runs against hashes you generate on your own machine, or hashes you are explicitly authorized to test in a lab such as lab.0xpriv.dev or a box on 10.10.10.0/24. Cracking hashes you pulled from someone else's system without written permission is illegal. Generate your own and practice on those.

01Offline cracking versus online guessing, and why Hashcat is silent

Offline cracking is what Hashcat does, and it is a completely different activity from online guessing even though both end with a password. Online guessing means typing candidate passwords at a live login: a form, SSH, an API. The target sees every attempt, counts the failures, locks the account, and writes a log line each time. It is loud and slow, and a few wrong tries can trip an alarm or a lockout.

Offline cracking starts after you already hold the hash. You dumped it from a database you are testing, read it from a SAM file on a lab host at 10.10.10.5, or in this walkthrough you made it yourself. From that point the target is out of the loop entirely. You hash guesses on your own GPU, compare each result to the stolen hash locally, and the real system never receives a single packet. There is no account to lock and no log to write, which is exactly why offline cracking is silent and why defenders cannot see it happening.

online guessing stays at the target; offline cracking leaves it entirely
online guessing Youtry a password Live logintarget system Logs, lockout, alertloud offline cracking Stolen hashfile you hold Your GPUhash + compare No contact with targetsilent

That silence cuts both ways. Cracking leaves no trace on the victim, so defenders must assume any stolen hash will eventually be attacked, and the only sane place to practice is on hashes you made yourself, which is what you do next.

02Getting practice hashes you own

Practice hashes you own are the safe way to learn, and making them takes two short commands. You will create one MD5 and one NTLM hash from passwords you choose, because those two cover the most common beginner cases and they crack fast enough to see results in seconds. Pick weak sample passwords on purpose so the dictionary stage has something to find.

MD5 is a plain unsalted hash of the input bytes. On Linux or Git Bash you can make one with the standard tools that ship with the system.

bash
# make an MD5 of a sample password you choose  # 0xpriv.dev/labs
printf '%s' 'Summer2023' | md5sum | cut -d' ' -f1
printf '%s' 'letmein'    | md5sum | cut -d' ' -f1
two practice MD5 hashes
$ printf '%s' 'Summer2023' | md5sum | cut -d' ' -f1
31c8c0724c0cd1b5c92f00e4a1d7c5a1
$ printf '%s' 'letmein' | md5sum | cut -d' ' -f1
0d107d09f5bbe40cade3de5c71e9e9b7

NTLM is the hash Windows stores for local accounts, and it is just the MD4 of the password encoded as little endian UTF-16. You do not need Windows to make one. A single line of Python produces the exact value Windows would store, so you can practice the Windows case on any machine.

bash
# make an NTLM hash of a sample password (MD4 of the UTF-16LE bytes)
python3 -c "import hashlib; print(hashlib.new('md4','Password1'.encode('utf-16le')).hexdigest())"
one practice NTLM hash
$ python3 -c "import hashlib; print(hashlib.new('md4','Password1'.encode('utf-16le')).hexdigest())"
64f12cddaa88057e06a81b54e73b949b

Save each hash on its own line in a plain text file. Keep MD5 and NTLM in separate files for now, because Hashcat cracks one hash type per run and mixing them will only confuse you while you learn.

bash
# one hash per line, grouped by type
cat md5.txt
# 31c8c0724c0cd1b5c92f00e4a1d7c5a1
# 0d107d09f5bbe40cade3de5c71e9e9b7
cat ntlm.txt
# 64f12cddaa88057e06a81b54e73b949b
These are real hashes of weak passwords

The three values above are genuine MD5 and NTLM hashes of Summer2023, letmein, and Password1. Run the commands yourself and you will get the same strings. That is the whole idea of a hash: the same input always produces the same output.

03Identifying the hash type and picking the right -m mode

Identifying the hash type comes before anything else, because Hashcat needs to be told exactly which algorithm a hash came from through its mode number, the -m flag. Give it the wrong mode and it will either refuse the hash or churn forever and find nothing. You already know your two hashes are MD5 and NTLM, but on a real engagement you often do not, so it helps to know how people guess.

Length and shape are the first clue. A 32 character hex string is probably MD5 or NTLM, a 40 character one is likely SHA-1, and a value starting with $2b$ is bcrypt. Tools automate this. Hashcat has a built in guesser, and hashid is a small standalone tool that does the same job.

asking Hashcat to identify a hash
$ hashcat --identify md5.txt
The following hash-mode(s) match the structure of your input hash:

      # | Name                              | Category
  ======+===================================+======================
      0 | MD5                               | Raw Hash
     10 | md5($pass.$salt)                  | Raw Hash salted
    900 | MD4                               | Raw Hash

Notice that a bare 32 character hex string matches several modes, because MD5 and NTLM and plain MD4 all look identical on the wire. That is why context matters: a hash from a Windows SAM is NTLM, a hash from a PHP app is usually MD5 or something stronger. The table below lists the modes you will meet most often. Confirm any mode you are unsure about against the official example hashes page, which shows a sample hash for every single mode Hashcat supports.

Mode -mAlgorithmWhere you see it
0MD5Old web apps, quick checksums
100SHA-1Legacy web apps
1400SHA-256Apps that upgraded past MD5
1000NTLMWindows local and domain accounts
1800sha512crypt $6$Modern Linux /etc/shadow
3200bcrypt $2*$Well built web apps
Verify the mode, do not trust memory

Mode numbers are easy to mix up and the list grows with every release. When you meet a hash you have not cracked before, match its shape against the Hashcat example hashes page before you start a long run. A wrong -m is the most common reason a beginner's crack finds nothing.

04Running a dictionary attack with rockyou

A dictionary attack is the first thing to try on any hash, because most real passwords are words or small variations of words that already sit in a public list. Mode -a 0 is the straight dictionary mode: Hashcat reads a wordlist line by line, hashes each candidate, and compares it to your target. The classic starter wordlist is rockyou.txt, a list of millions of real passwords from an old breach that ships with Kali and is free to download anywhere.

Point Hashcat at your MD5 file with mode 0 and attack mode 0, and hand it the wordlist as the last argument.

bash
# -m 0 = MD5, -a 0 = straight dictionary attack
hashcat -m 0 -a 0 md5.txt rockyou.txt
both MD5 hashes fall almost instantly
$ hashcat -m 0 -a 0 md5.txt rockyou.txt
31c8c0724c0cd1b5c92f00e4a1d7c5a1:Summer2023
0d107d09f5bbe40cade3de5c71e9e9b7:letmein

Session..........: hashcat
Status...........: Cracked
Recovered........: 2/2 (100.00%) Digests

Hashcat prints each cracked hash next to its password, separated by a colon. Your NTLM hash cracks the same way, you just change the mode to 1000 and point it at the other file.

bash
# same attack, NTLM this time
hashcat -m 1000 -a 0 ntlm.txt rockyou.txt

If nothing cracks, the password simply is not in the list. That is not a dead end, it is your cue to make the list work harder, which is the next two sections.

05Adding rules to multiply a wordlist

Rules let one wordlist stand in for hundreds, by transforming each word on the fly before it is hashed. Real people rarely use a bare dictionary word. They capitalize it, stick a year on the end, swap an a for an @. A rule file is a list of these mutations written in Hashcat's own mini language, applied to every candidate so that password also becomes Password, password1, P@ssw0rd, and so on.

You enable rules with -r and a rule file. Hashcat ships several ready made in its rules/ folder. The one everyone starts with is best64.rule, a compact set of the 64 most productive mutations.

bash
# multiply every rockyou word by the best64 ruleset
hashcat -m 0 -a 0 md5.txt rockyou.txt -r rules/best64.rule

Each rule is a short instruction. A few examples show how readable the language is once you see it: c capitalizes, $1 appends a 1, sa@ swaps every a for an @. The official rule based attack page documents every function, and you can write your own file one rule per line.

bash
# a tiny custom rule file, one mutation per line
:            # leave the word unchanged
c            # Capitalize first letter
$1           # append 1
$2 $0 $2 $3  # append 2023
sa@ so0      # a -> @ and o -> 0

Rules are the highest value trick in this whole walkthrough. A medium wordlist plus a good ruleset cracks far more real passwords than a huge raw wordlist alone, because it models how humans actually build passwords out of base words.

06Running a mask attack for a known pattern

A mask attack brute forces a known pattern instead of reading a wordlist, and it is the right tool when you know the shape of a password but not its contents. Mode -a 3 is the mask mode. You describe the password with character set placeholders, and Hashcat tries every combination that fits. This only stays fast while the pattern is short, because the number of guesses explodes as you add positions.

Each placeholder stands for a character set. You can combine the built in ones or define your own.

MaskCharacter set
?llowercase a to z
?uuppercase A to Z
?ddigits 0 to 9
?ssymbols like !@#$
?aall of the above

Say you know a lab account uses one capital, five lowercase, then two digits, a pattern like Summer23. That is ?u?l?l?l?l?l?d?d. Hashcat walks every value of that exact shape.

bash
# one uppercase, five lowercase, two digits: e.g. Summer23
hashcat -m 0 -a 3 md5.txt '?u?l?l?l?l?l?d?d'

# do not know the length? let it grow the mask from short to long
hashcat -m 0 -a 3 --increment md5.txt '?u?l?l?l?l?l?l?l?d?d'
Mask length is the whole story

Adding one ?a position multiplies the work by about ninety five. A seven character all sets mask is already tens of billions of guesses. Use masks when you know the pattern, and lean on dictionaries and rules when you do not.

07Reading the pot file and --show

The pot file is where Hashcat remembers every hash it has ever cracked, so you never lose a result and never redo work. Each time a hash falls, Hashcat writes a hash:password line to a file called hashcat.potfile in its data directory. Run the same hash again later and it will tell you it is already cracked rather than starting over.

To print what you have already recovered, run the same command with --show and no wordlist. Hashcat looks each hash up in the pot file and prints the matches.

recalling results from the pot file
$ hashcat -m 0 md5.txt --show
31c8c0724c0cd1b5c92f00e4a1d7c5a1:Summer2023
0d107d09f5bbe40cade3de5c71e9e9b7:letmein

If your hash file is in username:hash format, which is common when you dump a whole table, add --username so Hashcat ignores the name column, and add --show --username to get the names back next to the cracked passwords. You can also send live results to a file of your own with -o cracked.txt.

bash
# crack a user:hash file, then list who cracked
hashcat -m 1000 -a 0 --username domain.txt rockyou.txt -r rules/best64.rule
hashcat -m 1000 --username domain.txt --show

08Performance and realistic expectations

Performance is the part beginners misjudge most, so it helps to see real numbers before you start a run that would take a century. Hashcat's speed depends on two things: how fast your hardware is, and how slow the hash algorithm is by design. A modern GPU hashes fast algorithms like MD5 and NTLM in the billions per second, but deliberately slow algorithms like bcrypt in only a few thousand per second. Run hashcat -b to benchmark your own card.

benchmark on a single mid range GPU, rounded
$ hashcat -b -m 0
Speed.#1.........:  25000.0 MH/s (MD5)
$ hashcat -b -m 1000
Speed.#1.........:  45000.0 MH/s (NTLM)
$ hashcat -b -m 3200
Speed.#1.........:      15.0 kH/s (bcrypt)

Those numbers decide what is realistic. The table shows roughly how the same guess budget plays out. The lesson is not that you need a bigger GPU, it is that the algorithm chosen by the defender sets the ceiling, and no hardware saves you from a well chosen slow hash.

HashGPU speedWhat that buys you
MD5 / NTLMbillions/secFull 8 char keyspace in hours
sha512crypt~hundreds of thousands/secOnly dictionaries and rules are practical
bcryptthousands/secOnly weak, short, or leaked passwords fall

So your plan on any real hash is the same order you learned here: dictionary first, then rules, then a targeted mask, and accept that a long random password behind a slow hash is simply not coming out. Add -O for the optimized kernel and -w 3 to push the workload when you want more speed on a dedicated box.

09A first crack end to end

A first crack end to end ties the pieces together so you can see the whole rhythm in one place. These are the exact steps you just ran, in the order you will repeat them on every hash from now on.

Generate or collect the hash

Make a practice MD5 and NTLM as above, or work from hashes you are authorized to test. One hash type per file.

Identify the type

Use hashcat --identify or hashid, then confirm the mode against the example hashes page and set -m.

Dictionary first

Run -a 0 with rockyou. Most weak passwords fall here in seconds.

Add rules

Re-run with -r rules/best64.rule to catch capitalized, suffixed, and substituted variants.

Mask for a known pattern

If you know the shape, switch to -a 3 with a mask. Keep it short.

Collect results

Read everything back with --show from the pot file, and write keepers to -o cracked.txt.

every attack is the same loop: make a guess, hash it, compare
Wordlist Wordlist + rules Mask Hash the guesson your GPU Compare to targetmatch = cracked

10Ethics and handling recovered passwords

Handling recovered passwords responsibly is part of the job, not an afterthought, because a cracked password is one of the most sensitive things you can hold during an engagement. The first rule is scope: you crack only hashes you generated or were authorized in writing to test, and you crack them only for the engagement that authorized it. A hash you found interesting on an unrelated system is off limits, full stop.

Once you have cleartext, treat it like live credentials, because it is. Store cracked passwords in an encrypted vault tied to that engagement, never in a plain file that syncs to a cloud drive. Report them to the client in the agreed secure channel, note any password reuse you spot since reuse is often the most useful finding, and then destroy your copies when the engagement closes. Do not reuse a cracked password to log into anything outside the agreed scope, even to prove it works, unless the rules of engagement say you may.

Cracked means compromised

Assume every password you recover is in use somewhere else too. Reporting it clearly and handling it securely is what separates authorized testing from the thing you are supposed to be defending against.

11How to make cracking fail

Making cracking fail is the defender's side of this walkthrough, and a few sound choices move passwords from "falls in seconds" to "not coming out." The attacks you just ran succeed because of two mistakes: passwords that resemble dictionary words, and hashes that are fast and unsalted. Fix both and the same tools come up empty.

Make the hashes not worth cracking
Long, unique, random passwords. A password that is not a word and not reused defeats dictionaries and rules outright, because there is no base word to mutate. A password manager makes this the easy path for users.
Slow, salted hashing. Store passwords with bcrypt, scrypt, or Argon2id, which are designed to be slow and are salted per user. At a few thousand guesses a second instead of billions, even a modest password survives.
Never use fast unsalted hashes. MD5, SHA-1, SHA-256, and NTLM were built for speed, which is exactly wrong for passwords. With no salt, every identical password shares a hash, so one cracked value unlocks every account that reused it, and precomputed tables do the work in advance.
Add a unique salt per user. A salt makes two identical passwords hash differently, which kills rainbow tables and forces an attacker to crack each hash on its own instead of all at once.
Back it with MFA. If a cracked or stuffed password still cannot log in without a second factor, the value of cracking drops sharply.

There is a detection angle too. You cannot see the cracking, but you can watch the steps around it: a bulk read of your user table or a SAM or NTDS dump means hashes just left the building, and a later wave of logins with those passwords shows up as credential stuffing. Alert on the theft and on the reuse, since the crack in the middle happens where you will never see it.

12FAQ

What is the difference between Hashcat and John the Ripper?FAQ

They do the same job, cracking hashes offline, and both are excellent. Hashcat is built around the GPU and is usually faster for raw dictionary and mask work, while John the Ripper has famously good automatic hash detection and handy cracking modes. A common workflow is to let John identify and seed, then let Hashcat grind at speed. Learning either teaches you the other.

Why did my crack find nothing even though the mode was right?FAQ

Almost always because the password is not in your wordlist and does not match any rule you applied. Try a bigger list, add -r rules/best64.rule, then a targeted mask if you know the pattern. If the hash is a slow algorithm like bcrypt, also accept that a strong password simply may not be recoverable.

Where does Hashcat store cracked passwords?FAQ

In a file called hashcat.potfile in its data directory. Every cracked hash is appended as hash:password, so running --show later prints your results without recracking. Point a run at a custom file with --potfile-path if you want to keep engagements separate.

Do I need an expensive GPU to start?FAQ

No. Hashcat runs on a CPU and will happily crack the practice hashes in this walkthrough on any laptop. A GPU matters when you move to large keyspaces or slow hashes, but for learning the method, your current machine is plenty.

Is it legal to run Hashcat?FAQ

The tool itself is legal and dual use, like nmap or curl. What matters is the hashes. Cracking hashes you generated or own, or ones you have written authorization to test, is fine. Cracking hashes taken from systems you do not own or have no permission to assess is a crime, regardless of the tool.

references

Was this writeup useful?
Alex Rowan Founder & Security Researcher at 0xpriv

Founder of 0xpriv, covering offensive security, web application vulnerabilities, and practical security research through PHP, Python, and hands-on technical analysis.

related writeups

more in Labs →

discussion

0 comments
`code` supported · comments are reviewed before they appear