Entra ID Banned Password Lists: password spraying optimizations and defenses

Written by Matthieu Barjole - 17/04/2024 - in Pentest - Download

Banned Password Lists is a feature of the Password Protection component of Entra ID providing additional security for password-based authentication by enforcing both a global and a custom list of banned words for user passwords. This article aims at analyzing its implementation with the objectives to improve password spraying attacks for red team operators while providing better defenses for blue teams.

Introduction

Entra ID stands as a central component in Azure's security infrastructure, serving as a fundamental identifier for users, groups, and applications within the platform. Its role is critical in enabling robust authentication and authorization processes, ensuring seamless access control and permission management across Azure services. Integration with various security features such as Conditional Access and Multi-Factor Authentication enhances Azure's ability to defend against cyber threats effectively.

Entra ID Password Protection (EIPP) contributes to the overall security posture by providing additional security mechanisms around password-based authentication. One of them called "Banned Password Lists" (BPL) and introduced in October 2021, has been designed to prevent users from using weak passwords by enforcing both a global and a custom list of words to ban. The global BPL is maintained by Microsoft and kept secret, while the custom BPL is available in licensed tenants to configure more relevant words for the specific entity.

Entra ID Password Protection menu

This blogpost will cover the implementation and limitations of this security feature, the extraction of the secretly-kept global BPL through the analysis of on-premises deployments, and provide tools to improve the configuration of the custom BPL.

Implementation and limitations

Availability

According to the documentation:

The global banned password list is automatically applied to all users in a Microsoft Entra tenant. There is nothing to enable or configure, and cannot be disabled. This global banned password list is applied to users when they change or reset their own password through Microsoft Entra ID.

However, as several other security features, the BPL mechanism is subject to license requirements to be used at its full potential. By default, the global BPL is only applied to cloud identities and its customization or enforcement on AD DS requires an Entra ID P1 license (included in Microsoft 365 Security E3).

Licensing
Identities Global BPL Custom BPL
Cloud-only Free and enforced Entra ID P1/P2
Synchronized Entra ID P1/P2 Entra ID P1/P2

 

The custom BPL is limited to 1000 entries of 4 to 16 characters and this cannot be increased even with better licensing. Note that while Entra ID P1/P2 licenses are user-based, the BPL features are enabled globally as soon as one license is subscribed. Moreover, the on-premises deployment does not require identities to be synchronized with Azure and can be used as a standalone feature.

Algorithm

To evaluate password candidates, Entra ID first normalizes them by switching to lowercase and performing common substitutions:

private static readonly Dictionary<char, char> BannedPasswordEquivalentMapping = new Dictionary<char, char>
{
  { '0', 'o' },
  { '1', 'l' },
  { '|', 'l' },
  { '$', 's' },
  { '@', 'a' },
  { '!', 'l' },
  { '5', 's' },
  { 'i', 'l' },
  { '3', 'e' },
  { '2', 'z' }
};

Then, while the name "banned password list" may suggest a simple algorithm blocking password candidates as soon as a banned entry is matched, Entra ID actually implements a more permissive decisional process.

Instead, a score is calculated for each candidate depending on their length and a penalty is applied when a banned word is matched:

  • 1 point for each banned password found.
  • 1 point for each remaining character that is not part of a banned entry.
  • 5 points required to be accepted.

In addition to BPLs, a few others terms are banned such as the tenant name and the name / surname of the user. The algorithm uses both substring and fuzzy matching (Levenshtein distance of 1), but the latter is only performed on the entire candidate, thus limiting its purpose.

As a result, P@ss2024 is normalized as passzoz4 and if pass and 2024 are banned:

  • P@ss2024 = 2 points and is blocked.
  • P@ssw0rd2024 = 6 points and is authorized despite using 2 banned words.

This permissive algorithm combined with the limitation of 1000 entries in the custom BPL while not knowing the global BPL has proven to make the optimization of this protection difficult. Indeed, while 1000 entries may seem enough, organizations with thousands of users spread over the globe may require more entries to efficiently block common passwords from different populations of employees. In fact Microsoft themselves state:

It is not designed for blocking extremely large lists of passwords.

In this case, knowing the global BPL may help in optimizing the definition of the custom entries, but Microsoft keeps it secret:

Cyber-criminals also use similar strategies in their attacks to identify common weak passwords and variations. To improve security, Microsoft doesn't publish the contents of the global banned password list.

On the other hand indeed, knowing this global BPL could help us, as red team operators, to optimize password spraying attacks as this list is enforced on all Azure users. Besides, Microsoft also states this list is only coming from Azure telemetry and does not rely on any third-party data sources, which could therefore also be a good wordlist to spray other targets.

While our first idea was to test each password spraying candidate to determine its validity, the documentation of on-premises deployments caught our attention.

User clear-text passwords never leave the domain controller, either during password validation operations or at any other time.

This statement means domain controllers are somehow able to evaluate passwords against the global BPL locally.

On-premises deployment

Installation

Because domain controllers should not be able to access the internet, Microsoft designed the on-premises implementation with two components:

  • Proxy Service: installed on a server with internet access, authenticates itself to the Azure tenant to fetch the password policy including the global and custom BPLs.
  • DC Agent: installed on domain controllers, authenticates itself to the proxy service to request the password policy.
Entra ID Password Protection on-premises architecture

For the purpose of this research, both components were deployed on the domain controller. After the proxy service is installed, it must be registered to the Azure tenant using a global administrator.

PS > Import-Module AzureADPasswordProtection
PS > Register-AzureADPasswordProtectionProxy -AccountUpn 'globaladmin@corp.onmicrosoft.com' -AuthenticateUsingDeviceCode

Similarly, the Active Directory forest must be registered to the Azure tenant after the installation of the DC agent using a global administrator. The cmdlet must be run with Enterprise Admin privileges.

PS > Register-AzureADPasswordProtectionForest -AccountUpn 'globaladmin@corp.onmicrosoft.com' -AuthenticateUsingDeviceCode

During these registrations, CSRs are submitted to Azure and the issued certificates will later be used to encrypt and sign messages. These certificates are stored encrypted at C:\Program Files\Azure AD Password Protection Proxy\Data\*.ppfxe and are added as keyCredentials to the AADPasswordProtectionProxy service principal in the Azure tenant for authentication. From an offensive perspective, the presence of this service principal reveals the on-premises deployment of Entra ID Password Protection.

PS > ls 'C:\Program Files\Azure AD Password Protection Proxy\Data\20240411201352_24DE851D.ppfxe'
-a----  4/11/2024  8:13 PM  18663 20240411201352_24DE851D.ppfxe
$ az ad sp list --display-name AADPasswordProtectionProxy --query [].keyCredentials[]
[{
  "customKeyIdentifier": "32560AB812BEE0E5493A25725E4EE7B915C9322F",
  "displayName": "CN=AzureADPasswordProtectionProxy",
  "endDateTime": "2024-09-29T07:06:18Z",
  "key": null,
  "keyId": "f8970354-41ca-4d08-b798-5e29bfbf2c75",
  "startDateTime": "2024-04-11T20:08:52Z",
  "type": "AsymmetricX509Cert",
  "usage": "Verify"
}]

The registration process can be analyzed by configuring the proxy service with an HTTP proxy.

PS > cat 'C:\Program Files\Azure AD Password Protection Proxy\Service\AzureADPasswordProtectionProxy.exe.config'
<configuration>
   <system.net>
      <defaultProxy enabled="true">
      <proxy bypassonlocal="true"
         proxyaddress="http://proxy.corp.local:8080" />
      </defaultProxy>
   </system.net>
</configuration>

After registration, service connection points are created within the forest and the AzureADPasswordProtection folder is created in the SYSVOL share, containing encrypted configuration files (.cfge) and password policies (.ppe).

$ ldeep ldap -s 'dc.corp.local' -d 'corp.local' -k -u 'user' -b 'cn=Configuration,dc=CORP,dc=LOCAL' search '(objectClass=serviceConnectionPoint)' distinguishedName | jq -r .[].dn
CN=790FC8F1-1863-45D4-BA7E-5FC131970FDB,CN=Forest Certs,CN=Azure AD Password Protection,CN=Services,CN=Configuration,DC=CORP,DC=LOCAL
CN=Proxy Presence,CN=Azure AD Password Protection,CN=Services,CN=Configuration,DC=CORP,DC=LOCAL
PS > ls \\DC01\sysvol\CORP.LOCAL\AzureADPasswordProtection
d-----  4/11/2024  8:21 PM  Azure
d-----  4/11/2024  8:21 PM  Configuration
d-----  4/11/2024  8:21 PM  PasswordPolicies

PS > ls \\DC01\sysvol\CORP.LOCAL\AzureADPasswordProtection\Configuration
-a----  4/11/2024  8:21 PM    674 20240411202153_0D33020D.cfge

PS > ls \\DC01\sysvol\CORP.LOCAL\AzureADPasswordProtection\PasswordPolicies
-a----  4/11/2024  8:21 PM  30038 20240411202153_7760CF74.ppe

These elements may be used to identify an on-premises deployment of Entra ID Password Protection.

Global BPL extraction

The DC agent responsible for applying the password policy is a .NET service located at C:\Program Files\Azure AD Password Protection DC Agent\Rules\1.0.0.0.

PS > ls 'C:\Program Files\Azure AD Password Protection DC Agent\Rules\1.0.0.0'
Mode          LastWriteTime  Length Name
----          -------------  ------ ----
-a----  3/28/2022  10:14 PM   30088 DCAgentEvents.dll
-a----  3/28/2022  10:14 PM  176008 DCAgentServiceBL.dll
-a----  3/28/2022  10:14 PM   40840 Microsoft.AzureAD.Policy.Password.dll
-a----  3/28/2022  10:14 PM   26008 Microsoft.DeviceRegistration.BPLEntities.dll
-a----  3/28/2022  10:14 PM   54680 Microsoft.DeviceRegistration.JOSE.dll
-a----  3/28/2022  10:13 PM  663944 Newtonsoft.Json.dll
-a----  3/28/2022  10:14 PM  161176 ServiceCommon.dll
-a----  3/28/2022  10:14 PM   71560 ServiceCommonHelper.dll
-a----  3/26/2018   2:33 AM   89248 vcruntime140.dll

Decryption and parsing of the configuration files are implemented in the DCAgentServiceBL.dll binary.

namespace Microsoft.DCAgent.BL.ServiceComponents {

  internal class DomainEncryptor : ServiceComponent<DomainEncryptor>, [...] {

    public byte[] Encrypt(byte[] bytesToEncrypt) {
      return this._dpapiHelper.Encrypt(bytesToEncrypt);
    }

    private void BuildNcryptProtectionString() {
      LsaDnsDomainInfo lsaDnsDomainInfo = LsaPolicyDB.QueryDnsDomainInfo();
      string ncryptProtectionString = string.Format("SID={0}-516", lsaDnsDomainInfo.Sid);
      [...]
}}}

These files are encrypted using DPAPI and a protection descriptor set to the Domain Controllers group. From these routines, we developed the EIPPDecrypt tool to extract the configurations and password policies.

PS > cp 'C:\Program Files\Azure AD Password Protection DC Agent\Rules\1.0.0.0\ServiceCommon.dll' .
PS > cp 'C:\Program Files\Azure AD Password Protection DC Agent\Rules\1.0.0.0\ServiceCommonHelper.dll' .
PS > cp 'C:\Program Files\Azure AD Password Protection DC Agent\Rules\1.0.0.0\vcruntime140.dll' .

PS > .\EIPPDecrypt.exe 'C:\Program Files\Azure AD Password Protection Proxy\Data\20240411201352_24DE851D.ppfxe'
{"ProxyCertificate":"MII[...]Cg"}

PS > .\EIPPDecrypt.exe \\localhost\SYSVOL\CORP.LOCAL\AzureADPasswordProtection\Configuration\20240411222352_61143DAB.cfge
{
  "ConfigurationValues": {
    "0": "3600",
    "1": "120",
    "2": "3600",
    "3": "3600",
    "4": "3600",
    "5": "5",
    "6": "86400",
    "7": "3600",
    "8": "5"
  },
  "VersionMajor": 1,
  "VersionMinor": 0,
  [...]
}

PS > .\EIPPDecrypt.exe \\localhost\SYSVOL\CORP.LOCAL\AzureADPasswordProtection\PasswordPolicies\20240411222352_30364B88.ppe > policy.json

The decrypted policy contains both the global and custom BPLs and their timestamp.

$ jq -r .GlobalBPLTimestampUTC policy.json
2020-10-30T00:00:00Z

$ jq -r .GlobalBPL policy.json | tr '\t' '\n' | wc -l
3270

$ jq -r .GlobalBPL policy.json | tr '\t' '\n'
administracion123
administrador123
administrateur123
administrator123
administrator2022
administrator2023
password1
password12
password123
[...]

The global BPL contains a bit more than 3000 entries including a few duplicates because they are not normalized. While its timestamp suggests it has not been updated since October 2020, several entries containing 2022 or 2023 indicate a more recent update. Yet, these updates may be manually released by Microsoft, as there is still no entry containing 2024.

The complete list is available on our GitHub repository.

Custom BPL optimizations

In addition to the decryption helpers, the DC agent also implements the password evaluation algorithm in the Microsoft.AzureAD.Policy.Password.dll binary.

namespace Microsoft.AzureAD.Policy.Password {

  public enum PasswordStatus {
    Valid,
    BannedByGlobalList,
    BannedByTenantList,
    BannedByAdditionalWords,
    BannedByGlobalAndTenantList
  }

  public enum BannedPasswordSources {
    None = 0,
    Global = 1,
    Tenant = 2
  }

  public enum BannedPasswordAlgorithm {
    None = 0,
    EditDistance = 1,
    Substring = 2,
    StrengthScore = 4,
    All = 7
  }

  public class BannedPasswordChecker {
    public PasswordStatus CheckPassword(
      Guid tenantId, string password, IEnumerable<string> additionalBannedWords,
      out IDictionary<string, BannedPasswordSources> matchedBannedWords,
      out BannedPasswordAlgorithm matchAlgorithm))
      [...]
  }
}

Using these functions, an additional tool was developed to assist auditors and administrators in the generation of an optimized custom BPL from a list of passwords compromised through offline dictionary attacks, while taking into account the global BPL. The list of passwords is first normalized and split into substrings of 4 to 16 characters. This new list is then used as a custom BPL to evaluate the initial list of compromised passwords and assess the efficiency of each BPL entry. The 1000 substrings having the more matches (BannedPasswordSources.Tenant) while not already being part of the global BPL are then selected.

PS > .\EIPP.exe generate passwords.txt global.txt custom.txt

Once the custom BPL is generated, its efficiency can be assessed using the same tool.

PS > .\EIPP.exe stats passwords.txt global.txt custom.txt
BannedByGlobalAndTenantList,StrengthScore,Company2024!,companyzozel
[...]
Results: 37% banned

This generation algorithm was tested on lists of 10k passwords compromised during password audits. Yet, on such large lists including various populations of users, the ban rate could only reach 40% due to the limit of 1000 entries. However, because this evaluation is performed against passwords compromised using offline attacks, this rate is still considered sufficient to block most password spraying attempts, which EIPP is mainly aiming at.

Conclusion

Entra ID Password Protection is a native solution to implement better password policies by "banning" common words for cloud identities. With a single Entra ID P1 license (5$) and without requiring identities synchronization, this protection can be extended to on-premises identities, allowing organizations to augment Active Directory policies which do not natively support ban lists.

However, the limitations of the BPL implementation make it difficult for large organizations to exploit its full potential. By extracting the global BPL and providing this tooling, we aim at helping blue teams to define more relevant custom BPL while providing insights to red team operators to optimize password spraying attacks by avoiding unnecessary attempts.

Apart from relying on other identity providers, alternative solutions may also be considered for on-premises identities using the Password Filters API. Lithnet Password Protection or PassFiltEx are popular implementations but should follow a thorough review as they are provided by third-parties and not officially supported by Microsoft.

The tools and global BPL are available at our GitHub.