Why Password Policies Don't Protect Us

This article is the second in our our password security series. Article 1 – Pragmatic Password Security – can be found here.

In this post we do a technical deep-dive into how password cracking is efficient because of the human’s language and the need to remember. With examples, we’ll demonstrate why we should all think carefully about the passwords we create and even how we should act on Social Media platforms!

We’ll investigate and see why modern password policies are not really fit for purpose anymore and why technologies like MFA are essential when passwords are in use.

In the conclusion, we’ll define some rules for making passwords easy to remember as well as being **much** harder to crack.


Using tooling called hashcat we can efficiently attack cryptographic password hashes in an attempt to find the password someone has used. We can be successful if the password has been poorly selected. However, poorly selected can still meet the requirements of many password policies employed by companies today.

For this demo, we’re working on a Fedora 35 Desktop machine with an AMD Radeon graphics card that is a mid-range card:


42:00.0 VGA compatible controller: Advanced Micro Devices, Inc. [AMD/ATI] Baffin [Radeon RX 550 640SP / RX 560/560X] (rev cf)

Hashcat is only supplying work to the GPU and not the CPU.


On Fedora with an AMD GPU it’s worth heading to the AMD website and installing their linux driver for OpenCL support. It works pretty well.

Compromised vs Stolen Passwords

In any system that stores passwords, the password data must be stored in some encrypted way. We do not store passwords as plain text so that people can simply obtain them by reading a file for example. Unless you’re Godaddy.

Cryptographic Hash Functions

vary a lot in complexity and are designed to map input data of any length to a deterministic, unique hash (usually represented as a string value) of fixed size.

Cryptographic Hash Functions are one-way, you cannot reverse the computation. From the unique hash it must *not* be possible to reverse the cryptographic function in order to deduce the password. If a function allows you to reverse the computation then all hashes could be put through the reverse function to get to the password value easily.

If the cryptographic hash of your password in a system is obtained by a bad actor, the password is said to be Stolen. It is not known whether the hash has been cracked yet so the password value that created it is known, but as we will demonstrate – it’s probably worth thinking of it as Compromised.

Compromised passwords have already been cracked. This means the account that uses the password can be immediately used by a bad actor who bought the password set.

Cryptographic Attacks

Since we’ve already established that cryptographic functions cannot be reversed, we already really know how our attack should work. Once we have a cryptographic hash of a password we simply keep trying to generate the same hash by running the cryptographic function over and over with different data each time.

At some point we will get the right combination of characters input to the function in order to generate the same hash. The cryptographic function must of course produce the same unique hash if the same data is input to the function, otherwise we would never be able to match someone’s password.

Brute Force Attacks

We have a couple of options for attack. The first of which is also the dumbest, we call it a Brute Force attack. In this mode of attack we start with a minimum number of characters for the input and we also pass in a character set to use. We then start at the beginning of that data and start generating hashes – thousands and millions of hashes as we sequentially move through the data trying every combination of letters that fits. When we run out of combinations of characters at the minimum number of characters we add another character and keep going through all combinations of that number of characters and so on and so fourth until we get the hash we’re looking for.

Brute Force is the most reliable method of cracking a password. However, even for modern compute this represents a potentially huge investment in time. Length of password for Brute Force attacks is what will protect you. Every character extra raises the power of the number of combinations you have to search through to find the answer.

Dictionary Attacks

A dictionary attack is still a form of Brute Force attack, but we heavily optimise the number of combinations of characters to stick within human languages. This drastically speeds up the cracking process and yields a very good success rate.

Passwords have a large Achilles heal in that generally (especially before password managers) humans have to remember them. Humans are not good at remembering random sequences of characters, especially when they must include punctuation characters as well. There are still places where humans must input passwords to unlock things like encrypted disks, etc.

Humans instead tend to manipulate words in order to remember their password. A password used by a human is usually a single word that has been picked pseudo-randomly and then manipulated to fit the password policy of the company.

When this happens, a dictionary-based attack can be launched that relies on a human only using a subset of the complete set of character combinations.

NOTE: Pseudo-randomly here is an important part of the human trait as we’ll see later on when discovering how large word datasets are scraped by the bad actors intent on cracking passwords for a living.

This drastically reduces the time we need to pull off an attack. Firstly, let’s dive straight in to a basic password which we pick. Let’s for now just pick a simple password with no changes and lets choose a simple cryptographic function to use to store it.

MD5 Cryptographic Function

Some systems will store an MD5 hash of the password text. This hash is regarded as insecure these days as compute power has increased, but it’s still used today albeit usually for determining a level of trust of a file. Usually an MD5 hash is provided by someone providing a file to download. This is the hash they computed when the file was originally published. If the hash of the file you download doesn’t match then it’s corrupted in some way or else some bad actor has interfered with the file in some way.

Storing passwords as MD5 hashes is not particularly secure because:

  • They’re not salted
  • They’re cheap to compute
  • Collision attacks can break MD5 quite easily these days

We’ll talk about what hash salting is in a bit, but cheap computation means that we can be very fast at trying to produce the same MD5 hash.

Collision attacks rely on the length and complexity of a hash. As noted at the start, a Cryptographic Function has a fixed-length hash as an output. Unfortunately the MD5 hash is not particularly long and searching for an input that produces the same hash output is a rather quick process.

Information for fun: Certificates that form the basis of trust on the internet used to be signed with MD5 and in 2008 was destroyed by a paper that showed how an algorithm that required around 2^39 hash functions (NVIDIA cards can do some 2^8 hashes per second these days) could sign a rogue, but trusted version of the certificate.

Create the Hash

Let’s create an MD5 hash of the password I’ve picked at random – the word penguinery. It seems obscure enough to a human I think that another human wouldn’t guess it. But humans don’t guess, computers do. So long as this word is in the dictionary used to attack, the password will be cracked:

$ echo -n "penguinery" | md5sum | cut -d' ' -f1 > penguinery.md5 && cat ./penguinery.md5

We create the MD5 hash using the md5sum tool and save just the hash into a file which has the name of the password and an extension of md5 so we can remember what the password that generated the hash was!

It seems obscure enough to a human I think that another human wouldn’t guess the password we used. But humans don’t guess, computers do. So long as this word is in the dictionary used to attack, the password will be cracked.

Attack the Hash

Now we can attack the hash using a Dictionary Attack with hashcat. Linux comes with a built-in “dictionary” that’s used by a lot of different tools. We’ll use it as the basis for our dictionary attack. It has 479826 words in it, so it’s alright to get started with, but hashcat suggests getting the maximum performance requires a word list of at least 10 million words!

More on how you come up with so many words later.

For now, let’s crack this simple password:

An abbreviated output from hashcat:

$ hashcat -m 0 -a 0 -O -w 3 ./penguinery.md5 /usr/share/dict/words
hashcat (v6.2.4) starting

OpenCL API (OpenCL 2.1 AMD-APP (3224.4)) - Platform #2 [Advanced Micro Devices, Inc.]
* Device #2: Radeon RX 560 Series, 3456/3546 MB (3014 MB allocatable), 16MCU

Minimum password length supported by kernel: 0
Maximum password length supported by kernel: 31

Hashes: 1 digests; 1 unique digests, 1 unique salts
Bitmaps: 16 bits, 65536 entries, 0x0000ffff mask, 262144 bytes, 5/13 rotates
Rules: 1

Watchdog: Temperature abort trigger set to 90c

Host memory required for this attack: 1585 MB


Session..........: hashcat
Status...........: Cracked
Hash.Mode........: 0 (MD5)
Hash.Target......: 3c4c2ab3fc26b1512a08dbdb2b2acf6c
Time.Started.....: Fri Nov 19 21:34:54 2021 (1 min, 6 secs)

The password is cracked in a little over a minute on a mediocre GPU. But then we expected this to be pretty straightforward.

Picking a known word without anything else added to it is a very weak password approach.

The Importance of Salting


is something that’s added to the input of a cryptographic function to disguise or alter the output. The aim here is to obscure the cryptographic hash output.

Let’s see what reasons there are for doing that. As we’ve mentioned before the cryptographic function *must* produce the same output hash for the same set of characters in the same order as the function input. This in itself creates a problem, but is essential for checking credentials.

Let’s crack the same password again and see what hashcat has to say for itself. This time however, we pass in the `–show` option and use `time` to time the hashcat execution time:

$ time hashcat -m 0 -a 0 -O -w 3 --show ./penguinery.md5 /usr/share/dict/words

real	0m0.015s

5ms this time round because we can take advantage of the cryptographic function producing the same output given the same input. When hashcat cracks a password it stores the hash and password together in a lookup table. If two people are using the same password, cracking the second password is almost instantaneous. In fact, once you’ve simply gone through the word list and generated hashes for all of the words, it’s a simple case of using the hash to lookup the password in your cache. Hashcat calls this cache the `hashpot` and stores it under `~/.local/share/hashcat/`.

Unsalted hashes therefore bring up another problem in that if you were to administer this system and see the hashes of the passwords you might be able to guess what another person’s password is just from the hash. If someone else has the same hash and you, you know that the passwords are the same.

Enter, salt. Salting

Salting injects cryptographically random data to the start or end of the data input into the hashing function. The salt is stored alongside the hash and this results in transforming the hash value to something completely different for the same password. The salt spoils the hash value and is available to generate the same hash again using the password input when authentication needs to be performed.

It’s a really neat way of obscuring the hash and if you’re storing passwords in anything that doesn’t include a salt, change your methods NOW.

An example can be the Linux password store which stores the cryptographic hashes by standard as salted SHA-512. We can go ahead and create two password hashes that use the same password and the salt will mean that we cannot do any comparison between the hashes to determine that they’re the same password:

$ echo -n "penguinery" | openssl passwd -6 -stdin

$ echo -n "penguinery" | openssl passwd -6 -stdin

The salt in the first hash is encoded as elx9u77vYEYSoxqO and in the second the salt is Eun76i8AwJGUtRCd resulting in a completely different hash for the same password data.

This means that the people cracking passwords have to at least work for each password they crack. But as we’ve seen, it doesn’t take long anyway. Let’s do some more cracking, this time with the better salted sha512 hash:

$ hashcat -a 0 -O -w 3 ./penguinery.sha512 /usr/share/dict/words
hashcat (v6.2.4) starting in autodetect mode

OpenCL API (OpenCL 2.1 AMD-APP (3224.4)) - Platform #2 [Advanced Micro Devices, Inc.]
* Device #2: Radeon RX 560 Series, 3392/3480 MB (2958 MB allocatable), 16MCU


Session..........: hashcat
Status...........: Cracked
Hash.Mode........: 1800 (sha512crypt $6$, SHA512 (Unix))
Hash.Target......: $6$OObnBEEodi19.tix$arvoMZnGRV2xjeD8.kV.zkeRlhs91Hi...nUIrU0
Time.Started.....: Fri Nov 19 22:10:08 2021 (12 secs)
Time.Estimated...: Fri Nov 19 22:10:20 2021 (0 secs)

So, 12 seconds this time, not too shabby. Please don’t jump the gun and go around thinking that md5 is harder to crack than sha512. GPU workloads are very sensitive and you have to shove them lots of the same stuff to do in order to make them efficient. It just so happens that this workload suited my GPU better and Hashcat warned me that the MD5 could not be paralelised very well because of the workload.

But just so we’re sure – salting doesn’t slow down cracking the password, but it does stop you having large lookup tables that can be easily trawled for common passwords.

Password Policies

OK, so we’ve cracked a password that we knew was in a dictionary. But, password policies have been around for a long time.

Let’s apply what I think everyone considers to be the bog-standard business password policy. Instead of length, they really concerntrate on character groups. For `Brute Force` attacks this has some truth in it, as well as length (the best attribute by far) the number of character groups is also a good way of slowing down the crack as there are many more combinations to work through.

But what about the `Dictionary Attack`? Unfortunately the human element of remembering the password comes into play and we can optimise the cracking process to follow the model of the human.

Here’s our password policy:

– minimum 8 characters
– at least one number
– at least one special character
– at least one uppercase character

Further password policies also work into the hands of the cracking teams with having to rotate passwords on a regular basis for example. This means that a human tends to add a single digit number to the end of the password and increments it each time the password has to rotate. This is because humans can remember complex combinations of characters if they practice it enough. Sometimes even getting muscle memory for the password and perhaps not really being able to remember it without typing it on a keyboard! But regular rotation forces a human to use something they can remember easily in a short amount of time.

We’ve covered the [human problem with passwords before](https://propellent.io/security/pragmatic-password-security/) if you want to gain a little more information.

Here’s an example of how a human applies that policy so that the password can still be remembered: `Pengu1nery!`

We’re at 11 characters – IT will be pleased with us. We’ve got all of the character sets represented and most importantly, it’s something that a human can remember.

Breaking the Rules

Hashcat (and other tools like it) have a trick up their sleeve – they can manipulate word lists using easy to define rules which can exchange characters in each word, append or chomp characters. Essentially we just generate rules to do the typical things that humans do.

The rules essentially just extend the wordlist. We could write software scripts to do the same or more complex transforms to the wordlist and just use the extended wordlist instead, it’d have the same effect. But the important thing here is that it is still nowhere near as many character combinations as it would take via a Brute Force attack and we will get a very high success rate.

Let’s create a [rules file](https://hashcat.net/wiki/doku.php?id=rule_based_attack) for hashcat with some common things that humans do:

$ cat ./rules
[email protected]
c ss$ so0
c ss$ so0 [email protected]
c ss$ so0 [email protected] sB3
c ss$ so0 [email protected] sB3 $!
c ss$ so0 [email protected] sB3 si1 $!

I’ve included a number of rules here, but it’s by no means exhaustive of course. Here’s a little breakdown of what the [rules](https://hashcat.net/wiki/doku.php?id=rule_based_attack) do:

l – All lowercase characters
u – All uppercase characters
c – Captilise the word
$1 – Append the character 1 to the end of the word (this is done for all single digit numbers)
[email protected] – Substitute the a character in the word with an @ character

Other rule sets combine a number of these rules so more than one rule is applied to each word. You have to define many rules to cover all the bases of course.

Now we’ve met the password policy and we’ve got a suitably obscure password and we’ve armed hashcat with a ruleset we can go ahead and attempt to crack the salted SHA-512 linux password hash:

$ hashcat -a 0 -O -w 4 -r rules ./penguinery-special.sha512 /usr/share/dict/words
hashcat (v6.2.4) starting in autodetect mode

OpenCL API (OpenCL 2.1 AMD-APP (3224.4)) - Platform #2 [Advanced Micro Devices, Inc.]
* Device #2: Radeon RX 560 Series, 3392/3475 MB (2954 MB allocatable), 16MCU


Session..........: hashcat
Status...........: Cracked
Hash.Mode........: 1800 (sha512crypt $6$, SHA512 (Unix))
Hash.Target......: $6$gUm.zjJdZDySyM3J$oOM6X0Yf5PMuDSxiIbLcSehag0Mx9TG...1pGtp.
Time.Started.....: Fri Nov 19 22:48:23 2021 (19 mins, 12 secs)
Time.Estimated...: Fri Nov 19 23:07:35 2021 (0 secs)
Kernel.Feature...: Optimized Kernel
Guess.Base.......: File (/usr/share/dict/words)
Guess.Mod........: Rules (rules)
Guess.Queue......: 1/1 (100.00%)
Speed.#2.........:      731 H/s (8.08ms) @ Accel:2048 Loops:256 Thr:128 Vec:1
Recovered........: 1/1 (100.00%) Digests
Progress.........: 6521724/10556172 (61.78%)
Rejected.........: 182754/6521724 (2.80%)
Restore.Point....: 296313/479826 (61.75%)
Restore.Sub.#2...: Salt:0 Amplifier:21-22 Iteration:4864-5000
Candidate.Engine.: Device Generator
Candidates.#2....: [email protected]! -> [email protected]!
Hardware.Mon.#2..: Temp: 44c Fan: 31% Util:  1% Core:1224MHz Mem: 300MHz Bus:8

19 minutes to crack. It’s longer, but it’s still gone quickly. This is far from an unbreakable password. My GPU is hardly breaking a sweat.

Hashcat was 61% through the entire wordspace that we’d supplied through the dictionary and ruleset when it cracked the password.

This is why these simple password policies haven’t been well-formed. Instead, we need to be able to educate people in password protection of devices. We need to give them advice on how to form their passwords and not to just do the standard things that everyone else is doing.

Dictionary Forming

We mentioned earlier about forming very large dictionaries and that we’d see how they’re formed later on. It could be you helping to create these dictionaries if you use social media!

Social Media forms a large part of creating these 10 million word plus dictionaries. Social Engineering is used on social media platforms to extract the harder to form words such as names or places. Classically bad actors use social media to post questions that probe for names of old pets or places that are memorable to people. These can get common mis-spellings and variations of spelling, especially when it comes to pet names the can form part of a password.

These posts are not innocent. They are extremely popular and end up with millions of comments. The comments are scraped by software to extract the words which are then filtered into unique entries and formed into dictionaries to optimise password cracking.

Here’s some examples of those posts that I see friends and family commenting on. I have seen some of my smart friends adding comments on these sorts of posts…

The goading of the messages that end with “Bet you can’t” are the ones that people seem to be compelled to answer. It doesn’t matter that there are endless answers to the question or bet – 1.7M comments on that post alone. It would take someone quite a while to come up with a list of place names so rich on their own. Each comment probably has several examples in it.

With the knowledge of how optimisations are formed, it’s possible to get decent passwords.

Password Policies do not add significant value

The example used to test this is quite simple, but clearly demonstrates that there is no step change in security posture when implementing a password complexity policy.

The key takeaways are:

  1. There are no fundamental changes to the cracking process, only an addition of text substitution rules.
  2. There is a small increase in cracking time when compared to a standard dictionary. However, anyone seriously cracking passwords will already have these rules in place.
  3. It makes passwords more complex and harder to remember for us humans, which in turn, encourages users to choose simpler passwords, or to write them down.

If you do have to follow a password policy, the best way to improve the strength of a password is to prioritise password length.

Another potential way to enhance a password is to  selectively apply the password policy, i.e. don’t consistently apply the same policy to all letters – which while making the password harder to crack, makes it even harder to remember, as the systematic approach introduced by the password policy is removed.

Quite a damning set of conclusions. 

Best Practice Passwords

The best practices if you must use passwords for authentication are:

Use Multi Factor Authentication

Make the password just one of the elements of the authentication.

Two or three forms of confirmation are often required by serious applications these days and can be achieved using trusted devices and/or time or counter based PIN number generators, etc.

Use Password Managers

Ramp the password manager up to 25+ characters with all character families.

Password managers remove the need for humans to remember (or even ever see) passwords. Once a human doesn’t have to remember, we are free to use a good length of password with all character sets and instead of words we can simply use random characters. Because of the time it takes to Brute Force attack a hash formed with these sorts of passwords they can be considered very secure.

Use multiple words to generate a long password

Encourage users that have to type in a password to use a sentence rather than a modified word.

– Tell yourself a story in a small sentence
– Add in some modifications too
– When there are repeated characters in a single word, only modify one of them. i.e. elementary -> el£mEntary is harder to make rules for than elementary -> El£m£ntary
– Changing the password regularly certainly helps and remembering small stories is much easier than a changing, random set of characters
– Minimum length should be 20 characters or so.

Get notified of new content

If you’re interested in what you just read and would like more information or advice on this topic, we’re always happy to share our knowledge.

We provide practical, compliant IT solutions, services, and expertise to support you and your team in building, protecting, and optimising your digital estate. Click here to learn more

To get more new blog articles and resources on this and other key IT topics, enter your name and email address and we’ll keep you informed.