ADTRUST-005: SID History Abuse Detection
- Plataforma
- Active Directory
- Categoría
- AD Trust Relationships
- Severidad
- Critical
- Pilar de Zero Trust
- Identity (peso 2)
- Fixtures de referencia
- 3
- Cobertura de ramas
- Observada: los fixtures prueban los veredictos que ejercitan
- Procedencia
- baseline
Qué comprueba
The check binds to each domain in the forest, performs an LDAP search for (sIDHistory=*) across the user, computer, and group object classes, and pulls sIDHistory, sAMAccountName, objectClass, whenCreated, and whenChanged. Each binary SID value in the multi-valued sIDHistory attribute is parsed into S-R-X-Y1..Yn-RID form. The trailing RID is tested against the well-known privileged RID set (498 Enterprise Read-Only Domain Controllers, 500 Administrator, 502 KRBTGT, 512 Domain Admins, 513 Domain Users when used as a token-injection target, 516 Domain Controllers, 518 Schema Admins, 519 Enterprise Admins, 520 Group Policy Creator Owners, 521 RODCs, 526 Key Admins, 527 Enterprise Key Admins) and against the BUILTIN SIDs (S-1-5-32-544 Administrators, -548 Account Operators, -549 Server Operators, -550 Print Operators, -551 Backup Operators). The domain identifier prefix is then resolved against the forest topology and any known trust partner domains via Get-ADTrust and the Partitions container, so the check can label whether the source SID belongs to the local domain, another domain in the same forest, an external trust, or a forest trust. Finally, for every inbound trust enumerated under CN=System,DC=<domain>, the check reads the trustAttributes bitmask: TRUST_ATTRIBUTE_QUARANTINED_DOMAIN (0x4) on external trusts and TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL (0x40) plus the SID-filtering state for forest trusts (netdom trust /quarantine and /enablesidhistory) are recorded so the finding can correlate a privileged historical SID with the trust path that allows it to be honored.
Por qué importa
SID injection is a forest-wide privilege escalation primitive whose blast radius is bounded only by the trust topology. Three properties make it dangerous. First, the attribute is honored at logon by every KDC in the forest: there is no separate authorization layer between sIDHistory and the constructed user token. Whatever SIDs are present become part of the PAC. Second, the attribute is writable by domain administrators of any domain through DSInternals Add-ADDBSidHistory, by the LSA RPC DsAddSidHistory call, and historically by Mimikatz misc::addsid against a writable DC, none of which require touching the target account in the target domain. Third, the default trust posture before Windows Server 2003 SP1 (and still the default on legacy external trusts created without /quarantine) is to honor every SID presented by the trusted side; SID filtering must be explicitly enabled. The combination means that a single compromised child domain or external trust partner that lacks SID filtering can stamp Enterprise Admins (S-1-5-21-<forest root>-519) into the sIDHistory of any account it controls and authenticate cross-trust as a forest-wide administrator. Historical incidents include Mandiant-reported intrusions where attackers planted SID history on dormant accounts as a persistence mechanism that survives password resets, group membership audits, and even account disable, because nothing in the standard hardening checklist enumerates sIDHistory.
Ruta de ataque
Step 1: Foothold in a trusted domain. The attacker compromises any domain in the forest other than the target, or any external/forest trust partner where SID filtering (Quarantine) is not enabled on the trust. Krbtgt hash, DCSync rights, or Domain Admin in that domain are the minimum. Step 2: Mint a SID. The attacker chooses the target SID, typically S-1-5-21-<forest root domain>-519 (Enterprise Admins) or S-1-5-32-544 (BUILTIN Administrators). For forest-internal abuse, the attacker reads the forest root domain SID from the Partitions container or via Get-ADDomain (Get-ADDomain -Server forestroot.local).objectSID. Step 3: Write sIDHistory on a controlled account. From a DC the attacker owns, they invoke DSInternals: Add-ADDBSidHistory -SamAccountName attacker -SidHistory "S-1-5-21-<forest-root>-519" -DatabasePath C:\Windows\NTDS\ntds.dit. Alternatively, on a live DC, mimikatz sid::patch followed by sid::add /sam:attacker /new:<sid> rewrites the attribute in-place. The historical Microsoft-supplied LSA API (DsAddSidHistory) also works when invoked from a Domain Admin context with the right Audit policy flags. Step 4: Replicate. The modified attribute propagates via normal AD replication. No event is generated that distinguishes a malicious sIDHistory write from a benign migration write. Step 5: Authenticate cross-trust. The attacker logs on as the controlled account against any DC in the target domain or any member server in the forest. The KDC reads sIDHistory, constructs a PAC that includes the injected Enterprise Admins SID, and the attacker has forest-wide rights. If SID filtering is disabled on the inbound trust at the target side (trustAttributes bit 0x4 not set on external trusts, or SIDFilteringForestAware not enabled on forest trusts), the foreign SID is honored verbatim. Step 6: Persistence. Even after the attacker loses the foothold in the source domain, the planted sIDHistory remains on the controlled account. Password resets, group cleanups, and tier separation do not remove it. Only an explicit Set-ADUser -Replace @{sIDHistory=$null} or the documented Active Directory Migration Tool cleanup removes the value.
Cómo lo evalúa Guerrilla
Guerrilla iterates every domain in the forest returned by Get-ADForest. For each domain it issues an LDAP query with filter (&(|(objectClass=user)(objectClass=computer)(objectClass=group))(sIDHistory=*)) requesting sIDHistory, sAMAccountName, distinguishedName, objectClass, userAccountControl, whenCreated, whenChanged. Each binary SID is converted via System.Security.Principal.SecurityIdentifier and decomposed into authority, domain identifier, and RID. The RID is compared against the privileged RID set listed above. The domain identifier is resolved by joining against (objectClass=trustedDomain) entries in CN=System and the Partitions container in the Configuration NC; this lets the check label the source as same-domain, intra-forest, external-trust, or forest-trust. The check also pulls each (objectClass=trustedDomain) and reads trustAttributes, trustDirection, trustType, and trustPartner so it can flag any inbound trust where TRUST_ATTRIBUTE_QUARANTINED_DOMAIN (0x4) is clear on an external trust or where forest-trust SID filtering has been disabled via netdom trust /enablesidhistory:Yes. Findings are correlated: a privileged historical SID is Critical when an unfiltered trust path exists, High when the SID is forest-internal, and Medium when the SID belongs to the same domain (likely a legacy migration artifact that still expands the user token and should be cleaned up).
Valor recomendado
No accounts with SID history values referencing privileged groups. SID history cleaned up after all migrations complete
Remediación
Search for accounts with SID history using Get-ADUser -Filter {SIDHistory -like '*'} -Properties SIDHistory. Identify any SID history entries that reference privileged groups (Domain Admins, Enterprise Admins, etc.). Clean up SID history after migration using Remove-ADUser with the SIDHistory parameter. Enable SID filtering on trusts
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-6, AC-6(1)
- ANSSI
- R32
- CIS AD Benchmark
- 3.2.2
- MITRE ATT&CK
- T1134.005