ADPWD-021: Account Lockout Threshold

Platform
Active Directory
Category
AD Password & Lockout Policies
Severity
High
Zero Trust pillar
Identity (weight 2)
Golden fixtures
3
Branch coverage
Observed: fixtures prove the verdicts they exercise
Provenance
baseline

What it checks

The check binds to the default naming context root (for example DC=contoso,DC=local) and reads the lockoutThreshold attribute on the domain object itself, which is where Default Domain Policy lockout values are persisted by Group Policy. lockoutThreshold is a 32-bit integer representing the number of failed interactive, network, or service logon attempts permitted within the lockoutObservationWindow before the account moves to a locked state. A value of 0 means lockout is disabled and an unlimited number of bad-password attempts will be accepted. ADPWD-021 also pulls the two companion attributes that define the lockout behavior, lockoutDuration (FILETIME negative interval, how long the account remains locked) and lockoutObservationWindow (FILETIME negative interval, the rolling window in which bad-password counts accumulate before reset), because the three settings only make sense as a tuple. Finally, the check enumerates Fine-Grained Password Policies via Get-ADFineGrainedPasswordPolicy -Filter * and inspects msDS-LockoutThreshold, msDS-LockoutDuration, and msDS-LockoutObservationWindow on each PSO, then resolves msDS-PSOAppliesTo to determine which users and groups are covered by a stronger or weaker lockout policy than the domain baseline. Privileged groups (Domain Admins, Enterprise Admins, Schema Admins, Tier 0 service accounts) that are not covered by a stricter PSO are flagged as elevated risk.

Why it matters

Account Lockout Threshold sits at the intersection of two opposing failure modes, and getting it wrong in either direction is a recurring root cause of incidents. With lockoutThreshold = 0 (disabled), the domain accepts unbounded online password attempts. An attacker who has scraped a user list from Exchange, Teams, the GAL, or Azure AD UPN enumeration can spray a single low-entropy candidate (Spring2026!, Welcome1, CompanyName123) across the entire population. Real-world telemetry from Mandiant M-Trends, Microsoft Defender for Identity, and the Verizon DBIR consistently shows that a single sweep against ten thousand users with one common password yields a foothold in the majority of mid-sized enterprises. With lockoutThreshold set very low (1, 2, or 3), the inverse pathology applies: an unauthenticated or low-privilege attacker can trivially deny service by submitting three bad Kerberos pre-auth requests per account against the KDC, locking every account in the domain in minutes. This includes Domain Admins, the helpdesk accounts that would otherwise unlock them, and any service account whose name is discoverable through SPN enumeration. The 5 to 10 window is the operational equilibrium: high enough that legitimate users with stale cached credentials on phones or RDP sessions will not trip the threshold during a normal credential change, low enough that an attacker can only test a handful of guesses per observation window before the account becomes useless to them. The Account Lockout Threshold also interacts directly with detection: when set correctly, repeated failures produce Event 4740 (account locked) on the PDC Emulator and Event 4625 (logon failure) at high volume on every DC, both of which feed SIEM correlation rules for spray and brute-force. With lockout disabled, those signals never fire and the spray runs invisibly.

Attack path

Spray scenario (lockoutThreshold = 0 or very high). Step 1: Reconnaissance. The attacker queries lockoutThreshold via LDAP, Get-ADDefaultDomainPasswordPolicy, PowerView Get-DomainPolicy, or Impacket samrdump.py. The attribute is readable by any authenticated user and confirms whether the domain will lock accounts on failed attempts. Step 2: Username harvesting. The attacker enumerates users via LDAP (Get-ADUser -Filter *), Kerberos pre-auth username probing (kerbrute userenum), GAL extraction from compromised Exchange, OWA timing oracle, or Azure AD UPN scraping. Step 3: Password spray. With lockout disabled, the attacker uses Rubeus, kerbrute, MailSniper, Spray365, or DomainPasswordSpray.ps1 to attempt one common password per account per observation window. Because no lockout fires, the spray can run continuously: one password per user per 30 minutes across 10,000 users produces 480,000 attempts per day with zero account-locked side effects. Step 4: Foothold. A single hit returns a valid domain user. The attacker pivots to Kerberoasting (ADKERB-001), AS-REP roasting (ADKERB-002), and BloodHound graph traversal toward Tier 0. Denial-of-service scenario (lockoutThreshold = 1, 2, or 3). Step 1: Username harvesting. As above, but the attacker does not need credentials, only usernames. Step 2: Mass lockout. The attacker submits three bad passwords per account against any authentication endpoint that increments badPwdCount: Kerberos AS-REQ to the KDC, LDAP simple bind to any DC, NTLM authentication to any SMB share. Tools include kerbrute bruteuser, a trivial loop around Test-Credential, or Get-ADUser piped into a credential test loop. Step 3: Operational outage. Within minutes every account in the domain is locked, including Domain Admins, helpdesk, and emergency-access accounts (unless those are correctly placed in a no-lockout PSO). Helpdesk cannot unlock accounts because their own accounts are locked. Service accounts fail and dependent applications go offline. The attacker has converted an unauthenticated network position into a domain-wide outage without ever obtaining a single valid credential. Detection-evasion scenario. Step 1: The attacker observes that lockoutThreshold = 0 and chooses a spray cadence that stays under any rate-limiting on the DC. Because no account ever locks, Event 4740 never fires. Event 4625 fires per attempt but at low rate per source IP and per account, blending into normal failed-logon noise. The spray runs for days or weeks before any analyst notices.

How Guerrilla assesses it

Guerrilla calls Get-ADDefaultDomainPasswordPolicy and additionally reads the domain naming-context head via Get-ADObject -Identity (Get-ADDomain).DistinguishedName -Properties lockoutThreshold, lockoutDuration, lockoutObservationWindow. The two FILETIME interval values (lockoutDuration, lockoutObservationWindow) are converted from Windows negative-tick intervals to TimeSpan for human-readable comparison. The check evaluates the parsed lockoutThreshold against the hardened 5 to 10 range and emits one of three outcomes: Fail Disabled (lockoutThreshold = 0), Fail Too Low (1 to 4, denial-of-service risk), Fail Too High (greater than 10, spray risk), or Pass (5 to 10). When the threshold is in range, the check additionally verifies that lockoutDuration is at least 15 minutes and lockoutObservationWindow is at least 15 minutes, because a 5-attempt threshold paired with a 30-second observation window is functionally equivalent to no lockout against a paced spray. The check then enumerates every Fine-Grained Password Policy via Get-ADFineGrainedPasswordPolicy -Filter * and reports the msDS-LockoutThreshold value alongside the principals returned by msDS-PSOAppliesTo, so an analyst can see at a glance whether privileged tiers are covered by a stronger PSO or are still inheriting a weak domain default. The check is read-only, requires only authenticated user rights to the domain object and to the Password Settings Container under CN=System, and produces no Event 4738 or 4739 noise.

Recommended value

Lockout threshold between 5-10 failed attempts

Remediation

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

Fixture-proven verdicts

Every verdict below is proven by a golden fixture in the module's gating test suite. This table derives from the last green run; it cannot be edited by hand.

Verdict scenarios for ADPWD-021
ScenarioExpected verdict
cleanPASS
known-badFAIL
throttledNot Assessed

Framework mappings

NIST SP 800-53
AC-7
CIS Benchmark
1.2.1
ANSSI
R35
CIS AD Benchmark
5.3.2
MITRE ATT&CK
T1110.001, T1110.003