Say No to Sh!tty Passwords

A couple of months ago, I shared some password policy advice as one of my Five Easy Security Wins in Windows. Changing your password policy to a 365-day expiration and 15-character minimum is easy and far more secure than shorter password requirements, but it isn't impossible for people to still set bad passwords. Without a password checker in place, users can still set truly terrible passwords for their Windows logins. I needed to change that in my environment, and in my search for just the right tool, I found Lithnet Password Protection (LPP) for Active Directory. I'll share some background info about why implementing LPP was necessary, and give a quick run-down on how to set it up on Domain Controllers.


Shitty Passwords Everywhere

I have seen no shortage of truly awful passwords in my career as a sysadmin. For whatever reason, end users seem convinced that they need to tell IT what their passwords are if they need assistance. I have never really wanted to know people's passwords, yet they've been needlessly shared with me anyway. Password length and complexity best practices have changed a lot since I started my career, but one thing that has not changed is that most end users will put in the least amount of effort possible to set a password.

My personal hall of shame from my end users over the years will make any sysadmin worth half their weight in salt cringe. With 8, 10, and 12 character minimums with no password checking, these cold-sweat nightmares happened:

  • Summer123
  • Lake1982
  • Password1234
  • Office2020
  • Spring2021!!

I'm sure I've seen even worse examples that I have tried to repress for the sake of my sanity. Those incredibly bad passwords all flew under the radar because technically they met the Windows password complexity requirements in place: at least one character from three of four character sets: a-z, A-Z, 0-9, and non-alphanumerics. The problem with complexity requirements is that users will stick to predictable patterns--typically a single word followed by enough numbers or other padding characters to fit the length requirement. 

Implementing a password checker on every domain controller might sound a little daunting, but it's actually fairly easy and you can't beat the security payoff. The days of trying to explain why "Mommy2017" is a bad password are over.


Implementing Lithnet Password Protection

Before you start to set up Lithnet, take my advice and go get yourself a cup of coffee/tea/hot cocoa/water/beer/wine/whiskey because the initial setup process can take over an hour. Lithnet provides clear, straightforward instructions on how to set up LPP, but there are some prerequisites that they don't walk through.

Step 0: Be aware that you will need to install Lithnet on all of your Domain Controllers, not just one. You will also need to decide how you want to set up the data store, but I'll echo Lithnet's own recommendation to go with the DFS replication option. Download Have I Been Pwned's data store of NTLM passwords ordered by hash and extract it somewhere convenient, like on the DFS master DC you'll pick in step 1. You won't do anything except unpack it until step 5, so sit tight.

Step 1: From Server Management on each DC, install the DFS Replication role. Pick one of your DCs to be the master of the DFS share, and install the DFS Namespaces role alongside the DFS Replication role. Once the roles are installed, it's a good idea to perform a rolling reboot on the DCs.

Step 2: From the DFS Management console on the DFS master DC, you'll create a new namespace. The wizard will walk you through each step. By default, it will place namespaces in C:\DFSRoots, so your namespace will be something like C:\DFSRoots\Lithnet.

Step 3: When you add the other DCs to the replication group, you will be able to set a location on each for the DFS replication folder. I would strongly recommend setting it up in the same location on each DC for the sake of simplicity and sanity.

Step 4: It's finally time to start setting up LPP. Install the program on each DC and perform a rolling reboot. Passwords will not be filtered until the data stores are set up and a Group Policy Object is configured.

Step 5: On the DFS master DC, open an elevated PowerShell window and run the cmdlets from Part 2 of Lithnet's guide:

  • Import-Module LithnetPasswordProtection
  • Open-Store 'C:\DFSRoots\Lithnet'
  • Import-CompromisedPasswordHashes -Filename 'C:\yourPath\pwnedpasswords.txt'

This step is the real time-consuming bit, but it is thankfully hands-off. Even with flash storage and decent memory, my master DC took nearly an hour to convert the NTLM hashes into binary. Go refresh your drink, play through Chimney Alfonzo a few times, or spend some time creating a simple txt file of additional words you want to ban. Once the conversion is complete, allow some time for the other DCs to replicate the data store.

Step 6: While waiting on the replication process, you can start to build a GPO--just don't apply it to anything yet. I've provided an example of a test GPO. If you want to test a ton of password changes on a single account, set the minimum password age to 0 days. (When making your real policy, never set the minimum age to 0.)

Step 7: Apply your test GPO to a computer OU you use for testing. If you don't already have one, create a test user account (my go-to is named Hellen Skelter, which is significantly more clever than Testy McTestface in my opinion). Run gpupdate /force on a test computer, then start experimenting with password changes. When you are satisfied with Lithnet's ability to block compromised passwords, add the Group Policy preferences above into whichever GPO houses your password policy--likely your Default Domain Policy. 

And that's it for setup! Once Group Policy refreshes on all of your endpoints (this can take 30-90 minutes by default, unless you run gpupdate /force on everything), users will no longer be able to set their passwords to anything in your banned words list or the compromised passwords list. If the process of checking new passwords against the data store adds any time to changing a password, I haven't noticed it. 


Additional Tips for Password Policy Changes

Simply blocking compromised/banned passwords isn't best practice on its own. There are some other things you'll need to do before applying any new password policy to your domain:

  • Write up a formal policy about password requirements for your company
  • Inform users ahead of time about when and how the password policy is changing
  • Don't force a password change on unexpired passwords if they haven't been exposed
  • Provide guides and resources on how to create stronger passwords

In my experience, users do not take to password policy changes very well. I'm pleased to report, though, that no one has complained directly to me since I informed them that their longer passwords will be good for an entire year. Getting to keep a password for longer has seemed to be a fair tradeoff for requiring a few extra characters.

If you implement Lithnet, or something like it in your environment, tell us about it in the comments!