TechEarl

Dictionary vs Brute Force vs Mask vs Hybrid: When to Use Each

Picking the attack is the judgement call that separates fast cracks from wasted GPU-weeks. I lay out the five attack types, the keyspace maths that decides between them, and the order I actually run them in, with the slow-hash caveat that changes everything.

Ishan Karunaratne⏱️ 9 min readUpdated
Share thisCopied
The five password attack types explained with the keyspace maths behind them: dictionary, rules, mask, hybrid, combinator. A decision framework for picking the right one.

The single decision that separates a fast crack from a wasted GPU-week is which attack you run. Beginners reach for brute force because it sounds powerful, watch the estimated time read "3 years," and give up. Experienced crackers almost never brute force. They run a wordlist, then mangle it with rules, then add a small brute-forced piece, and they crack most of the table before an exhaustive search has finished its first one percent. This is the framework for choosing, and the keyspace maths behind why the order matters.

TL;DR

There are five attacks. Dictionary (-a 0) hashes a wordlist, your default. Rules mutate each wordlist word (capitalise, append digits, leetspeak) and give the best effort-to-reward ratio in cracking. Mask (-a 3) is smart brute force: try every string matching a pattern like ?u?l?l?l?d?d. Hybrid (-a 6/-a 7) bolts a brute-forced piece onto wordlist words (summer + 2024). Combinator (-a 1) joins two wordlists for passphrases. Run them in that order, and against a slow hash (bcrypt, Argon2) stop after rules, because exhaustive masks are computationally hopeless. The reason a wordlist beats brute force is simple: real passwords are not random, so most of them are in a list or one rule away from a word in it.

Why brute force is usually the wrong answer

Every attack is really a way of generating candidates to hash and compare. The question is always "how many candidates does this attack produce, and how likely is the password to be among them?" That count is the keyspace, and it grows terrifyingly fast.

A full-keyspace brute force of an eight-character password over all 95 printable ASCII characters is 95^8, about 6.6 quadrillion candidates. Even at a fast-hash rate of 100 billion guesses a second, that is around 18 hours; push to nine or ten characters and it is years, then centuries. Against a slow hash it is hopeless at eight. The keyspace is the enemy, and brute force does nothing to shrink it.

A wordlist is the opposite bet. rockyou.txt is 14 million candidates, a rounding error next to 6.6 quadrillion, yet it cracks a large fraction of any real dump because humans pick from a tiny, predictable slice of the keyspace. You are not searching the space; you are searching the likely space. That is the whole game.

The five attacks

1. Dictionary attack (-a 0)

Hash every word in a wordlist and compare. This is your first move, every time.

bash
hashcat -m 0 -a 0 hashes.txt rockyou.txt

Most cracks you will ever get come from a good wordlist alone, because the password literally is a known password from a previous breach. Start here. Always. The art is in choosing and ordering wordlists.

2. Rule-based attack (-a 0 plus -r)

A rules file mutates each wordlist word: capitalise it, append 123, swap a for @, double it. One word becomes hundreds of plausible variants, so a 14-million-word list with a 66-rule file tests roughly a billion candidates, still tiny, but now it covers Password1, p@ssw0rd, summer2024!, the shapes people actually use.

bash
hashcat -m 0 -a 0 hashes.txt rockyou.txt -r rules/best66.rule

This is the highest-yield attack in cracking. If you learn one technique beyond the plain dictionary, learn rules.

3. Mask attack (-a 3)

A mask is smart brute force. Instead of trying every character everywhere, you constrain each position to a likely set: ?u?l?l?l?l?l?d?d?d is "one capital, five lowercase, three digits," which matches Summer123 and millions like it, while ignoring the vastly larger space of random strings.

bash
hashcat -m 0 -a 3 hashes.txt ?u?l?l?l?l?d?d?d

Reach for a mask when you know the shape but not the value: a known password policy, a leaked hint, an observed pattern. A targeted mask of 26 * 26^4 * 1000 is about 11 billion, days of work shrunk to minutes.

4. Hybrid attack (-a 6 and -a 7)

Hybrid bolts a brute-forced mask onto wordlist words. -a 6 appends (word + mask), -a 7 prepends (mask + word). This nails the single most common real-world pattern: a dictionary word with digits or a symbol stuck on the end.

bash
# password + 3 digits: password000 .. password999
hashcat -m 0 -a 6 hashes.txt rockyou.txt ?d?d?d
# 4 digits + word: 2024summer
hashcat -m 0 -a 7 hashes.txt ?d?d?d?d rockyou.txt

Hybrid is what you run after rules when you suspect a word + number or word + year + ! shape and want to be exhaustive about the appended part.

5. Combinator attack (-a 1)

Concatenate every word in list A with every word in list B. Built for passphrases assembled from two words (correcthorse, redballoon).

bash
hashcat -m 0 -a 1 hashes.txt words-a.txt words-b.txt

Two 10,000-word lists make 100 million combinations. Useful, niche, and the right tool the moment you suspect a two-word passphrase.

The decision framework

Here is the table I actually think through. Match the attack to what you know about the password.

What you knowBest attackWhy
Nothing yetDictionary, then rulesMost passwords are a breached password or one mutation away from one
It is a common-ish password with tweaksRules (-r)Covers capitalise/append/leetspeak cheaply
The exact shape (length + character classes)Mask (-a 3)Constrain the keyspace to plausible strings only
A word with digits/symbol appendedHybrid (-a 6)Exhaustive over the appended part, wordlist over the base
A two-word passphraseCombinator (-a 1)Joins two lists into phrase candidates
A username or old password per hashAssociation (-a 9)One targeted candidate per hash

And the order I run them, against a fast hash:

  1. Dictionary with rockyou.txt.
  2. Dictionary plus best66.rule (the classic best64), then a bigger rule set.
  3. Hybrid for word + digits patterns.
  4. Targeted masks for any known shapes.
  5. Bigger wordlists, then incremental masks, only if time allows.

The slow-hash caveat that changes everything

Everything above assumes you can try a lot of candidates. Against a slow hash, bcrypt, Argon2, sha512crypt, you cannot. At tens of thousands of guesses per second instead of billions, the maths inverts:

  • A wordlist and good rules still work, because they are small (millions to low billions of candidates) and target weak passwords.
  • Masks and brute force are off the table. An eight-character mask that takes minutes against MD5 takes centuries against bcrypt. The estimated-time line will tell you so.

So against a slow hash the strategy is: run your best wordlist with your best rules, crack the weak passwords, and accept that the strong ones are out of reach. That is not a failed crack; that is the defender's slow hash doing exactly its job. Knowing when to stop is as much a skill as knowing which attack to start with.

Where to go next

Sources

Authoritative references this article was fact-checked against.

Tagspassword crackingdictionary attackbrute forcemask attackhybrid attack

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

find -name uses shell globs on the basename; find -regex matches a full regular expression against the whole path. The -regextype flavors, the GNU emacs vs BSD basic default drift, and when each one is the right tool.

find -regex vs -name: When to Use Regex in find

find -name takes a shell glob and matches the basename; find -regex takes a full regular expression and matches the whole path. That whole-path detail is the number one surprise: -regex '.*\.txt' works but -regex '.txt' matches nothing. The flag reference, -regextype flavors, the GNU vs BSD default-flavor drift, and when -name is the better tool.