Checking for Breached Passwords in Active Directory – Using k-Anonymity!
I’d like to preface this post by saying that I 100% understand concerns about using an external API, even when sending it just a small amount of unusable information. The possibility of compromise and subsequent infection on Domain Controllers is a true security risk and it is totally acceptable to not want to take that risk. For those not wishing to use an external API at all, I wrote an original post on checking breached passwords with AD, that works entirely offline with downloaded hashes of Troy Hunt’s Pwned Passwords – you can read about that project here.
Last year Troy Hunt released a freely searchable database of previously breached passwords. The available data was pretty much a godsend for anyone looking to verify that their users aren’t using breached passwords, with the ability to query for passwords through plaintext or as SHA1 hashes via API, as well as the ability to download the entire data set to use offline. A little after the release of this information, I wrote a DLL for use in Active Directory environments. However, that tool required users to download the massive lists of passwords so that it could be queried over the network. While it requires no external service interaction, it still requires downloading and searching through gigabytes of files.
Utilising and Understanding k-Anonymity
Troy’s latest update to Pwned Passwords includes way more passwords and, in conjunction with Cloudflare, is the use of k-Anonymity. In essence, you can now search the database by range – using the beginning of an SHA1 hash, then using the API response to check whether the rest of the hash exists in the database. This is a far more secure method than transmitting the entire hash – an attacker will only ever gain access to the beginning of a hash (essentially useless!), and any validation of the remaining part of the hash will be done by an external tool.
As an example, let’s use the password hunter2. The SHA1 hash of hunter2 is f3bbbd66a63d4bf1747940578ec3d0103530e21d. Using a range query, you would query the Pwned Passwords API with the first 5 characters of the hash (https://api.pwnedpasswords.com/range/f3bbb). Notice that the external service will only receive those first five characters. The response from the API is the remaining hash characters for all hashes beginning with f3bbb. Now you would search yourself for the remaining suffix, finding that it does exist in the data set (and that it has appeared in breaches a whopping 16902 times!)
As mentioned at the start of the post, I’m sure there will always be some absolutely valid arguments to not using an external API, for example if Troy’s site itself is hacked, however it makes for a far less resource intensive querying method than having to quickly search files of many gigabytes.
Integrating With Active Directory
Integrating k-Anonymity with AD is essentially the same as I outlined in my first post. The only difference in the code is the use of libCurl to send a request and receive an API response. Essentially, a user attempts to change their AD password, this is request is received by the LSA service on a domain controller. This request is then forwarded to the DLL I have written. The DLL will receive the password, convert it to an SHA1 hash, and then query the API. The API response is then stored as a string, which is then searched through to find the remaining hash suffix. A response of either TRUE or FALSE is then sent back to LSA to return to the user (TRUE if the user is allowed to use the prospective password, FALSE if not). Again, you can read more about the process in my original post linked above.
Compiling The Code
The code is reliant on the Crypto++ library in order to convert the calling password to a SHA1 hash, as well as libCurl for sending a request and receiving a response. 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-API 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
VC++ Directories. Edit the Include Directories and add the
- Then, edit the Library Directories and add the
Debugdirectory from the
cryptlib.libto your Additional Dependencies list under
- Build libCurl as a library in x64 mode – this is a great resource from Github that will build libcurl on Windows
- Include the libCurl header directories through
VC++ Directories. Edit the Include Directories and add the
- Then, edit the Library Directories and add the relevant library directory.
libcurl_a.libto your Additional Dependencies list under
CURL_STATICLIBto your Preprocessor Definitions under
- Change Runtime Library to
Multi-threaded Debug (/MTd)under
- All that’s left now is to Build and then test out the DLL!
Alternatively, you can get a Release from Github: https://github.com/JacksonVD/PwnedPasswordsDLL-API/releases/
Implementing the DLL
The implementation of the DLL is the easy part – just download or build the DLL, place it in system32 and add a registry key!
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.
- Download or build the DLL
- The DLL itself needs to be placed in your system root directory (generally C:\Windows\System32).
- The DLL name needs to be added to the multi-string “Notification Packages” registry subkey under
HKLM\System\CurrentControlSet\Control\LSA– note that you only need to add the name of the DLL, not including the file extension.
- To ensure that the DLL works alongside your Group Policy password filtering settings, ensure that the Passwords must meet complexity requirements policy setting is enabled through your relevant GPO(s).
- Reboot the DC(s). Any password change request should now be filtered through the DLL.