Detecting compromised passwords
Yes, dear hearts, the bad guys do have extensive dictionaries of passwords that either are commonly used or have been compromised. One of the most effective ways of brute-forcing passwords is to use these dictionaries to perform a dictionary attack. This is when the password-cracking tool reads in passwords from a specified dictionary and tries each one until either the list has been exhausted, or until the attack is successful. So, how do you know if your password is on one of those lists? Easy. Just use one of the online services that will check your password for you. One popular site is Have I Been Pwned?, which you can see here:
All you really have to do is to type in your password, and the service will show if it's on any lists of compromised passwords. But think about it. Do you really want to send your production password to somebody's website? Yeah, I thought not. Instead, let's just send a hash value of the password. Better yet, let's just send enough of the hash to allow the site to find the password in its database, but not so much that they can figure out what your exact password is. We'll do that by using the Have I Been Pwned? Application Programming Interface (API).
To demonstrate the basic principle, let's use curl, along with the API, to see a list of password hashes that have 21BD1 as part of their values. (You can do this on any of your virtual machines. I'll just do it on the Fedora workstation that I'm currently using to type this.) Just run this:
curl https://api.pwnedpasswords.com/range/21BD1
You're going to get a lot of output like this, so I'll just show the first few lines:
[donnie@fedora-teaching ~]$ curl https://api.pwnedpasswords.com/range/21BD1
0018A45C4D1DEF81644B54AB7F969B88D65:1
00D4F6E8FA6EECAD2A3AA415EEC418D38EC:2
011053FD0102E94D6AE2F8B83D76FAF94F6:1
012A7CA357541F0AC487871FEEC1891C49C:2
0136E006E24E7D152139815FB0FC6A50B15:3
01A85766CD276B17DE6DA022AA3CADAC3CE:3
024067E46835A540D6454DF5D1764F6AA63:3
02551CADE5DDB7F0819C22BFBAAC6705182:1
025B243055753383B479EF34B44B562701D:2
02A56D549B5929D7CD58EEFA97BFA3DDDB3:8
02F1C470B30D5DDFF9E914B90D35AB7A38F:3
03052B53A891BDEA802D11691B9748C12DC:6
. . .
. . .
Let's pipe this into wc -l, a handy counting utility, to see how many matching results we've found:
[donnie@fedora-teaching ~]$ curl https://api.pwnedpasswords.com/range/21BD1 | wc -l
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 20592 0 20592 0 0 197k 0 --:--:-- --:--:-- --:--:-- 199k
526
[donnie@fedora-teaching ~]$
According to this, we've found 526 matches. But that's not very useful, so let's fancy things up just a bit. We'll do that by creating the pwnedpasswords.sh shell script, which looks like this:
#!/bin/bash
candidate_password=$1
echo "Candidate password: $candidate_password"
full_hash=$(echo -n $candidate_password | sha1sum | awk '{print substr($1, 0, 32)}')
prefix=$(echo $full_hash | awk '{print substr($1, 0, 5)}')
suffix=$(echo $full_hash | awk '{print substr($1, 6, 26)}')
if curl https://api.pwnedpasswords.com/range/$prefix | grep -i $suffix;
then echo "Candidate password is compromised";
else echo "Candidate password is OK for use";
fi
Okay, I can't try to turn you into a shell scripting guru at the moment, but here's the simplified explanation:
- candidate_password=$1: This requires you to enter the password that you want to check when you invoke the script.
- full_hash= , prefix=, suffix=: These lines calculate the SHA1 hash value of the password, and then extract just the portions of the hash that we want to send to the password-checking service.
- if curl: We wrap up with an if..then..else structure that sends the selected portions of the password hash to the checking service, and then tells us whether or not the password has been compromised.
After saving the file, add the executable privilege for the user, like so:
chmod u+x pwnedpasswords.sh
Now, let's see if TurkeyLips, my all-time favorite password, has been compromised:
[donnie@fedora-teaching ~]$ ./pwnedpasswords.sh TurkeyLips
Candidate password: TurkeyLips
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 09FDEDF4CA44D6B432645D6C1D3A8D4A16BD:2
100 21483 0 21483 0 0 107k 0 --:--:-- --:--:-- --:--:-- 107k
Candidate password is compromised
[donnie@fedora-teaching ~]$
Yeah, it's been compromised, all right. So, I reckon that I don't want to use that for a production password.
Now, let's try it again, except with a random two-digit number tacked on at the end:
[donnie@fedora-teaching ~]$ ./pwnedpasswords.sh TurkeyLips98
Candidate password: TurkeyLips98
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 20790 0 20790 0 0 110k 0 --:--:-- --:--:-- --:--:-- 110k
Candidate password is OK for use
[donnie@fedora-teaching ~]$
Well, it says that this one is okay. Still though, you probably don't want to use such a simple permutation of a password that's known to have been compromised.
If you're interested in security solutions for your Internet of Things devices, you can check them out here:
https://www.vdoo.com/
Full disclosure: the VDOO company is one of my clients.
Now, having said all of this, I still need to remind you that a passphrase is still better than a password. Not only is a passphrase harder to crack, it's also much less likely to be on anyone's list of compromised credentials.