TechEarl

Hashcat Mask Attack (-a 3): Smart Brute Force That Finishes

A mask attack is brute force with a brain: you tell hashcat the shape of the password and it skips the quadrillions of strings nobody picks. I cover the charsets, custom charsets, increment mode, the keyspace maths, and the masks that actually crack real passwords. Tested on hashcat 7.1.2.

Ishan Karunaratne⏱️ 7 min readUpdated
Share thisCopied
Built-in and custom charsets, increment mode, keyspace maths, and the policy-shaped masks that crack passwords a plain brute force never would in time. Tested on hashcat 7.1.2.

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:

TokenExpands to
?llowercase a-z
?uuppercase A-Z
?ddigits 0-9
?hlowercase hex 0-9a-f
?Huppercase hex 0-9A-F
?ssymbols (space and punctuation)
?aeverything: ?l?u?d?s
?bevery 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:

bash
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:

text
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.

bash
# -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):

bash
# 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:

MaskKeyspaceNotes
?d?d?d?d10,000A 4-digit PIN. Instant.
?l?l?l?l?l?l308 million6 lowercase. Seconds on a fast hash.
?u?l?l?l?l?d?d?d~11 billionPolicy shape. Minutes on a fast hash.
?a?a?a?a?a?a?a?a~6.6 quadrillion8 all-printable. Hours to days on a fast hash.
?a?a?a?a?a?a?a?a?a~630 quadrillion9 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?a on a hope.

Where to go next

Sources

Authoritative references this article was fact-checked against.

Tagshashcatmask attackbrute forcecharsetpassword cracking

Found this useful? Pass it on.

Copied

Ishan Karunaratne

Software Systems Architect · Senior Software Engineer · Engineering Leadership

Software systems architect and senior software engineer with more than two decades designing, building, and running production software, Linux systems, and DevOps infrastructure, and lately working AI into the stack. Now a CTO, though what I write here is drawn from the full arc of that work, across architecture, engineering, and operations, not any single job.

Keep reading

Related posts

ShellCheck tutorial: install the shell script linter, run it on a Bash file, read SC2086 and other SC codes, suppress findings with disable directives, and add it to CI.

ShellCheck: Catch Bash Bugs Before They Bite

ShellCheck is a static analysis linter for Bash and POSIX sh: install it, run it on a script, read the SC codes, suppress false positives with disable directives, and wire it into CI.

How hashcat rule functions mutate wordlist words, the best64 ruleset with real output, stacking rules, writing your own, and the big public rulesets. Tested on hashcat 7.1.2.

Hashcat Rules: More Cracks From the Same Wordlist

Rules are the highest-yield technique in cracking: one wordlist word becomes hundreds of plausible variants. I cover the rule functions, best64 with real generated output, stacking, writing your own, and the big public rulesets. Tested on hashcat 7.1.2.