Hopefully you have a basic familiarity with using Hashcat. This guide will assume that you know what Hashcat is, how to load up basic dictionaries and start a job from the command line. If Any of that sounds foreign to you, I recommend you read the precursor A guide to password cracking with Hashcat before continuing.

Terms to be aware of

  • cracking chain The entire set of tools (both hardware and software) used to crack hashes
  • hash A fixed-length output of data that represents a plaintext value after it has been placed through a hash function
  • mask A specific set of rules used to tell your cracking utility which parts of a key space should be used
  • plaintext The input for a hash function. For example, a password.

The power of masks

Dictionaries can be a great tool in your password cracking arsenal, but they can hit some very real limits quickly. For example, in order to use a dictionary, you have to store it, which means you need to store all of the data for all of the different combinations you want to try. Depending on the key space and combinations you are working against, this can add up very quickly. It's also worth noting that there is a very measurable amount of time it takes to load dictionaries into memory, and I/O can become a bottleneck in your cracking chain. This is where using masks come in!

Back in the early days of password cracking, if you didn't use a dictionary, you could pretty much only use a straight brute-force attack. Fortunately, our techniques and tools have come quite a long way since then, and honestly brute-force cracking is all but obsolete these days. By using a mask, you can specify what combinations of characters to run through during your attack. The great thing here is flexibility: If you just wanted to run a straight brute-force, you would wait for your tools to run through every combination in every position until it (eventually) finds the right combination. Obviously that can take an extraordinary amount of time to complete. But what if we knew something about the plaintext itself. Let's say we now that the password is only made with lowercase letters, and ends with two numbers. Even further, let's assume that we know the password is exactly 6 characters long. At this point, it would seem like a waste to go through standard brute-force. that would require running through all combinations from one letter up, and using uppercase and special characters that we know will never match. The biggest problem here is running through bad combinations simply wastes time, and no one wants that.

Hashcat is a very flexible tool. It offers a range of different attack modes to help you out with your various cracking needs. You should reference the help information often, but let's take a quick look at it now to see the list of these modes. Using oclHashcat v1.21, you will see this information:

$ hc --help | grep -A 7 "Attack modes"
* Attack modes:

    0 = Straight
    1 = Combination
    3 = Brute-force
    6 = Hybrid dict + mask
    7 = Hybrid mask + dict

Now, technically we don't see an option for "masks" here, but I will let you in on a tiny secret. Even though Hashcat called mode 3 "Brute-force", it isn't actually your traditional brute-force. This is your mask mode ( in fact, you have to go out of your way to even make hashcat perform a traditional brute-force ). Using a mask, we can simply use the following to skip all of the extra combinations we know will fail:

$ hc -a3 hash.file ?l?l?l?l?d?d

It's a lot easier than it looks, and it's ridiculously powerful. This attack mode is a definite game-changer.

Defining your masks

When using masks, you need to define a minimum of 4 options for hashcat:
hashcat-binary attack-mode hash-file mask

It is crucial that you define each part. That being said, you are free to add in additional options, as long as those 4 exist.

  • hashcat-binary This should be obvious. It's the path to the hashcat binary itself ( in our examples: hc )
  • attack-mode For mask attacks, this will always be -a3
  • hash-file Similar to a dictionary attack, this will be the location of the file with all of your hashes
  • mask The mask can either be the actual mask you want to use, or the location of a file with multiple masks inside of it. Either one is fine, but you must supply one of them ( and only one )

To create your mask, you will simply specify which character set you want to use in which position. By default, Hashcat comes with 5 pre-defined character sets, and allows you to make up to 4 custom character sets. The built-in sets are as follows:

   ?l = abcdefghijklmnopqrstuvwxyz
   ?d = 0123456789
   ?s =  !"#$%&'()*+,-./:;<=>?@[]^_`{|}~
   ?a = ?l?u?d?s

So let's say we wanted to act more like a traditional brute-force and go through all character combinations exactly 3 characters long. To do this, we will use the full "all characters" set, defined as ?a.

$ hc -a3 hash.file ?a?a?a

Easy, isn't is?

Now, what if we wanted to try the first letter uppercase, the next four lowercase, and the last two digits?

$ hc -a3 hash.file ?u?l?l?l?l?d?d

As I mentioned, it's very flexible and powerful.

Custom character sets

Using masks this way covers a lot of ground, but it can still stand to be a little more specific. Sometimes you may only need to search a very specific subset of characters. For example, what if you had a hash where you knew the plaintext was going to be a six character hex value. Obviously, using ?d isn't going to be enough, and using ?a will run through too many combinations that we know will be absolutely wrong. This becomes a good candidate for a custom character set.

Hashcat allows you to define a custom character set in one of four buffers:


When you define a character set, you can then call a new flag with the number of that set to use the character set in your mask. Using the example above, we would first need to define our hex character set to be used with hashcat, and we will place it in buffer 1.

-1  0123456789abcdefABCDEF

If you don't know whether the plaintext will be in uppercase or lowercase hex, this character set should cover both. The only drawback here is that it's a little cumbersome to type. Fortunately, you can shorten this down using built-ins. The following character set is exactly the same:

-1  ?dabcdefABCDEF

I tend to think that typing less is better, but feel free to use whichever method suits you best. At this point, you are ready to use character set 1 in your masks. Continuing our previous example, we can use this mask to run through your six character hex key space:


Now let's put this all together and see how it looks on the command line:

$ hc -a3 hash.file -1 ?dabcdefABCDEF ?1?1?1?1?1?1

It's not quite as daunting as it first seemed, is it? You can, of course, refine this even further and mix in additional character sets if you need to. Let's say we know that the hex plaintext we are looking for starts with "c6" and the second to last position will only be between 0 and 9. We could create the following mask:

$ hc -a3 hash.file -1 ?dabcdefABCDEF c6?1?1?d?1

Differences in versions

It's worth noting that Hashcat and oclHashcat work slightly different here. Hashcat will run through all increments of your mask by default, for example: ?a?a?a will become:


oclHashcat will not do this unless you explicitly declare incremental mode with the -i flag.

Real world practice

Using Hashcat, let's see a quick example of masks you can try from the pre-packaged examples. If you look at the file examples/A3.M0.word (in your hashcat directory), you will notice that all plaintexts for this exercise are lowercase and 5 characters long. We should easily be able to create a simple mask to crack 100% of these hashes.

$ ./hc -a3 examples/A3.M0.hash ?l?l?l?l?l
Initializing hashcat v0.47 by atom with 8 threads and 32mb segment-size...

Added hashes from file examples/A3.M0.hash: 102 (1 salts)

NOTE: press enter for status-screen

Input.Mode: Mask (?l) [1]
Index.....: 0/1 (segment), 26 (words), 0 (bytes)
Recovered.: 0/102 hashes, 0/1 salts
Speed/sec.: - plains, - words
Progress..: 26/26 (100.00%)
Running...: --:--:--:--
Estimated.: --:--:--:--

Input.Mode: Mask (?l?l) [2]
Index.....: 0/1 (segment), 676 (words), 0 (bytes)
Recovered.: 0/102 hashes, 0/1 salts
Speed/sec.: - plains, - words
Progress..: 676/676 (100.00%)
Running...: --:--:--:--
Estimated.: --:--:--:--

Input.Mode: Mask (?l?l?l) [3]
Index.....: 0/1 (segment), 17576 (words), 0 (bytes)
Recovered.: 0/102 hashes, 0/1 salts
Speed/sec.: - plains, - words
Progress..: 17576/17576 (100.00%)
Running...: --:--:--:--
Estimated.: --:--:--:--

Input.Mode: Mask (?l?l?l?l) [4]
Index.....: 0/1 (segment), 456976 (words), 0 (bytes)
Recovered.: 0/102 hashes, 0/1 salts
Speed/sec.: - plains, - words
Progress..: 456976/456976 (100.00%)
Running...: --:--:--:--
Estimated.: --:--:--:--

    --- Output Omitted ---

All hashes have been recovered

Input.Mode: Mask (?l?l?l?l?l) [5]
Index.....: 0/1 (segment), 11881376 (words), 0 (bytes)
Recovered.: 102/102 hashes, 1/1 salts
Speed/sec.: - plains, 19.94M words
Progress..: 11819600/11881376 (99.48%)
Running...: --:--:--:--
Estimated.: --:--:--:--

Started: Fri Jun 27 12:52:04 2014
Stopped: Fri Jun 27 12:52:05 2014

100% recovery; we must be on a roll! :]