Building a Custom Active Directory Password Filter: Inside OpenPasswordFilter’s LSASS Integration
Hook
Any domain of significant size has users running Password1 or Summer2015. Microsoft gives you the hooks to stop this—but almost nobody uses them because commercial solutions hide behind “call for pricing” walls.
Context
Active Directory’s weakest link isn’t configuration drift or patch management—it’s the inevitable statistical certainty that someone in your organization chose “Company123” as their password. Penetration testers exploit this reality ruthlessly: enumerate usernames via LDAP or SMB, spray common passwords, and escalate from there. Microsoft actually provides a native mechanism to prevent this through custom password filter DLLs that LSASS loads at boot time. When users attempt password changes, these DLLs can inspect and reject passwords before they’re committed to the directory. The catch? Commercial implementations of password filters typically require enterprise licensing that smaller organizations can’t justify, leaving them vulnerable to the most pedestrian attack vector in the book.
OpenPasswordFilter emerged from this gap as an open-source alternative that implements dictionary-based password rejection using Windows’ native password filter infrastructure. It’s explicitly designed for organizations that have been burned during penetration tests by users choosing obvious passwords but can’t afford commercial security products. The tool provides two wordlist modes: exact matching for blocking specific passwords like “Password1” and substring matching for rejecting any password containing poison strings like “password” or “welcome.” This dual approach lets administrators block both known-bad passwords and patterns that users might try to disguise with leetspeak.
Technical Insight
OpenPasswordFilter’s architecture splits password filtering across two components to solve a critical operational problem: LSASS loads DLLs at boot time and doesn’t reload them, meaning any in-DLL password list would require domain controller reboots to update. Instead, OpenPasswordFilter.dll acts as a thin client that forwards password validation requests over the loopback interface to OPFService.exe, a C# Windows service that maintains in-memory dictionaries and can be restarted independently.
The deployment requires registering the filter DLL with Windows through a registry key that LSASS reads during initialization:
HKLM\SYSTEM\CurrentControlSet\Control\Lsa\Notification Packages
You add “OpenPasswordFilter” (without the .dll extension) to this key, then copy the DLL to System32. When LSASS loads the DLL, Windows calls its exported functions following the password filter contract defined in Microsoft’s documentation. The DLL communicates with the service to check passwords, returning TRUE or FALSE to approve or reject them.
Rather than evaluating passwords directly, the DLL opens a network connection to localhost where OPFService.exe listens. The service loads two text files at startup: opfmatch.txt for exact string matching and opfcont.txt for substring searches. This design enables sophisticated blocking strategies. For example, if opfcont.txt contains “password”, the service can reject variations like “P@ssw0rd123” that contain the forbidden substring.
The README suggests a practical workflow for generating comprehensive wordlists using hashcat’s mangling rules:
hashcat -r /usr/share/hashcat/rules/Incisive-leetspeak.rule --stdout seedwordlist | tr A-Z a-z | sort | uniq > opfcont.txt
This command takes seed words (company names, industry terms, common bases like “welcome” or “password”) and generates leetspeak variations, producing entries like “p@ssw0rd”, “passw0rd”, and “pa$$word” that users might attempt. The lowercase normalization and deduplication ensure the service loads a clean dictionary.
The service itself is registered as a boot-start Windows service, ensuring it’s available before LSASS might call the filter DLL:
sc create OPF binPath= "<full path>\opfservice.exe" start= boot
This boot-time startup is critical—if the service isn’t running when a password change occurs, the DLL’s network request may fail and password validation could break. The architecture accepts this in exchange for hot-reloadable wordlists: administrators can edit opfmatch.txt or opfcont.txt and restart just the OPF service without touching the domain controller’s uptime.
One architectural detail worth noting: the filter must be deployed to every domain controller, because any DC might service a password change request. This creates operational overhead but is unavoidable given how Active Directory distributes authentication operations.
Gotcha
OpenPasswordFilter carries significant caveats that the README addresses honestly. The project is marked “very ALPHA” with limited production testing, and the author explicitly warns that “your mileage may vary” and recommends testing “in a safe location before using this in real life.” This isn’t enterprise-ready software with vendor support—it’s a community tool that requires internal validation before production deployment. Organizations with strict change management or compliance requirements may struggle to justify running alpha-quality code in their authentication path.
The split architecture introduces potential failure modes that in-process filtering avoids. If OPFService.exe crashes or becomes unresponsive, password changes may behave unpredictably, though the README doesn’t document the DLL’s specific error handling behavior. The loopback network communication may add some latency to password change operations, though the performance impact is not documented.
Deployment friction comes from unexpected places. Wordlists generated on Unix systems require line-ending conversion with unix2dos, or the service fails to start with errors written to the Application event log. The project requires .NET 3.5, which must be explicitly installed on modern Windows Server versions. While installers are provided (with wordlists derived from rockyou.txt), administrators who want custom dictionaries must manually wrangle these format and dependency issues. The multi-DC deployment requirement also means maintaining consistency across servers—updating the wordlist on one DC but forgetting another creates inconsistent password policies that users will find confusing.
Verdict
Use OpenPasswordFilter if you’re protecting an Active Directory domain against dictionary password attacks and can’t justify commercial password filter costs. It’s particularly valuable if penetration tests have repeatedly caught users with Password1 or SeasonYear patterns, and you need enforcement that users can’t bypass through client-side tools. The substring matching with hashcat-generated leetspeak variations provides sophisticated blocking for common password mangling attempts. Just plan for thorough testing in a non-production domain first—the README recommends testing “in a safe location before using this in real life.” Spin up a test DC, load sample wordlists, and verify the service handles your specific Active Directory configuration before touching production authentication systems. Skip OpenPasswordFilter if you need vendor support or have compliance frameworks requiring production-grade software with defined SLAs. Also reconsider if your environment requires frequent wordlist updates or you lack the engineering capacity to troubleshoot alpha-quality code when something breaks. The README acknowledges this is “very ALPHA” software, so ensure you have the technical resources to support it before deployment.