ADSTALE-003: Disabled Accounts with Group Memberships
- 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
Guerrilla queries every user, computer, and inetOrgPerson object where the ACCOUNTDISABLE flag (0x2) is set on the userAccountControl attribute. For each disabled principal it reads the memberOf back-link attribute and the primaryGroupID attribute, then resolves the primary group RID against the domain SID to compute the effective primary group (typically Domain Users RID 513 for users, Domain Computers RID 515 for workstations, Domain Controllers RID 516 for DCs). Any memberOf entry that is not the principal's primary group is reported as a residual membership. The check also flags disabled accounts that are still members of protected groups (Domain Admins, Enterprise Admins, Schema Admins, Account Operators, Backup Operators, Print Operators, Server Operators, Cert Publishers, Group Policy Creator Owners, DnsAdmins, Remote Desktop Users, Protected Users) and disabled accounts that hold an adminCount value of 1, because both indicate the principal is still tracked by SDProp and AdminSDHolder despite being disabled.
Por qué importa
Disabling an account is not the same as deprovisioning it. The Windows access check evaluates group membership at token build time, so a disabled account that is re-enabled immediately inherits every right and ACL grant carried by its old groups, with no human in the loop to notice that a long-dormant principal just became Domain Admin again. Three real-world failure modes drive the severity. First, helpdesk and Account Operators can re-enable user accounts by design, which means any compromise of those tiers turns the pool of disabled-but-privileged accounts into a ready-made list of one-click escalations. Second, attackers who land in the directory routinely scan for disabled accounts with elevated group membership because re-enabling an existing account is quieter than creating a new one and survives most "new admin account" detections. Third, group-based access reviews (SailPoint, Saviynt, Entra ID Governance, native AD reporting) typically count members without filtering on userAccountControl, which means disabled accounts inflate the apparent population of every privileged group and dilute the signal of legitimate access certification. The cleanup is cheap, the blast radius if missed is the forest.
Ruta de ataque
1. Attacker obtains rights to enable disabled accounts. This is granted by default to Account Operators, by delegation to most helpdesk OUs, and is held implicitly by anyone with GenericAll, GenericWrite, or WriteProperty on userAccountControl over the target OU. 2. Attacker enumerates disabled accounts that still hold privileged group membership: Get-ADUser -Filter {Enabled -eq $false} -Properties MemberOf,adminCount | Where-Object { $_.adminCount -eq 1 -or $_.MemberOf -match "Admins|Operators" }. The same query runs in BloodHound as MATCH (u:User {enabled:false})-[:MemberOf*1..]->(g:Group) WHERE g.admincount=true RETURN u,g. 3. Attacker selects a disabled account with the desired privilege (commonly a former IT staff member still in Domain Admins, a decommissioned service account in Backup Operators, or a contractor account in a custom application admin group). 4. Attacker re-enables the account with Enable-ADAccount or by clearing the ACCOUNTDISABLE bit directly: Set-ADUser -Identity victim -Enabled $true. The group memberships are already attached, no Add-ADGroupMember call is required. 5. Attacker resets the password (often allowed by the same helpdesk delegation that allowed re-enable) and authenticates as a fully privileged principal. 6. After use, the attacker disables the account again to restore the appearance of a stale tombstone, leaving the privileged memberships intact for the next round. The trail in security event logs looks like routine account hygiene rather than privilege escalation, which is why this technique consistently appears in Mandiant M-Trends and Microsoft DART case write-ups of human-operated ransomware.
Cómo lo evalúa Guerrilla
Guerrilla issues a single LDAP query filter (&(|(objectCategory=person)(objectCategory=computer))(userAccountControl:1.2.840.113556.1.4.803:=2)) which uses the LDAP_MATCHING_RULE_BIT_AND OID to match the ACCOUNTDISABLE bit on userAccountControl. For each result it requests memberOf, primaryGroupID, adminCount, lastLogonTimestamp, whenChanged, and objectSid. The primary group is resolved by combining the domain SID with the primaryGroupID RID. Every memberOf entry that is not the primary group DN is emitted as a residual membership. Findings are tagged with three severity uplifts: adminCount=1 on the disabled account, membership in a built-in protected group (matched against the well-known RIDs 512, 518, 519, 520, 544, 548, 549, 550, 551, 552, 555, 557, 562, 573, 578, 580), and lastLogonTimestamp older than the configured stale threshold (default 180 days). The check is purely read-only and runs as part of invoke-adaudit with no writes to the directory.
Valor recomendado
All disabled accounts removed from all security groups except Domain Users
Remediación
Identify disabled accounts with non-default group memberships using Get-ADUser -Filter {Enabled -eq $false} -Properties MemberOf. Remove all security group memberships (except the primary group) from disabled accounts. Implement an automated workflow that strips group memberships when accounts are disabled. Include group membership cleanup in the account deprovisioning process
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 |
| throttled | Not Assessed |
Mapeos a marcos de referencia
- NIST SP 800-53
- AC-2(3), AC-6
- CIS AD Benchmark
- 9.2.1