ADPWD-017: LAPS Password Expiration
- Plataforma
- Active Directory
- Categoría
- AD Password & Lockout Policies
- Severidad
- Medium
- Pilar de Zero Trust
- Identity (peso 1)
- Fixtures de referencia
- 3
- Cobertura de ramas
- Observada: los fixtures prueban los veredictos que ejercitan
- Procedencia
- baseline
Qué comprueba
The check enumerates every (objectClass=computer) under the defaultNamingContext and reads the expiration attribute in priority order: - msLAPS-PasswordExpirationTime (Windows LAPS, schema introduced by KB5025227 in April 2023) - ms-Mcs-AdmPwdExpirationTime (legacy Microsoft LAPS, schema extension shipped with the original MSI) Both attributes are stored as a Windows FILETIME (Int64, 100-nanosecond intervals since 1601-01-01 UTC). Guerrilla converts the raw value with [DateTime]::FromFileTimeUtc() and compares the result against the current UTC time. A computer is reported in one of four states: - Expired: the timestamp is in the past. The CSE failed to rotate at the scheduled interval. - Missing: neither attribute is populated. LAPS has never written to this object, either because the CSE is not installed, the GPO does not target it, or the machine account lacks SELF write permission on the attribute. - Stale Beyond Policy: the timestamp is in the future but exceeds today + the policy ceiling (default 30 days). Indicates a per-object override or a misconfigured GPO PasswordAgeDays setting. - Healthy: timestamp is in the future and within the policy ceiling. The check also reads the PasswordAgeDays value the GPO is publishing (HKLM\Software\Microsoft\Windows\CurrentVersion\LAPS\Config\PasswordAgeDays for Windows LAPS, HKLM\Software\Policies\Microsoft Services\AdmPwd\PasswordAgeDays for legacy LAPS) on a sample of endpoints so the operator can see whether the AD-side baseline matches the GPO-side baseline. Computer objects that are disabled or whose lastLogonTimestamp is older than the configured stale window are excluded from the FAIL count and listed separately so a long-offline laptop fleet does not create false positives.
Por qué importa
LAPS exists to defeat lateral movement by Pass-the-Hash (T1550.002) and Pass-the-Ticket (T1550.003). Before LAPS, the local Administrator account on every workstation in an OU image shared the same NT hash, so a single LSASS dump from any endpoint yielded SMB and WMI admin to every other machine in the same image, a primitive the SDL community calls Same-Local-Admin-Password. LAPS breaks that primitive by giving each computer a unique random password that rotates on a fixed schedule. The control depends entirely on rotation actually happening. An expired expiration timestamp means the password on disk has been static for longer than policy allows, and in the worst case it means a credential that was exposed in a prior incident (a stolen LAPS backup, a help-desk audit log, a forensic image, a screenshot in a ticket) is still valid today. The blast radius scales with how many machines are stale at once: a single expired endpoint is a hygiene issue, a stale population of hundreds is a deployment failure that re-creates the pre-LAPS lateral movement surface. The check also catches a quieter persistence pattern: an attacker with WriteProperty on ms-Mcs-AdmPwdExpirationTime can push the timestamp far into the future on machines they care about, suppressing rotation while keeping a known plaintext password indefinitely (a variant of T1098, Account Manipulation).
Ruta de ataque
1. Foothold and discovery: the attacker, holding any authenticated session, queries LDAP for computer objects and asks for ms-Mcs-AdmPwdExpirationTime and msLAPS-PasswordExpirationTime. Get-ADComputer -Filter * -Properties ms-Mcs-AdmPwdExpirationTime, msLAPS-PasswordExpirationTime returns the entire fleet without raising any non-standard event because both attributes are world-readable by default in legacy LAPS deployments (only the password attribute is ACL-protected). 2. Identify stale population: the attacker filters for objects where the expiration timestamp is in the past or unset. Tools: LAPSToolkit (Jake Liefer), Get-LAPSComputers from PowerView, or a one-line LDAP filter (&(objectClass=computer)(ms-Mcs-AdmPwdExpirationTime<=132000000000000000)). 3. Read protected attribute on healthy LAPS targets: for endpoints where LAPS did rotate, the attacker tests whether they hold ExtendedRight Read-LAPS-Password (ms-Mcs-AdmPwd or msLAPS-Password / msLAPS-EncryptedPassword) via misconfigured delegations. ADACL checks for LAPS read rights pair directly with this check. 4. Reuse static credentials: on machines flagged Expired or Missing, the local Administrator password may still be the image default or an older LAPS value. The attacker tries known prior LAPS dumps from incident-response artifacts, help-desk tickets, or stolen ELK indices. 5. Persistence via expiration manipulation: with GenericWrite on a target computer object, the attacker sets ms-Mcs-AdmPwdExpirationTime to a far-future FILETIME (Set-ADComputer -Replace @{"ms-Mcs-AdmPwdExpirationTime" = "133000000000000000"}). The CSE on that endpoint will not rotate again until the timestamp passes, freezing the password the attacker has captured. 6. Lateral movement: with a working local-admin credential, the attacker pivots via SMB (impacket-psexec, smbexec), WMI (wmiexec), or WinRM. Because the credential is local, no domain logon event fires on a DC, and Defender for Identity does not see it.
Cómo lo evalúa Guerrilla
Guerrilla binds to the defaultNamingContext with the credentials of the invoking principal and issues a paged LDAP search for (objectClass=computer), requesting msLAPS-PasswordExpirationTime, ms-Mcs-AdmPwdExpirationTime, lastLogonTimestamp, userAccountControl, operatingSystem, and distinguishedName. For each object the check picks the newer Windows LAPS attribute when present and falls back to legacy LAPS. The Int64 FILETIME is converted via [DateTime]::FromFileTimeUtc() and compared against [DateTime]::UtcNow. Computers are bucketed as Expired (now > timestamp), Missing (attribute not present and not null), Stale Beyond Policy (timestamp > now + PasswordAgeDays ceiling), or Healthy. Disabled accounts (userAccountControl bit 0x2) and accounts whose lastLogonTimestamp is older than the configured stale-computer threshold (default 90 days, aligned with ADSTALE-001) are tagged Excluded and shown in a separate table. The check also performs a sampled GPO read: it resolves the LAPS GPOs by querying gpLink on the OUs that contain the failing machines, pulls the Registry.pol values for PasswordAgeDays, and reports any mismatch between the GPO-published ceiling and the observed AD timestamps. Output rows include computer DN, OS, last logon, expiration timestamp, days-since-expiration (negative for healthy), the source attribute (legacy vs Windows LAPS), and the OU-level GPO ceiling.
Valor recomendado
LAPS password expiration set to 30 days. No computers with expired LAPS passwords
Remediación
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
Veredictos probados con fixtures
Cada veredicto de esta tabla está probado por un fixture de referencia en la suite de pruebas que valida el módulo. La tabla se deriva de la última ejecución en verde; no puede editarse a mano.
| Escenario | Veredicto esperado |
|---|---|
| clean | PASS |
| known-bad | WARN |
| no-data | Not Assessed |
Mapeos a marcos de referencia
- NIST SP 800-53
- IA-5(1)
- ANSSI
- R42
- CIS AD Benchmark
- 5.7.2
- MITRE ATT&CK
- T1078.003