A plain brute force tries every possible string, which is why it never finishes against anything longer than seven or eight characters. A mask attack is brute force with a brain: you describe the shape of the password (one capital, four lowercase, three digits) and hashcat tries only the strings matching that shape. The keyspace collapses from quadrillions to billions, and a search that was hopeless becomes minutes. This is how you brute force in a way that actually completes. Verified on hashcat 7.1.2.
TL;DR
The mask attack is hashcat's -a 3. A mask is a pattern, one token per character position, built from charsets: ?l lowercase, ?u uppercase, ?d digit, ?s symbol, ?a all of them. hashcat -m 0 -a 3 hashes.txt '?u?l?l?l?l?d?d?d' tries every "capital + 4 lowercase + 3 digits" string and nothing else. Quote the mask in zsh (the single quotes above), or the shell tries to glob the ? and aborts with "no matches found". Define your own charset with -1 (then reference it as ?1), and use --increment to try short lengths first. Use a mask when you know the password's shape but it is not in a wordlist. The catch: every extra character multiplies the keyspace, so masks only finish against fast hashes; against bcrypt or Argon2 they are hopeless.
How a mask works
In a mask, each ?x token is one character position and the charset it expands to. hashcat walks every combination. So ?l?l?l is every three-lowercase-letter string (aaa to zzz, 26^3 = 17,576 candidates), and ?d?d?d?d is every four-digit PIN (0000 to 9999, 10,000).
The built-in charsets, verified against hashcat --help:
| Token | Expands to |
|---|---|
?l | lowercase a-z |
?u | uppercase A-Z |
?d | digits 0-9 |
?h | lowercase hex 0-9a-f |
?H | uppercase hex 0-9A-F |
?s | symbols (space and punctuation) |
?a | everything: ?l?u?d?s |
?b | every byte 0x00 to 0xff |
You can mix literals and tokens freely. admin?d?d?d?d cracks admin0000 through admin9999. ?u?l?l?l?l?l?l20?d?d cracks a capitalised seven-letter word followed by a year in the 2000s.
A real mask crack
Here is a mask attack finding a five-character password of the shape "three letters, two digits." First the hash (MD5 of abc12), then the attack:
echo -n "abc12" | md5sum | cut -d' ' -f1 > target.hash
hashcat -m 0 -a 3 target.hash '?l?l?l?d?d'hashcat walks the 26 * 26 * 26 * 10 * 10 = 1,757,600 candidate keyspace and finds it:
b2157e7b2ae716a747597717f1efb7a0:abc12
Status...........: Cracked
Hash.Mode........: 0 (MD5)
Guess.Mask.......: ?l?l?l?d?d [5]Under two million candidates, instant. Compare that to ?a?a?a?a?a (all-printable, same length), which is 95^5 = about 7.7 billion, thousands of times larger for the same password length. Knowing the shape is what makes the difference.
Custom charsets
The eight built-in custom-charset slots, -1 through -8, let you define exactly the set a position should use. Define it, then reference it by number in the mask.
# -1 = lowercase + digits. Mask: 6 chars from that set.
hashcat -m 0 -a 3 -1 '?l?d' hashes.txt '?1?1?1?1?1?1'
# -1 = upper+lower (a letter), -2 = symbols.
# Mask: capital, 5 letters, one symbol.
hashcat -m 0 -a 3 -1 '?u?l' -2 '?s' hashes.txt '?u?1?1?1?1?1?2'Custom charsets are how you encode a known password policy precisely: "must start with a letter, must end with a symbol, the middle is alphanumeric" becomes one mask with two custom sets, and hashcat searches only the strings that policy allows.
Increment mode: try shorter first
A fixed mask is exactly one length. Real passwords vary, so --increment runs the mask at every length from 1 up to its full width, shortest first (where the cracks are cheapest):
# All-lowercase, lengths 1 through 8, short first
hashcat -m 0 -a 3 --increment hashes.txt '?l?l?l?l?l?l?l?l'
# Bound the range: only lengths 6 to 8
hashcat -m 0 -a 3 --increment --increment-min 6 --increment-max 8 hashes.txt '?l?l?l?l?l?l?l?l'Without increment you would crack an eight-character password but miss the six-character ones entirely. With it, you sweep the whole length range in increasing order of cost.
The keyspace maths (why masks have a ceiling)
A mask's size is the product of its charset sizes. Each token multiplies:
| Mask | Keyspace | Notes |
|---|---|---|
?d?d?d?d | 10,000 | A 4-digit PIN. Instant. |
?l?l?l?l?l?l | 308 million | 6 lowercase. Seconds on a fast hash. |
?u?l?l?l?l?d?d?d | ~11 billion | Policy shape. Minutes on a fast hash. |
?a?a?a?a?a?a?a?a | ~6.6 quadrillion | 8 all-printable. Hours to days on a fast hash. |
?a?a?a?a?a?a?a?a?a | ~630 quadrillion | 9 all-printable. The wall. |
Two lessons. First, constrain every position you can: dropping ?a to ?l?d on even a couple of positions cuts the keyspace by orders of magnitude. Second, the ceiling is real. Add characters and you eventually hit a wall no hardware clears in your lifetime, and against a slow hash that wall arrives at eight characters. When the estimated time reads in years, you are past the point where a mask is the right tool; switch to a wordlist plus rules.
When to use a mask (and when not to)
Use a mask when:
- You know the shape: a password policy, a leaked hint, an observed pattern like
Name@1234. - The password is plainly not in a wordlist (random-ish but structured).
- The hash is fast (MD5, NTLM, SHA-1) so a billion-candidate mask finishes.
Do not use a mask when:
- You have not run a wordlist and rules yet, those crack more, faster.
- The hash is slow (bcrypt, Argon2, sha512crypt); the maths does not allow it.
- You do not actually know the shape and are about to brute force
?a?a?a?a?a?a?a?aon a hope.
Where to go next
- Where masks fit in the bigger picture: dictionary vs brute force vs mask vs hybrid.
- The techniques that crack more: wordlists and rules.
- The fill-in-your-values commands: hashcat cheat sheet.
- The basics: how to use hashcat.
Sources
Authoritative references this article was fact-checked against.
- hashcat, mask attack (official)hashcat.net
- hashcat wiki, core usage (official)hashcat.net





