Why an unsalted MD5 hash is bad practice

The other day, Paul Moore asked this question on Twitter:

Every developer will immediately recognize that as a hex string, since every character is in this range:

[0..9, "a".."f"]

Converting this hex number to an array of bytes gives us this result:

[216, 216, 166, 210, 233, 225, 251, 153, 48, 43, 71, 154, 143, 229, 64, 212]

Interestingly enough, these are exactly 16 bytes.

Now I do not claim to be an #infosec expert, but I do know you should never ever store passwords in plain text. You should use a cryptographic hash function. Most cryptographic hash functions produce a fixed-length hash value (aka digest length). SHA-1, for example, always produces a 160-bit hash value.

16 bytes do not match a whole lot of cryptographic hash functions, but they do match the (128 bit) MD5 digest length. I’m beginning to suspect at least 2 mistakes were made here; the developer used an insecure hash function, and he/she did not salt the password.

Let us put that to the test.

First, I downloaded the RockYou database off the Internet. In 2009, RockYou lost a list of 14.5 million unique passwords to hackers, and it has been famous as the mother of all password word lists ever since.

I then ran the RockYou word list through Hashcat in attack mode 0 (aka “straight”):

echo d8d8a6d2e9e1fb99302b479a8fe540d4> hash.file
hashcat-cli64 -m 0 -a 0 -r rules/best64.rule hash.file rockyou.txt

Boom. Less than 10 seconds later, Hashcat cracked that password. On my commodity Windows laptop.

Paul Moore sha256 as a password? Are you trolling us? :-)

To answer Paul Moore’s question, at least 3 mistakes were made here:

  1. While MD5 is a generally a good checksum, it is insecure as a password hashing algorithm because it is simply too fast. You will want to slow your attacker down. Use bcrypt or PBKDF2 with at least 100K iterations. Depending on what hardware your attacker has at his disposal, his brute force attack on your data suddenly takes hundreds of years, if not longer.
  2. Always salt your passwords. Generate a unique, cryptographically secure random value for each password (so that two identical passwords, when hashed, will not hash to the same value). You will then stop rainbow table attacks on your data.
  3. Do not use sha256 as a password, stupid. Use a password manager, and generate yourself long and strong passwords. I highly recommend 1Password. If you think you’re safe without a password manager (you’re not), then at least be smart about 2FA and turn that on wherever possible.

Note: Because this password is unsalted, I could have cracked it even faster (probably in less than a second) had I used a pre-computed rainbow table with every word on Wikipedia. But in the interest of this article, I wanted to demonstrate how easy a rule-based dictionary attack is. Honestly, every script kiddie and his mum can do it.

Delphi/Rust/Go developer. Ethereum consultant. Embarcadero MVP. Ex-Adobe, Macromedia. Helped build 1Password.