ADSTALE-001: Cuentas de usuario inactivas
- Plataforma
- Active Directory
- Categoría
- AD Stale & Obsolete Objects
- Severidad
- Medium
- Pilar de Zero Trust
- Governance (peso 1)
- Fixtures de referencia
- 3
- Cobertura de ramas
- Observada: los fixtures prueban los veredictos que ejercitan
- Procedencia
- baseline
Qué comprueba
Las cuentas de usuario que no se han utilizado durante un período prolongado representan una superficie de ataque innecesaria. Las cuentas inactivas pueden conservar credenciales válidas y pertenencias a grupos, lo que las convierte en objetivos atractivos para los atacantes que pueden comprometer credenciales olvidadas o compartidas sin activar las alertas asociadas a usuarios activos. Las cuentas inactivas durante más de 90 días deben revisarse y deshabilitarse
Por qué importa
Every enabled account is a credential the directory will accept. An inactive account amplifies risk along four axes. First, monitoring blindness: the owner is gone (terminated, on long leave, role change, contractor offboarded) and is not watching their inbox for sign-in alerts, MFA prompts, or password-change emails, which removes the human tripwire that catches opportunistic compromise. Second, residual privilege: accounts created for a project, a migration, or a temporary delegation routinely retain the group memberships they were granted, so a stale Helpdesk or Backup Operators member is still functionally an administrator on the day an attacker phishes or sprays the credential. Third, weak credentials: inactive accounts disproportionately carry stale passwords set under whatever the policy was when the account was created (often a default like Welcome1, Spring2023!, or the company name plus a digit), and the owner is not rotating them. Fourth, audit signal-to-noise: failed authentication noise against active accounts gets investigated; failed authentication against a stale account is often filtered out as the dormant account it always was. The combined effect is that an enabled stale account is the cheapest, lowest-noise foothold in the directory, and attackers know to enumerate them first. CISA, Microsoft, and Mandiant have all published advisories tracing initial access in ransomware intrusions to dormant or forgotten accounts that had retained valid credentials and group memberships.
Ruta de ataque
Step 1: Enumeration. An attacker with any authenticated foothold (intern credential, harvested via phishing, captured from a public code repo) runs an LDAP query for enabled users with stale lastLogonTimestamp. PowerView Get-DomainUser -LDAPFilter "(&(samAccountType=805306368)(!userAccountControl:1.2.840.113556.1.4.803:=2))" and BloodHound collection both expose the data without elevated rights. The attacker also pulls servicePrincipalName, memberOf, and pwdLastSet in the same query to score targets. Step 2: Target selection. The attacker filters for accounts that are (a) enabled, (b) lastLogonTimestamp older than 90 days, (c) in a useful security group (Domain Users with file-share or VPN access, Helpdesk, Backup Operators, a legacy admin group), and (d) have a pwdLastSet older than the current password policy maximum, indicating the password has likely never been forced to rotate. A typical mid-size enterprise yields 50 to 500 such accounts. Step 3: Credential acquisition. The attacker chooses the cheapest exploitation primitive for each target. Password spraying (Rubeus, kerbrute, MSOLSpray): a single seasonal password against the entire stale-account pool, no lockout risk because the account is forgotten. AS-REP roasting (Rubeus asreproast, Impacket GetNPUsers.py): if the stale account has DONT_REQ_PREAUTH set (userAccountControl bit 0x400000), the attacker requests an AS-REP and cracks it offline. Kerberoasting (Rubeus kerberoast, Impacket GetUserSPNs.py): if the stale account is a service account with a Service Principal Name, the attacker requests a TGS, captures the RC4-HMAC-encrypted ticket, and cracks it offline. Step 4: Use the credential. Once cracked, the stale credential is used against VPN, RDP gateways, OWA, Autodiscover, or Azure AD / Entra ID sign-in. Because the account is enabled and the owner is absent, there is no MFA prompt visible to a human and no Conditional Access user-risk signal from anomalous travel. Step 5: Persistence and lateral movement. The attacker harvests further credentials, runs BloodHound to plot a path from the stale-account foothold to Tier 0, and pivots. The compromised stale account becomes the persistent backdoor: it survives password rotation cycles tied to active users, it survives helpdesk credential resets, and it survives EDR alerts that are scoped to known active personas. ADSTALE-001 closes this primitive at the source by ensuring no enabled account remains usable past its operational lifetime.
Cómo lo evalúa Guerrilla
Guerrilla calls Get-ADUser -Filter * with a properties list of (lastLogonTimestamp, lastLogonDate, whenCreated, pwdLastSet, userAccountControl, servicePrincipalName, memberOf, Enabled), filters in-process to those where Enabled is $true, converts lastLogonTimestamp from Windows FILETIME to DateTime, and computes (Get-Date) minus that DateTime. Any delta greater than the configured threshold (default 90 days) raises the account. Accounts with lastLogonTimestamp = 0 are reported as "never authenticated". The check also resolves memberOf transitively against the privileged-group list (Domain Admins, Enterprise Admins, Schema Admins, Account Operators, Backup Operators, Print Operators, Server Operators, Cert Publishers, DNSAdmins, Group Policy Creator Owners, plus any organization-specific Tier 0 groups) and elevates any stale account that lands in those groups to High severity in the report. Accounts with a populated servicePrincipalName are partitioned into a separate "stale service account" report bucket so they can be reviewed against the gMSA migration backlog. The check is read-only, requires only authenticated user rights to the domain partition, and emits no Event 4738 / 4740 / 4767 noise because it does not modify any object. The 9 to 14 day replication jitter on lastLogonTimestamp is documented and is the reason the threshold is set to 90 days rather than something tighter: the underlying attribute is intentionally lossy to reduce inter-DC replication traffic.
Valor recomendado
Ninguna cuenta de usuario habilitada inactiva durante más de 90 días; cuentas inactivas deshabilitadas o eliminadas
Remediación
Consulte las cuentas de usuario donde lastLogonTimestamp sea anterior a 90 días y la cuenta esté habilitada usando Search-ADAccount -AccountInactive -TimeSpan 90 -UsersOnly. Verifique con los propietarios o responsables de las cuentas antes de actuar. Deshabilite primero las cuentas inactivas y luego elimínelas tras un período de gracia de 30 días si no se reclaman. Elimine las cuentas deshabilitadas de todos los grupos de seguridad. Implemente una gestión automatizada del ciclo de vida
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 | FAIL |
| throttled | Not Assessed |
Mapeos a marcos de referencia
- NIST SP 800-53
- AC-2(3)
- CIS AD Benchmark
- 9.1.1
- MITRE ATT&CK
- T1078.002