Data/AuditChecks/ADPasswordPolicyChecks.json

{
  "categoryId": "adpwd",
  "categoryName": "AD Password & Lockout Policies",
  "categoryDescription": "Checks related to domain password policies, fine-grained password policies, account lockout configuration, password hygiene, LAPS deployment, and encryption key management",
  "checks": [
    {
      "id": "ADPWD-001",
      "name": "Default Domain Password Policy",
      "description": "The Default Domain Policy defines the baseline password and lockout settings for all domain users. This policy should enforce strong password requirements as it applies to every account not covered by a more specific fine-grained password policy",
      "severity": "High",
      "subcategory": "Password Policy",
      "recommendedValue": "Minimum length 14 characters, complexity enabled, maximum age 365 days, minimum age 1 day, history 24 passwords",
      "remediationSteps": "Review the Default Domain Policy using Get-ADDefaultDomainPasswordPolicy. Configure via Group Policy Management: Default Domain Policy > Computer Configuration > Policies > Windows Settings > Security Settings > Account Policies > Password Policy. Set values per organizational security requirements",
      "compliance": {
        "nistSp80053": ["IA-5(1)"],
        "mitreAttack": ["T1110.001", "T1110.003"],
        "cisBenchmark": ["1.1.1", "1.1.2", "1.1.3", "1.1.4", "1.1.5"],
        "anssi": ["R34"],
        "cisAd": ["5.1.1"]
      }
    },
    {
      "id": "ADPWD-002",
      "name": "Fine-Grained Password Policy Enumeration",
      "description": "Fine-grained password policies (FGPPs) allow different password requirements for different groups of users. All FGPPs should be documented to understand the complete password policy landscape and ensure no groups are covered by weaker-than-intended policies",
      "severity": "Medium",
      "subcategory": "Password Policy",
      "recommendedValue": "All FGPPs documented with their precedence, target groups, and policy settings. At minimum, a strict FGPP for privileged accounts and a standard FGPP for regular users",
      "remediationSteps": "Enumerate all FGPPs using Get-ADFineGrainedPasswordPolicy -Filter *. Document each policy's precedence value, target groups, and settings. Verify that privileged accounts are covered by a stricter policy than standard users. Create FGPPs if only the Default Domain Policy exists",
      "compliance": {
        "nistSp80053": ["IA-5(1)", "AC-2"],
        "mitreAttack": ["T1110.001"],
        "anssi": ["R34"],
        "cisAd": ["5.1.2"]
      }
    },
    {
      "id": "ADPWD-003",
      "name": "FGPP Application Analysis",
      "description": "Fine-grained password policies must be applied to the correct groups to be effective. Misconfigured FGPP application can leave high-value accounts under weaker policies or create policy gaps where no FGPP applies and the Default Domain Policy is used instead",
      "severity": "Medium",
      "subcategory": "Password Policy",
      "recommendedValue": "All privileged accounts covered by a strict FGPP. No policy gaps where high-value accounts fall back to a weaker default policy",
      "remediationSteps": "For each FGPP, review the msDS-PSOAppliesTo attribute to see target groups. Cross-reference with privileged group membership to verify coverage. Use Get-ADUserResultantPasswordPolicy for specific accounts to determine the effective policy. Fix any gaps in FGPP application",
      "compliance": {
        "nistSp80053": ["IA-5(1)", "AC-2"],
        "mitreAttack": ["T1110.001", "T1078.002"],
        "cisAd": ["5.1.3"]
      }
    },
    {
      "id": "ADPWD-004",
      "name": "Minimum Password Length",
      "description": "Passwords below 14 characters are vulnerable to offline brute-force cracking with modern GPU hardware. NIST SP 800-63B recommends supporting passwords up to 64 characters and setting a minimum that balances security with usability. For AD environments, 14 characters is the minimum recommended baseline",
      "severity": "High",
      "subcategory": "Password Policy",
      "recommendedValue": "Minimum 14 characters for standard users, 25 characters for privileged accounts",
      "remediationSteps": "Configure minimum password length in the Default Domain Policy or appropriate FGPP: Computer Configuration > Policies > Windows Settings > Security Settings > Account Policies > Password Policy > Minimum password length = 14. Create a stricter FGPP for privileged accounts requiring 25+ characters",
      "compliance": {
        "nistSp80053": ["IA-5(1)"],
        "mitreAttack": ["T1110.001", "T1110.003"],
        "cisBenchmark": ["1.1.4"],
        "anssi": ["R34"],
        "cisAd": ["5.2.1"]
      }
    },
    {
      "id": "ADPWD-005",
      "name": "Password Complexity Requirement",
      "description": "Windows password complexity requires at least three of four character categories (uppercase, lowercase, digits, special characters) and that the password does not contain the user's account name. While NIST no longer mandates complexity, disabling it in AD without compensating controls (such as banned word lists) significantly weakens passwords",
      "severity": "High",
      "subcategory": "Password Policy",
      "recommendedValue": "Complexity enabled. Ideally supplemented with Azure AD Password Protection or custom banned word lists for defense against common patterns",
      "remediationSteps": "Verify complexity is enabled in the Default Domain Policy: Computer Configuration > Policies > Windows Settings > Security Settings > Account Policies > Password Policy > 'Password must meet complexity requirements' = Enabled. Consider deploying Azure AD Password Protection for additional banned password enforcement",
      "compliance": {
        "nistSp80053": ["IA-5(1)"],
        "mitreAttack": ["T1110.001", "T1110.003"],
        "cisBenchmark": ["1.1.5"],
        "anssi": ["R34"],
        "cisAd": ["5.2.2"]
      }
    },
    {
      "id": "ADPWD-006",
      "name": "Account Lockout Policy",
      "description": "Account lockout policies protect against online brute-force and password spraying attacks by locking accounts after a threshold of failed attempts. Without lockout, attackers can attempt unlimited password guesses against any account. However, overly aggressive lockout creates denial-of-service risk",
      "severity": "High",
      "subcategory": "Lockout Policy",
      "recommendedValue": "Account lockout threshold: 5-10 attempts. Lockout duration: 15-30 minutes. Reset counter after: 15-30 minutes",
      "remediationSteps": "Configure account lockout in Default Domain Policy: Computer Configuration > Policies > Windows Settings > Security Settings > Account Policies > Account Lockout Policy. Set threshold to 5-10 attempts, duration to 15-30 minutes, and observation window to 15-30 minutes. Monitor for lockout events that may indicate attacks",
      "compliance": {
        "nistSp80053": ["AC-7"],
        "mitreAttack": ["T1110.001", "T1110.003"],
        "cisBenchmark": ["1.2.1", "1.2.2", "1.2.3"],
        "anssi": ["R35"],
        "cisAd": ["5.3.1"]
      }
    },
    {
      "id": "ADPWD-007",
      "name": "Password History Enforcement",
      "description": "Password history prevents users from cycling through the same passwords. Without sufficient history depth, users can alternate between a small set of passwords, negating the security benefit of password rotation requirements",
      "severity": "Medium",
      "subcategory": "Password Policy",
      "recommendedValue": "Password history remembering at least 24 previous passwords",
      "remediationSteps": "Configure in Default Domain Policy: Computer Configuration > Policies > Windows Settings > Security Settings > Account Policies > Password Policy > 'Enforce password history' = 24 passwords remembered. Also ensure 'Minimum password age' is set to at least 1 day to prevent rapid cycling through the history",
      "compliance": {
        "nistSp80053": ["IA-5(1)"],
        "mitreAttack": ["T1110.001"],
        "cisBenchmark": ["1.1.1"],
        "anssi": ["R34"],
        "cisAd": ["5.2.3"]
      }
    },
    {
      "id": "ADPWD-008",
      "name": "Maximum Password Age",
      "description": "Maximum password age forces periodic password rotation. While NIST SP 800-63B recommends against mandatory periodic changes unless compromise is suspected, many compliance frameworks still require it. The policy should balance compliance requirements with usability, avoiding excessively short rotation periods that lead to weak passwords",
      "severity": "Medium",
      "subcategory": "Password Policy",
      "recommendedValue": "Maximum password age between 90-365 days depending on compliance requirements. For privileged accounts, 60 days maximum",
      "remediationSteps": "Configure in Default Domain Policy: Computer Configuration > Policies > Windows Settings > Security Settings > Account Policies > Password Policy > 'Maximum password age' = 365 days (or per compliance requirement). Implement a stricter FGPP for privileged accounts with 60-day maximum",
      "compliance": {
        "nistSp80053": ["IA-5(1)"],
        "mitreAttack": ["T1078.002"],
        "cisBenchmark": ["1.1.2"],
        "anssi": ["R34"],
        "cisAd": ["5.2.4"]
      }
    },
    {
      "id": "ADPWD-009",
      "name": "Users with Password Never Expires",
      "description": "Accounts with the 'Password Never Expires' flag bypass the maximum password age policy. While this may be acceptable for managed service accounts (gMSAs handle rotation automatically), user accounts with this flag retain compromised passwords indefinitely",
      "severity": "High",
      "subcategory": "Account Flags",
      "recommendedValue": "No user accounts with Password Never Expires. Only Group Managed Service Accounts may have automatic rotation exemptions",
      "remediationSteps": "Identify accounts using Get-ADUser -Filter {PasswordNeverExpires -eq $true -and Enabled -eq $true} -Properties PasswordNeverExpires. Review each account for business justification. Clear the flag on user accounts and migrate service accounts to gMSA where possible. Document any approved exceptions with compensating controls",
      "compliance": {
        "nistSp80053": ["IA-5(1)"],
        "mitreAttack": ["T1078.002"],
        "cisBenchmark": ["1.1.4"],
        "anssi": ["R36"],
        "cisAd": ["5.4.1"]
      }
    },
    {
      "id": "ADPWD-010",
      "name": "Users with Blank Passwords",
      "description": "Accounts with the PASSWD_NOTREQD flag can have blank passwords, completely bypassing all password policies. This flag is sometimes set inadvertently during account creation scripts. Any account with a blank password can be accessed by anyone who knows the username",
      "severity": "Critical",
      "subcategory": "Password Security",
      "recommendedValue": "No enabled accounts with blank passwords or PASSWD_NOTREQD flag",
      "remediationSteps": "Identify accounts using Get-ADUser -Filter {PasswordNotRequired -eq $true -and Enabled -eq $true}. Clear the PASSWD_NOTREQD flag and set a strong password on all identified accounts immediately. Review account creation scripts and processes to prevent this flag from being set in the future",
      "compliance": {
        "nistSp80053": ["IA-5(1)", "IA-2"],
        "mitreAttack": ["T1078.002", "T1078"],
        "anssi": ["R36"],
        "cisAd": ["5.5.1"]
      }
    },
    {
      "id": "ADPWD-011",
      "name": "Duplicate Password Hashes",
      "description": "Multiple accounts sharing the same password hash indicate password reuse, commonly used passwords, or accounts with default passwords. Password reuse amplifies the impact of any single credential compromise, allowing lateral movement across multiple accounts",
      "severity": "High",
      "subcategory": "Password Security",
      "recommendedValue": "No clusters of accounts sharing identical password hashes. Each account should have a unique password",
      "remediationSteps": "Extract and compare NT hashes using DCSync-capable tools (DSInternals) with appropriate authorization. Identify clusters of accounts sharing hashes. Force password changes on all accounts in duplicate clusters. Implement password filters to prevent common passwords and consider deploying Azure AD Password Protection",
      "compliance": {
        "nistSp80053": ["IA-5(1)"],
        "mitreAttack": ["T1110.002", "T1078.002"],
        "cisAd": ["5.5.2"]
      }
    },
    {
      "id": "ADPWD-012",
      "name": "Passwords in HaveIBeenPwned Database",
      "description": "Passwords that appear in known breach databases are actively used in credential stuffing attacks. Comparing AD password hashes against the HaveIBeenPwned database identifies accounts using compromised passwords that are likely to be targeted",
      "severity": "High",
      "subcategory": "Password Security",
      "recommendedValue": "No active accounts using passwords found in the HaveIBeenPwned database",
      "remediationSteps": "Compare NT hashes against the HaveIBeenPwned Passwords database (downloadable hash list) using tools like DSInternals Test-PasswordQuality. Force immediate password changes on all accounts with matching hashes. Deploy Azure AD Password Protection or custom password filters to block known breached passwords going forward",
      "compliance": {
        "nistSp80053": ["IA-5(1)"],
        "mitreAttack": ["T1110.002", "T1078.002"],
        "cisAd": ["5.5.3"]
      }
    },
    {
      "id": "ADPWD-013",
      "name": "Custom Dictionary Password Check",
      "description": "Passwords based on organization-specific terms (company name, product names, seasons, location names) are commonly used and easily guessed by targeted attackers. Custom dictionary checks identify passwords that meet complexity requirements but are still predictable",
      "severity": "Medium",
      "subcategory": "Password Security",
      "recommendedValue": "No accounts using passwords containing organization-specific terms, common patterns (Season+Year), or keyboard walks",
      "remediationSteps": "Build a custom dictionary including company names, product names, location names, seasons, and common patterns. Test password hashes against this dictionary using DSInternals or similar tools. Force password changes on matching accounts. Deploy custom password filters or Azure AD Password Protection custom banned password list",
      "compliance": {
        "nistSp80053": ["IA-5(1)"],
        "mitreAttack": ["T1110.001", "T1110.003"],
        "cisAd": ["5.5.4"]
      }
    },
    {
      "id": "ADPWD-014",
      "name": "Default/Common Passwords",
      "description": "Accounts using default, common, or trivially guessable passwords (such as Password1, Welcome1, or the account name) are the first targets in password spraying attacks. These passwords are included in every attacker wordlist and are often tested first",
      "severity": "High",
      "subcategory": "Password Security",
      "recommendedValue": "No accounts using passwords from the top 1000 most common password lists or matching default password patterns",
      "remediationSteps": "Test password hashes against common password lists (such as SecLists) using DSInternals Test-PasswordQuality. Force immediate password changes on all accounts with common passwords. Implement Azure AD Password Protection which includes a global banned password list updated by Microsoft",
      "compliance": {
        "nistSp80053": ["IA-5(1)"],
        "mitreAttack": ["T1110.001", "T1110.003", "T1078.002"],
        "anssi": ["R34"],
        "cisAd": ["5.5.5"]
      }
    },
    {
      "id": "ADPWD-015",
      "name": "Password Last Set Age Distribution",
      "description": "Analyzing the distribution of password ages across all accounts reveals policy enforcement effectiveness and identifies accounts with extremely old passwords. Accounts with passwords unchanged for years may have been missed by policy changes or have Password Never Expires set",
      "severity": "Medium",
      "subcategory": "Password Hygiene",
      "recommendedValue": "No enabled user accounts with passwords older than the maximum password age policy. Distribution should show regular rotation patterns",
      "remediationSteps": "Generate a password age distribution report using Get-ADUser -Filter {Enabled -eq $true} -Properties PasswordLastSet | Group-Object {(New-TimeSpan $_.PasswordLastSet (Get-Date)).Days -replace '\\d$','0'}. Investigate accounts with passwords older than the policy allows. Force password changes where needed and review Password Never Expires flags",
      "compliance": {
        "nistSp80053": ["IA-5(1)"],
        "mitreAttack": ["T1078.002"],
        "cisAd": ["5.6.1"]
      }
    },
    {
      "id": "ADPWD-016",
      "name": "LAPS Deployment Status",
      "description": "Local Administrator Password Solution (LAPS) provides unique, randomly generated local administrator passwords for each computer, stored securely in AD. Without LAPS, local admin passwords are typically identical across all machines, enabling trivial lateral movement after capturing one hash",
      "severity": "High",
      "subcategory": "Local Admin",
      "recommendedValue": "LAPS deployed to 100% of domain-joined Windows computers. No computers with missing or expired LAPS passwords",
      "remediationSteps": "Deploy the LAPS client (CSE) to all domain-joined computers via GPO, SCCM, or Intune. Configure LAPS GPO settings for password complexity, length (at least 20 characters), and age (30 days). Verify deployment by checking the ms-Mcs-AdmPwd or msLAPS-Password attribute on computer objects. Investigate any computers without LAPS passwords",
      "compliance": {
        "nistSp80053": ["IA-5(1)", "AC-6"],
        "mitreAttack": ["T1078.003", "T1003"],
        "cisBenchmark": ["18.2.1"],
        "anssi": ["R42"],
        "nsaAsd": ["LAPS-1"],
        "cisAd": ["5.7.1"]
      }
    },
    {
      "id": "ADPWD-017",
      "name": "LAPS Password Expiration",
      "description": "LAPS passwords should be rotated regularly to limit the window of exposure if a local admin password is compromised. Expired or stale LAPS passwords indicate that the LAPS client is not functioning correctly on those machines",
      "severity": "Medium",
      "subcategory": "Local Admin",
      "recommendedValue": "LAPS password expiration set to 30 days. No computers with expired LAPS passwords",
      "remediationSteps": "Review LAPS password expiration dates on computer objects using Get-ADComputer -Filter * -Properties ms-Mcs-AdmPwdExpirationTime. Identify computers with expired passwords and investigate the LAPS CSE functionality on those machines. Configure GPO to set password age to 30 days maximum",
      "compliance": {
        "nistSp80053": ["IA-5(1)"],
        "mitreAttack": ["T1078.003"],
        "anssi": ["R42"],
        "cisAd": ["5.7.2"]
      }
    },
    {
      "id": "ADPWD-018",
      "name": "Windows LAPS vs Legacy LAPS",
      "description": "Windows LAPS (built into Windows Server 2019+ and Windows 10/11 with April 2023 update) provides improvements over legacy LAPS including password encryption, password history, and DSRM password management. Organizations should migrate from legacy LAPS to Windows LAPS for enhanced security features",
      "severity": "Low",
      "subcategory": "Local Admin",
      "recommendedValue": "Windows LAPS deployed with password encryption enabled. Legacy LAPS migration completed",
      "remediationSteps": "Verify which LAPS version is deployed by checking for the msLAPS-Password attribute (Windows LAPS) versus ms-Mcs-AdmPwd (Legacy LAPS). Plan migration to Windows LAPS. Update the AD schema for Windows LAPS attributes. Deploy Windows LAPS GPO settings with encryption enabled. Decommission legacy LAPS components after migration",
      "compliance": {
        "nistSp80053": ["IA-5(1)", "SC-28"],
        "mitreAttack": ["T1078.003"],
        "cisAd": ["5.7.3"]
      }
    },
    {
      "id": "ADPWD-019",
      "name": "Azure AD Password Protection",
      "description": "Azure AD Password Protection extends banned password enforcement to on-premises AD by deploying proxy and DC agent components. It blocks passwords matching a global Microsoft-curated banned list and an optional custom banned list, preventing users from choosing passwords known to be weak",
      "severity": "Medium",
      "subcategory": "Password Security",
      "recommendedValue": "Azure AD Password Protection deployed in enforced mode with custom banned password list configured",
      "remediationSteps": "Deploy the Azure AD Password Protection proxy service and DC agent on all domain controllers. Configure a custom banned password list in Azure AD including organization-specific terms. Set the mode to Enforced (not Audit). Monitor password change rejections through event logs and Azure AD reporting",
      "compliance": {
        "nistSp80053": ["IA-5(1)"],
        "mitreAttack": ["T1110.001", "T1110.003"],
        "cisAd": ["5.5.6"]
      }
    },
    {
      "id": "ADPWD-020",
      "name": "BitLocker Recovery Keys in AD",
      "description": "BitLocker recovery keys stored in Active Directory should be inventoried to ensure disk encryption is properly deployed and recovery keys are available. The presence of recovery keys also indicates which machines have BitLocker enabled, providing visibility into encryption coverage",
      "severity": "Info",
      "subcategory": "Encryption",
      "recommendedValue": "BitLocker recovery keys present in AD for all workstations and laptops. Recovery key access restricted to authorized administrators",
      "remediationSteps": "Query AD for BitLocker recovery information objects using Get-ADObject -Filter {objectClass -eq 'msFVE-RecoveryInformation'} -SearchBase 'DC=domain,DC=com'. Cross-reference with computer inventory to identify machines without BitLocker. Review ACLs on recovery key objects to ensure only authorized administrators can read them",
      "compliance": {
        "nistSp80053": ["SC-28", "SC-28(1)"],
        "mitreAttack": ["T1005"],
        "cisAd": ["5.8.1"]
      }
    },
    {
      "id": "ADPWD-021",
      "name": "Account Lockout Threshold",
      "description": "The account lockout threshold defines the number of failed logon attempts before an account is locked. A threshold that is too high (or zero, meaning no lockout) allows extensive password spraying, while a threshold that is too low enables easy denial of service",
      "severity": "High",
      "subcategory": "Lockout Policy",
      "recommendedValue": "Lockout threshold between 5-10 failed attempts",
      "remediationSteps": "Configure in Default Domain Policy: Computer Configuration > Policies > Windows Settings > Security Settings > Account Policies > Account Lockout Policy > 'Account lockout threshold' = 5-10 attempts. A value of 0 disables lockout entirely and should be avoided. Balance between security and usability based on organizational needs",
      "compliance": {
        "nistSp80053": ["AC-7"],
        "mitreAttack": ["T1110.001", "T1110.003"],
        "cisBenchmark": ["1.2.1"],
        "anssi": ["R35"],
        "cisAd": ["5.3.2"]
      }
    },
    {
      "id": "ADPWD-022",
      "name": "Lockout Observation Window",
      "description": "The lockout observation window defines the time period during which failed logon attempts are counted toward the lockout threshold. If the observation window is too short, attackers can spread password spray attempts over time to avoid triggering lockout",
      "severity": "Medium",
      "subcategory": "Lockout Policy",
      "recommendedValue": "Observation window of 15-30 minutes, matching or exceeding the lockout duration",
      "remediationSteps": "Configure in Default Domain Policy: Computer Configuration > Policies > Windows Settings > Security Settings > Account Policies > Account Lockout Policy > 'Reset account lockout counter after' = 15-30 minutes. Ensure this value is equal to or greater than the lockout duration to prevent attackers from waiting out the counter between spray attempts",
      "compliance": {
        "nistSp80053": ["AC-7"],
        "mitreAttack": ["T1110.001", "T1110.003"],
        "cisBenchmark": ["1.2.3"],
        "anssi": ["R35"],
        "cisAd": ["5.3.3"]
      }
    }
  ]
}