Edit: I have now overhauled the blog post and essentially recreated PwnedPasswordsDLL to run on-premises, and return results very quickly. Information regarding set-up and the new release can be found below. Changes have now been pushed to GitHub and are available for use.

Introduction –

In simplistic terms, PwnedPasswordsDLL will check a requested Active Direvtory password change against a local store of over 330 million password hashes. If the hash is found in the breached passwords, the requesting password is rejected. This entire process takes ~1 second against over 330 million previously breached password hashes.

Now on to a more technical explanation.

A little known setting through LSASS on Windows PCs is the ability to call a custom filter DLL when a password change request is received. The utilisation of this resource was commonplace back in the Windows 2000 days, but it’s now seldom used; many sysadmins are happy enough using totally third-party solutions or the in-built Group Policy settings.

The concept of a 100% customisable password filter intrigued me, and with Troy Hunt’s new freely searchable database of pwned passwords, I decided to look at setting up a filter DLL to call a local store of the breached passwords to check the prospective password change.

I discovered that writing such a DLL was a very simple process, most of the actual handling of the DLL is done internally, all that needs to be done by an author is to create a function that returns a Boolean expression based around whether or not conditions are met for the requesting password. The result of my research is PwnedPasswordsDLL.

In calling the DLL, the requested password is converted to an SHA1 hash through the Crypto++ library. This SHA1 hash is then passed to a binary search algorithm I wrote that searches the three HaveIBeenPwned breached passwords text files. If the hash is found, a false boolean is returned (i.e. telling LSA that the calling password failed to meet the required password policy).

If you’re willing to give it a whirl, you can find the code here.

If you want to compile for yourself, check out the Compiling the Code section below. If you want to download a Release version, look at the Implementing the DLL section below.

Here is an example of the DLL in use –





Compiling the Code –

The code is heavily reliant on the Crypto++ library in order to convert the calling password to a SHA1 hash. I have also only tested the code on x64 architecture, so I’m not sure if it will even work on 32-bit systems.

Compiling the code is quite simple in Visual Studio –

Download the PwnedPasswordsDLL source from here Download Crypto++ from the following link Build Crypto++ as a library in x64 mode – the following link is a good resource on compiling it for use in Visual Studio Include the Crypto++ header directories through Project –> PwnedPasswordsDLL Properties –> Configuration Properties –> VC++ Directories . Edit the Include Directories and add the include directory Then, edit the Library Directories and add the Debug directory from the x64\Output directory. Add to your Additional Dependencies list under Project –> PwnedPasswordsDLL Properties –> Configuration Properties –> Linker –> Input –> Additional Dependencies Change Runtime Library to Multi-threaded Debug (/MTd) under Project –> PwnedPasswordsDLL Properties –> Configuration Properties –> C/C++ –> Code Generation All that’s left now is to Build and then test out the DLL!

Implementing the DLL –

The implementation of the DLL is the easy part, save for downloading some rather large text files – whether you’ve compiled the code yourself or downloaded a release, the implementation process is the same.

Note: These instructions need to be followed on all Domain Controllers in the domain if you wish to implement this for Active Directory, as any of them may end up servicing a password change request.

As the solution is entirely on-premises, you need to download the 3 breached passwords zip files from HaveIBeenPwned and extract the plain-text documents to the C drive (the file path is customisable if you compile the code yourself, but not if you download the Release). Note that you don’t have to do this for all Domain Controllers if you set up the DLL to read from a network share before compiling.