How to create dictionaries that comply with specific password strength policies (using Rule-based attack)

Dictionaries that contain all groups of characters

Nowadays, many services, operating systems and websites have requirements for the complexity of the password that you want to use for your account. As a rule, it must be a password of at least a certain length and containing various groups of characters (upper and lower case letters, numbers, special characters). Some services also check passwords invented by the user in a dictionary and make sure that they do not have many identical characters in a row, do not accept previously used passwords, etc.

For this reason, common dictionaries become ineffective for security auditing. As will be shown below, in typical dictionaries, about 99.65% of the password candidates do not comply with the strong password policy. That is, using them means that almost all the brute-force time is wasted. Although a mask attack allows for a fairly flexible configuration of password candidates, it will also not be very effective in brute-force attacks on passwords created according to security policy requirements.

Hence, the problem arises: how to create a dictionary in which there will be only passwords, which, for example, have at least one number, one capital letter, one small letter and one special character? A rule-based attack is suitable for this.

But Rule Based Attack is not designed to generate new dictionaries (or password candidates) from scratch – it creates new words by changing existing ones using a set of rules. Can you think of something? Yes, you can do the following: create a dictionary using the Mask that contains all possible words and, using the Rules, filter out only those that comply with the complex password policy.

It is highly recommended to read about this attack:

Let's start with an example from John the Ripper. First, we will learn how to create a dictionary of complex passwords from existing dictionaries.

Selection of passwords that comply with a certain policy

For example, it is known that an organization has the following password requirements policy: the password must contain at least one capital letter; at least one small letter; at least one digit and at least one special character. Password length cannot be less than 8 characters.

Objective: to prepare a dictionary for hash cracking, which takes into account the policy of password requirements and removes passwords that do not match the criteria.

For this we need two rules:

  • /?C — means to reject a word if it does not contain a character in class C.
  • >N — means to reject the word if it does not exceed N characters

Let's turn to the table "Character classes".

This rule set rejects passwords that do not contain at least one of the characters: lowercase letter, uppercase letter, number, punctuation marks:

/?l /?u /?d /?p

Since passwords may contain other special characters, another set of rules needs to be compiled:

/?l /?u /?d /?s

This second set of rules rejects passwords that do not contain at least one of the characters: lowercase letter, uppercase letter, number, special character.

Within one line, there is a logical "AND" applied, that is, all passwords that are missing at least one character class will be discarded.

But there is a logical "OR" between the lines, that is, all passwords that meet the criteria of the first line or the criteria of the second line will be skipped.

/?l /?u /?d /?p >7
/?l /?u /?d /?s >7

Add the following lines to the /usr/share/john/john.conf file (the ruleset is named StrongPass):

/?l /?u /?d /?p >7
/?l /?u /?d /?s >7

To check their performance, download the dictionary:

wget -U 'Chrome'
7z e rockyou.txt.bz2

Let's check the number of passwords in it:

cat rockyou.txt | wc -l

That is, there are 14.344.391 (fourteen million) passwords in the file.

Now let's check how many passwords will be filtered:

john --rules=StrongPass --wordlist=rockyou.txt --stdout | wc -l


Using default input encoding: UTF-8
Press 'q' or Ctrl-C to abort, almost any other key for status
50785p 0:00:00:01 100,00% (2020-11-29 17:08) 35764p/s *7¡Vamos!

There are only 50,785 password candidates left that match the specified conditions! This is 50785 ÷ 14344391 × 100 = 0.35% of the entire dictionary!!! That is, if we had not used this optimization, 99.65% of calculations during brute-force would be meaningless, since we would be checking knowingly inappropriate passwords.

But does that make sense?

Now that we know how to extract passwords that meet the complexity requirements, we can start creating our own dictionaries. But there is a nuance…

Let's digress a little and talk about the size of dictionaries and the number of password candidates.

Let's turn to the formula for the number of possible combinations:

number of characters(password length)

That is, the number of password candidates is equal to the number of possible characters in the password to the power of the number, which is the length of the password.

Let's count how many passwords the following command will generate (uppercase and lowercase letters, numbers – no special characters!) with maskprocessor:

maskprocessor -1 ?l?u?d ?1?1?1?1?1?1?1?1

628 = 2,183401056×1014

That's… that's a lot of passwords.

For example, a dictionary with passwords of uppercase and lowercase letters plus numbers of three characters takes 0.931 MB, a dictionary of four characters will take 70.5 MB, and with a length of five characters the dictionary will be 5.1 GB in size. That is, with each new character added, the size of the dictionary will increase approximately 76 times. That is, an 8-character dictionary will be about 2238 Terabytes in size…

Dictionary with passwords in which at least one capital and small letter and number

Let's start creating a dictionary that contains at least one character from different groups.

Usually in different services it is required to set passwords of at least 8 characters, but for the reasons discussed above, I will work with passwords 5 characters long.

To speed up the creation of dictionaries, I will use the File system in RAM – you can also do this if you have enough RAM, or you can work with dictionaries on regular disks.

Let's create a mount point:

mkdir /tmp/mytmpfs

Let's create a virtual file system of 20 Gigabytes in RAM:

sudo mount -t tmpfs -o size=20g tmpfs /tmp/mytmpfs

Go to it

cd /tmp/mytmpfs

Create a 5-character dictionary (uppercase and lowercase letters, numbers):

maskprocessor -1 ?l?u?d ?1?1?1?1?1 > dic.txt

For those who got stuck after the previous command – this is not a bug, it should be so if you create a dictionary on a regular or even on a solid-state disk – just wait for the data to be written to the disk. This will take about a minute. My dictionary in RAM was created in 7 seconds and nothing hung.

RAM usage:

Let's count the number of passwords:

cat dic.txt | wc -l

I got: 916132832

Add the following lines to /usr/share/john/john.conf (the ruleset is named lud5):

/?l /?u /?d

Let's create a new dictionary and save it to the lud5.txt file:

john --rules=lud5 --wordlist=dic.txt --stdout > lud5.txt

In this file, 438859200 passwords were obtained, that is, about 2 times less, the size of the dictionary was also reduced by about 2 times (it became 2.5 GB, but it was 5.1 GB).

Let's look at an example of the content in the file:

tail lud5.txt

Dictionary with passwords in which at least two upper and lower case letters and numbers

Now let's look at how to create passwords in which at least two characters from the specified range.

For this there is a special rule “%N?C”: it rejects a word if it does not contain at least N characters of class C.

An example of rules that require a password to be at least 8 characters long and contain at least two characters from the classes:

  • big letters
  • small letters
  • digits
  • special symbols

The set is named SuperStrongPass:

%2?l %2?u %2?d %2?p >7
%2?l %2?u %2?d %2?s >7

An example of rules in which a password is required to have at least two lowercase letters, at least one uppercase and at least one digit:

%2?l /?u /?d

Let's start filtering passwords:

john --rules=llud5 --wordlist=dic.txt --stdout > llud5.txt

It turned out 281,216,000 passwords for 1.6 Gigabytes.


So, competent policies to the complexity of the password lead to the fact that the number of candidates for passwords becomes huge and with a sufficient length of the password, brute force becomes impossible due to the fact that it takes too long to complete the attack.

Recommended for you:

Leave a Reply

Your email address will not be published.