ADSTALE-008: SID History huérfano
- 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
El SID History se utiliza durante las migraciones de dominio para preservar el acceso a los recursos del dominio de origen. Una vez completada la migración, las entradas de SID History deben eliminarse, ya que pueden abusarse para la escalada de privilegios. Las entradas de SID History huérfanas que hacen referencia a dominios inexistentes o cuentas eliminadas no aportan ningún beneficio legítimo y aumentan el riesgo de ataques de inyección de SID a través de los límites de confianza
Por qué importa
SID History is one of the few AD constructs where the access token is derived from data the attacker can write rather than data the OS computes. Every SID in sIDHistory is unconditionally added to the user access token at TGT issuance and is honoured by every Windows access check unless SID filtering removes it at a trust boundary. This produces three distinct risks: 1. Hidden privilege. A standard user account whose sIDHistory contains the SID of the source-domain Domain Admins group (or, far worse, the SID of Enterprise Admins from the forest root) becomes effectively Domain Admin without holding any visible group membership. Standard privilege audits (Get-ADGroupMember "Domain Admins", BloodHound default queries against memberOf) miss this entirely because the attacker is not a group member, they are carrying the group SID in their token. Detection requires explicit sIDHistory enumeration, which is exactly what ADSTALE-008 performs. 2. Persistence after eviction. SID History injection (T1134.005) is the canonical AD persistence technique that survives password resets, krbtgt rotation, and account disable. An attacker who obtains DRSUAPI write access for ten seconds (Mimikatz misc::addsid, DSInternals Add-ADDBSidHistory, or direct LDAP write where Self-Membership ACLs allow it) can stamp a privileged SID onto any throwaway account and then evict their original foothold. The throwaway account looks unremarkable in every standard audit yet retains Domain Admin authority indefinitely. 3. Cross-trust escalation. SID filtering on inbound trusts (Quarantine) strips foreign SIDs from inbound tokens, but the filter exempts the local domain SID and well-known SIDs. An attacker who controls a trusted external domain can stamp the local-forest Enterprise Admins SID onto a foreign account and then ride the trust into the target forest. CVE-2022-26923 (certifried) and the broader research on cross-forest SID injection by SpecterOps, Dirk-jan Mollema, and Charlie Clark demonstrate that trust topology assumptions break down quickly when sIDHistory is uncontrolled. Orphaned entries are the easiest of these to triage and the easiest to ignore. They are easiest to triage because they provide no business value: by definition the SID points at nothing, so removing it cannot break any access. They are easiest to ignore because they appear inert: a SID for a domain that no longer exists cannot match an ACL on any resource the local forest holds. The risk is not the orphan itself, the risk is that environments which carry orphans demonstrably are not auditing sIDHistory at all. Every breach investigation that surfaces a malicious SID injection starts with an environment whose orphan count was nonzero and unmonitored, because the operational hygiene to clean up legitimate orphans is the same hygiene that detects malicious additions.
Ruta de ataque
1. Initial Domain Admin access. The attacker reaches Tier 0 through any of the standard paths (Kerberoasting a service account in Domain Admins, ADCS ESC1/ESC8, unconstrained delegation on a DC, NTLM relay to LDAPS, DCSync via Replicating Directory Changes). 2. Pick the carrier account. The attacker chooses a low-privilege, low-attention user account: a disabled migration account, a service account in a forgotten OU, a contractor account flagged for offboarding. These accounts are not in any privileged group and are not in the BloodHound shortest-path-to-Domain-Admins output. 3. Stamp the privileged SID. With Domain Admin, the attacker uses Mimikatz (misc::addsid /sam:targetUser /new:S-1-5-21-<domain>-512) which calls DRSUAPI IDL_DRSAddSidHistory and writes directly to the carrier account sIDHistory. Alternatively the attacker uses DSInternals Add-ADDBSidHistory offline against a stolen NTDS.dit, or invokes the documented ADMT API path for the same effect. The write produces no Event ID 4738 because the attribute change goes via DRSUAPI not via SAM. 4. Plant orphan cover. The attacker also writes one or two genuinely orphaned SIDs to the same account so that the malicious entry looks like routine migration cruft. An environment with thousands of orphan entries from a 2008 ADMT migration provides perfect camouflage for one new entry. 5. Use the privilege. The carrier account signs in. The KDC reads sIDHistory, builds the PAC with all SIDs present, and the user token contains S-1-5-21-<domain>-512 (Domain Admins). The account can now DCSync, modify the AdminSDHolder, dump LSASS on any DC, etc., despite holding no visible privileged group membership. 6. Survive eviction. The IR team identifies the attacker original foothold, rotates krbtgt twice, resets every Domain Admin password, and declares the incident closed. The carrier account, never identified as privileged, retains its SID-injected Domain Admin authority. The next time the attacker needs access they sign in as the carrier and repeat from step 5. 7. Cross-forest variant. In a multi-forest scenario the attacker compromises a trusted external forest (often the smaller "lab" or "dev" forest that shares a trust with prod), stamps the prod-forest Enterprise Admins SID onto a foreign account, and authenticates inbound. Unless the prod forest has SID filtering (Quarantine, trustAttributes 0x4) enabled on the trust, the injected SID survives the inbound PAC validation and the attacker holds Enterprise Admin in the prod forest. Tools observed in real engagements: Mimikatz misc::addsid, DSInternals Add-ADDBSidHistory, BloodHound CollectionMethod=ACL plus manual sIDHistory enumeration, Impacket secretsdump.py (read path) followed by addsid-equivalent write tooling, native Move-ADObject and Repadmin /writespn primitives where Self-Membership ACLs allow direct LDAP write to sIDHistory on objects the attacker controls.
Cómo lo evalúa Guerrilla
Guerrilla connects to the domain via LDAP (Get-ADObject -LDAPFilter "(sIDHistory=*)" -Properties sIDHistory, objectClass, sAMAccountName, distinguishedName, whenChanged) and pulls every security principal carrying a sIDHistory value across the entire defaultNamingContext. Each binary SID is decoded into its (Authority, Domain, RID) tuple. In parallel, the check enumerates Get-ADTrust -Filter * and reads the configuration NC Partitions container to build an authoritative list of (DomainSid, DomainName, TrustType, TrustDirection, TrustAttributes, IsForestTrust) tuples. For forest trusts the msDS-TrustForestTrustInfo blob is parsed to extract the TDO_FOREST_TRUST_DOMAIN_INFO records that whitelist additional SIDs. Each sIDHistory entry is then matched against this trust map: - Match on DomainSid AND RID resolves: LIVE. Logged for inventory only. - Match on DomainSid AND RID unresolvable: ORPHAN_RID. - No match on DomainSid AND DomainSid != local: ORPHAN_DOMAIN. - DomainSid == local domain SID: SAME_DOMAIN (highest scrutiny: never produced by legitimate ADMT). - RID in {500, 512, 518, 519, 544, 548, 549, 550, 551, 552, 9 (Enterprise Domain Controllers), 526, 527} regardless of domain match: WELL_KNOWN_PRIVILEGED, severity elevated. The check additionally cross-references whenChanged on the carrying object against the trust creation/deletion dates and the known migration project windows the operator configures (so a sIDHistory entry written ten years ago is treated differently than one written yesterday). The whenChanged correlation is the single most useful signal for separating dormant 2008-era migration debris from active malicious injection. Secondary detection: Guerrilla also reads the Directory Service Access audit policy (auditpol /get /subcategory:"Directory Service Changes") and the schema ACL on the sIDHistory attribute to confirm that Event ID 4662 with property GUID for sIDHistory writes will fire on the DC security log, and that Event 4765 (SID History added) and 4766 (SID History add failed) are being collected centrally. A check finding includes a note when audit coverage is missing.
Valor recomendado
Ninguna entrada de SID History que haga referencia a dominios inexistentes. SID History limpiado tras completar la migración
Remediación
Consulte todas las cuentas de usuario y de grupo con SID History usando Get-ADUser -Filter {SIDHistory -like '*'} -Properties SIDHistory. Coteje cada componente de dominio del SID History con las relaciones de confianza existentes para identificar entradas huérfanas. Elimine las entradas de SID History de las migraciones completadas usando Remove-ADUser con el parámetro -Remove o Netdom trust /CleanupSIDHistory. Supervise las nuevas adiciones de SID History usando el Event ID 4765
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
- CIS AD Benchmark
- 9.4.2
- MITRE ATT&CK
- T1134.005