Skip to content
All posts
cryptoJune 30, 20266 min read

Hash cracking that actually works: identify, wordlist, rules, mask

Cracking is a search problem, and most failed attempts are searches aimed at the wrong space. How to identify a hash from its shape, choose between wordlist, rules, and mask attacks, and recognise the hashes you should not be brute-forcing at all.

A hash is a one-way function, so cracking it means guessing the input and checking. That reframing is the whole discipline: you are not breaking a hash, you are searching a space. Everything that follows is about making the space smaller and the checks faster - in that order, because a smaller space beats a faster check every time.

Step one: identify, precisely

Length and alphabet narrow it down; a prefix usually settles it. Modern password hashes advertise themselves in Modular Crypt Format - $id$params$salt$digest - which is the easiest identification you will ever do.

ShapeHashhashcat mode
32 hex charsMD5 - or NTLM, or LM half; ambiguous0 / 1000
40 hex charsSHA-1100
64 hex charsSHA-256 (also SHA3-256, BLAKE2s)1400
128 hex charsSHA-5121700
$1$…md5crypt500
$5$… / $6$…sha256crypt / sha512crypt7400 / 1800
$2a$ $2b$ $2y$bcrypt3200
$argon2id$…Argon2id34000
pbkdf2_sha256$…Django PBKDF210000
$y$ / $7$yescrypt / scryptvaries
Three base64url segmentsJWT - crack the HMAC secret, not a password16500
Ambiguity between MD5 and NTLM is resolved by context, not by the string: NTLM comes out of Windows credential dumps, MD5 out of everything else.

Precision matters because the mode number selects the algorithm *and* the salt placement. md5($pass.$salt) is mode 10 and md5($salt.$pass) is mode 20; picking the wrong one means every candidate is checked against the wrong construction and you will crack nothing while the tool reports perfectly healthy progress.

Step two: do not crack it if you do not have to

Unsalted fast hashes of common passwords are already broken - not by you, by everyone, years ago. A plain MD5 or SHA-1 of a dictionary word is a lookup, not a computation. Search the digest before you launch anything, and if the CTF’s rules allow outbound lookups, that is thirty seconds well spent.

Two more shortcuts worth checking first. If the challenge gives you a *salted* hash plus the salt and the salt is short, the salt may itself be the target. And if the plaintext is structured - a flag, an ID, a timestamp - then it is not a password at all, and the correct attack is a mask over its structure rather than any wordlist.

Step three: wordlist, then rules

The baseline attack is a wordlist. rockyou.txt remains the standard first pass - fourteen million real passwords from a real breach, which is what makes it work. It fits in memory and finishes in seconds against a fast hash.

When the plain wordlist fails, do not jump to brute force. Add rules: per-candidate mutations applied on the fly - capitalise, append digits, substitute leetspeak, reverse, duplicate. A rule file multiplies your wordlist by the number of rules without any of the storage, and it models how humans actually modify passwords.

# Plain wordlist
hashcat -m 0 hashes.txt rockyou.txt

# Wordlist x rules - the highest-yield attack in practice
hashcat -m 0 hashes.txt rockyou.txt -r rules/best64.rule

# Combinator: every word of A joined to every word of B
hashcat -m 0 -a 1 hashes.txt words.txt words.txt

# Hybrid: wordlist plus a 3-digit suffix
hashcat -m 0 -a 6 hashes.txt rockyou.txt ?d?d?d
best64.rule is the standard starting point. OneRuleToRuleThemAll is far larger and worth trying when you have time to spend.

Step four: masks, when you know the shape

A mask attack is brute force with the search space restricted to a known pattern. It is the right tool when the challenge tells you the format - 'the password is six lowercase letters and two digits' - or when the plaintext is a flag with a known prefix.

TokenCharacter setSize
?la-z26
?uA-Z26
?d0-910
?sspecial characters33
?a?l?u?d?s95
?h / ?H0-9a-f / 0-9A-F16
# Exactly: uppercase, four lowercase, three digits  (Hello123)
hashcat -m 0 -a 3 hashes.txt ?u?l?l?l?l?d?d?d

# Custom charset: -1 defines a set, then use ?1
hashcat -m 0 -a 3 -1 ?l?d hashes.txt flag{?1?1?1?1?1?1}

# Incremental: every length from 1 to 8 over lowercase
hashcat -m 0 -a 3 --increment --increment-max 8 hashes.txt ?l?l?l?l?l?l?l?l

Do the keyspace arithmetic before you start, because it decides whether the attack is minutes or centuries. ?u?l?l?l?l?d?d?d is 26 × 26⁴ × 10³ ≈ 1.19 × 10¹⁰ candidates - trivial for MD5 on a GPU. Eight fully mixed characters, ?a × 8, is 95⁸ ≈ 6.6 × 10¹⁵, which is a different conversation entirely, and against bcrypt it is not a conversation at all.

Step five: build a custom wordlist

CTF passwords are frequently themed on the challenge itself - the event name, a character from the description, the service banner. A twenty-word list you wrote yourself, run through a rule file, beats fourteen million generic passwords when the target is themed.

# Scrape candidate words from the challenge’s own site
cewl -d 2 -m 5 -w custom.txt https://target.example/

# Expand with mutations: years, digits, capitalisation, leet
hashcat --stdout custom.txt -r rules/best64.rule > custom_expanded.txt

# Or generate combinations of a themed base list
crunch 8 8 -t ctf%%%%% -o masked.txt

Things that look like cracking and are not

  • Length extension. Given H(secret || data) and the length of the secret, you can compute H(secret || data || padding || anything) without ever knowing the secret. It applies to Merkle-Damgard hashes - MD5, SHA-1, SHA-2 - and not to SHA-3 or HMAC. If a challenge signs with sha256(secret + message), this is the intended attack and no guessing is involved.
  • Hash collisions. MD5 collisions are generated, not searched: a chosen-prefix collision produces two different files with the same digest in seconds on a laptop. A challenge asking for 'two different inputs with the same MD5' wants a collision tool, not a cracker.
  • JWT secret recovery. Cracking HS256 is cracking an HMAC key, and short keys fall to a wordlist quickly. Long random keys do not, and if the token resists a wordlist the bug is elsewhere in the token handling.

A working order of operations

  1. Identify the hash and confirm the salt placement.
  2. Look the digest up before computing anything.
  3. rockyou.txt plain, then rockyou.txt with best64.rule.
  4. A themed wordlist built from the challenge text, with rules.
  5. A mask, if the plaintext structure is known or stated.
  6. Incremental brute force - only for fast hashes and short candidates.
  7. If none of that lands: stop. The challenge is not a cracking challenge, and the real bug is in how the hash is used.

Step seven is the one people skip. Cracking is unusually seductive because a running job feels like progress even when it is searching a space the answer was never in.

Further reading

hashescrackinghashcatwordlistsrulesmasksbcrypt

Related posts