Active Directory Password Enforcement

The Problem

I work in a pentest team and when we compromise a domain controller we dump user hashes, run them through hashcat, and generate a password analysis report. The report includes statistics on cracked passwords, some charts, and recommendations on creating stronger passwords. These recommendations include minimum character lengths and avoiding dictionary words or common passwords. Unfortunately, Active Directory does not provide robust password filtering. Administrators are essentially limited to setting the minimum password length and enforcing character composition requirements.

Available password settings

Solutions

Microsoft provides an interface for creating a password filter DLL that checks proposed passwords before they are set. A custom filter DLL can choose to accept the new password or deny it. To implement this functionality, you can pay for a third party solution, find an open source project, or write your own filter DLL. A third party solution is likely your best bet as you don't want to risk introducing a buggy DLL into lsass on your domain controllers.

Another possibility is to periodically dump account hashes and run them through a password cracker. User's with cracked passwords can have a forced password change next time they login.

Custom Filtering

Let's say we want to write our own password filter DLL, what would it look like? At a high level, it will take a proposed password and do some checks to make sure it is unlikely to be guessed by an attacker. This might include checking for inclusion in a dictionary or in real world password breaches. haveibeenpwned maintains an excellent list of breached passwords for such use.

Additionally, you might feed the password off to a cracker for a quick run. This is probably not practical for most shops that may have limited resources and/or high password turnover. It can also introduce significant delay to the password changing process. Let's stick with something simpler by implementing a few quick checks and looking for inclusion in a password list.

NIST has published guidelines for passwords that include an 8 character minimum, no commonly used values (dictionary, overly simple, part of a breach), and it specifically mentions not to enforce character composition requirements. The reasoning being that many users satisfy this requirement in predictable ways, such as capitalizing the first letter and appending a digit or special character to the end. Although not harmful, it isn't particularly helpful.

The NIST guidelines also recommend against periodic password expiration unless there is evidence of compromise. A strong password should not be guessable in any reasonable amount of time.

With all this in mind, lets get started writing our filter DLL. We will need to implement the 3 required password filter functions: InitializeChangeNotify, PasswordChangeNotify, and PasswordFilter.

This simple filter DLL checks for a minimum character length of 8 and prohibits common passwords from a list. Compile the DLL and ensure the 3 required exports are present.

Verifying our 3 exports

The filter DLL looks good so let's install it according to Microsoft's instructions. Copy the DLL to %WINDIR%\System32 and add the module name minus the .dll extension to the "Notification Packages" key. You will also need to make sure complexity is enabled in your password policy otherwise the filter DLL will not be called. Once a password has met the password policy criteria and if complexity is enabled, password filters are then checked.

Modify registry to add filter DLL

Now, reboot your domain controller and check that PasswordFilter.dll is loaded into lsass.

Use Sysinternals listdlls.exe to make sure it's loaded

Now let's attempt a password change by providing a known banned password, followed by a compliant password.

"Changeme5?" is successfully filtered

Final Thoughts

The provided code is only meant to provide a starting point and a much more robust solution should be used in an actual AD environment. Ideally, the filter DLL would offload much of its functionality to another service since you don't want to risk introducing potentially buggy code to lsass. This could lead to instability in your domain controllers causing them to reboot.

If you find your filter DLL is causing issues, you can move it while it's loaded and replace it with a new version. A reboot is required to load the new DLL. Do not test custom filter DLLs on a production domain controller!

A Visual Studio project with source is available on github:
https://github.com/UserExistsError/PasswordFilterDll